diff options
author | Yves Fischer <yvesf-git@xapek.org> | 2011-10-23 21:14:40 +0200 |
---|---|---|
committer | Yves Fischer <yvesf-git@xapek.org> | 2011-10-30 11:33:54 +0100 |
commit | 770ba5201f5c60b2bb36602ff9d359f641e33125 (patch) | |
tree | 2200757f04efbc281c4b3714bfe2e4fe911193b6 /schall/static/RGraph/libraries | |
parent | 77f5e686612898974e13f2a5df57c20e4e529363 (diff) | |
download | mini-octopus-770ba5201f5c60b2bb36602ff9d359f641e33125.tar.gz mini-octopus-770ba5201f5c60b2bb36602ff9d359f641e33125.zip |
Charting with flask, rgraph and custom "timeseries database"
Diffstat (limited to 'schall/static/RGraph/libraries')
31 files changed, 27212 insertions, 0 deletions
diff --git a/schall/static/RGraph/libraries/RGraph.bar.js b/schall/static/RGraph/libraries/RGraph.bar.js new file mode 100644 index 0000000..b5946c6 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.bar.js @@ -0,0 +1,1840 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bar chart constructor + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.Bar = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'bar'; + this.max = 0; + this.stackedOrGrouped = false; + this.isRGraph = true; + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config type stuff + this.properties = { + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': true, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':true, + 'chart.background.grid.autofit.numhlines': 5, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.background.image': null, + 'chart.ytickgap': 20, + 'chart.smallyticks': 3, + 'chart.largeyticks': 5, + 'chart.numyticks': 10, + 'chart.hmargin': 5, + 'chart.strokecolor': '#666', + 'chart.axis.color': 'black', + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.labels': null, + 'chart.labels.ingraph': null, + 'chart.labels.above': false, + 'chart.labels.above.decimals': 0, + 'chart.labels.above.size': null, + 'chart.labels.above.angle': null, + 'chart.ylabels': true, + 'chart.ylabels.count': 5, + 'chart.ylabels.inside': false, + 'chart.xlabels.offset': 0, + 'chart.xaxispos': 'bottom', + 'chart.yaxispos': 'left', + 'chart.text.color': 'black', + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.font': 'Verdana', + 'chart.ymin': 0, + 'chart.ymax': null, + 'chart.title': '', + 'chart.title.font': null, + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.colors': ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'], + 'chart.colors.sequential': false, + 'chart.colors.reverse': false, + 'chart.grouping': 'grouped', + 'chart.variant': 'bar', + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.background.hbars': null, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.halign': 'right', + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.text.size': 10, + 'chart.key.linewidth': 1, + 'chart.contextmenu': null, + 'chart.line': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.scale.decimals': 0, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.linewidth': 1, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.noaxes': false, + 'chart.noxaxis': false, + 'chart.noyaxis': false + } + + // Check for support + if (!this.canvas) { + alert('[BAR] No canvas support'); + return; + } + + /** + * Determine whether the chart will contain stacked or grouped bars + */ + for (i=0; i<data.length; ++i) { + if (typeof(data[i]) == 'object') { + this.stackedOrGrouped = true; + } + } + + // Store the data + this.data = data; + + // Used to store the coords of the bars + this.coords = []; + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Bar.prototype.Set = function (name, value) + { + name = name.toLowerCase(); + + if (name == 'chart.labels.abovebar') { + name = 'chart.labels.above'; + } + + if (name == 'chart.strokestyle') { + name = 'chart.strokecolor'; + } + + /** + * Check for xaxispos + */ + if (name == 'chart.xaxispos' ) { + if (value != 'bottom' && value != 'center' && value != 'top') { + alert('[BAR] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center'); + value = 'center'; + } + + if (value == 'top') { + for (var i=0; i<this.data.length; ++i) { + if (typeof(this.data[i]) == 'number' && this.data[i] > 0) { + alert('[BAR] The data element with index ' + i + ' should be negative'); + } + } + } + } + + this.properties[name] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Bar.prototype.Get = function (name) + { + if (name == 'chart.labels.abovebar') { + name = 'chart.labels.above'; + } + + var value = this.properties[name]; + + return value; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.Bar.prototype.Draw = function () + { + // MUST be the first thing done! + if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) { + RGraph.DrawBackgroundImage(this); + return; + } + + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Convert any null values to 0. Won't make any difference to the bar (as opposed to the line chart) + */ + for (var i=0; i<this.data.length; ++i) { + if (this.data[i] == null) { + this.data[i] = 0; + } + } + + + // Cache this in a class variable as it's used rather a lot + + /** + * Check for tooltips and alert the user that they're not supported with pyramid charts + */ + if ( (this.Get('chart.variant') == 'pyramid' || this.Get('chart.variant') == 'dot') + && typeof(this.Get('chart.tooltips')) == 'object' + && this.Get('chart.tooltips') + && this.Get('chart.tooltips').length > 0) { + + alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts'); + } + + /** + * Stop the coords array from growing uncontrollably + */ + this.coords = []; + + /** + * 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.max = 0; + this.grapharea = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + this.halfgrapharea = this.grapharea / 2; + this.halfTextHeight = this.Get('chart.text.size') / 2; + + // Progressively Draw the chart + RGraph.background.Draw(this); + + + //If it's a sketch chart variant, draw the axes first + if (this.Get('chart.variant') == 'sketch') { + this.DrawAxes(); + this.Drawbars(); + } else { + this.Drawbars(); + this.DrawAxes(); + } + + this.DrawLabels(); + + + // Draw the key if necessary + if (this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + + /** + * Is a line is defined, draw it + */ + var line = this.Get('chart.line'); + + if (line) { + + line.__bar__ = this; + + // Check the X axis positions + if (this.Get('chart.xaxispos') != line.Get('chart.xaxispos')) { + alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised"); + } + + line.Set('chart.gutter.left', this.Get('chart.gutter.left')); + line.Set('chart.gutter.right', this.Get('chart.gutter.right')); + line.Set('chart.gutter.top', this.Get('chart.gutter.top')); + line.Set('chart.gutter.bottom', this.Get('chart.gutter.bottom')); + + line.Set('chart.hmargin', (this.canvas.width - this.gutterLeft - this.gutterRight) / (line.original_data[0].length * 2)); + + // If a BAR custom yMax is set, use that + if (this.Get('chart.ymax') && !line.Get('chart.ymax')) { + line.Set('chart.ymax', this.Get('chart.ymax')); + + } else if (line.Get('chart.ymax')) { + line.Set('chart.ymax', line.Get('chart.ymax')); + } + + // The boolean is used to specify that the Line chart .Draw() method is being called by the Bar chart + line.Draw(true); + } + + + /** + * Draw "in graph" labels + */ + if (this.Get('chart.labels.ingraph')) { + RGraph.DrawInGraphLabels(this); + } + + /** + * Draw crosschairs + */ + if (this.Get('chart.crosshairs')) { + RGraph.DrawCrosshairs(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * This function enables adjusting + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the charts axes + */ + RGraph.Bar.prototype.DrawAxes = function () + { + if (this.Get('chart.noaxes')) { + return; + } + + var xaxispos = this.Get('chart.xaxispos'); + var yaxispos = this.Get('chart.yaxispos'); + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.axis.color'); + this.context.lineWidth = 1; + + // Draw the Y axis + if (this.Get('chart.noyaxis') == false) { + if (yaxispos == 'right') { + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + } else { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + } + } + + // Draw the X axis + if (this.Get('chart.noxaxis') == false) { + if (xaxispos == 'center') { + this.context.moveTo(this.gutterLeft, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop); + this.context.lineTo(this.canvas.width - this.gutterRight, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop); + } else if (xaxispos == 'top') { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(this.canvas.width - this.gutterRight, this.gutterTop); + } else { + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + } + } + + var numYTicks = this.Get('chart.numyticks'); + + // Draw the Y tickmarks + if (this.Get('chart.noyaxis') == false) { + var yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / numYTicks; + var xpos = yaxispos == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight; + + for (y=this.gutterTop; + xaxispos == 'center' ? y <= (RGraph.GetHeight(this) - this.gutterBottom) : y < (RGraph.GetHeight(this) - this.gutterBottom + (xaxispos == 'top' ? 1 : 0)); + y += yTickGap) { + + if (xaxispos == 'center' && y == (RGraph.GetHeight(this) / 2)) continue; + + // X axis at the top + if (xaxispos == 'top' && y == this.gutterTop) continue; + + this.context.moveTo(xpos, y); + this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y); + } + + /** + * If the X axis is not being shown, draw an extra tick + */ + if (this.Get('chart.noxaxis')) { + if (xaxispos == 'center') { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), this.canvas.height / 2); + this.context.lineTo(xpos, RGraph.GetHeight(this) / 2); + } else if (xaxispos == 'top') { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), this.gutterTop); + this.context.lineTo(xpos, this.gutterTop); + } else { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(xpos, RGraph.GetHeight(this) - this.gutterBottom); + } + } + } + + + // Draw the X tickmarks + if (this.Get('chart.noxaxis') == false) { + + xTickGap = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.data.length; + + if (xaxispos == 'bottom') { + yStart = this.canvas.height - this.gutterBottom; + yEnd = (this.canvas.height - this.gutterBottom) + 3; + } else if (xaxispos == 'top') { + yStart = this.gutterTop - 3; + yEnd = this.gutterTop; + } else if (xaxispos == 'center') { + yStart = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + 3; + yEnd = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - 3; + } + + //////////////// X TICKS //////////////// + var noEndXTick = this.Get('chart.noendxtick'); + + for (x=this.gutterLeft + (yaxispos == 'left' ? xTickGap : 0); x<this.canvas.width - this.gutterRight + (yaxispos == 'left' ? 5 : 0); x+=xTickGap) { + + if (yaxispos == 'left' && !noEndXTick && x > this.gutterLeft) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + + } else if (yaxispos == 'left' && noEndXTick && x > this.gutterLeft && x < (this.canvas.width - this.gutterRight) ) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + + } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && !noEndXTick) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + + } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && x > (this.gutterLeft) && noEndXTick) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + } + } + + if (this.Get('chart.noyaxis')) { + this.context.moveTo(this.gutterLeft, yStart); + this.context.lineTo(this.gutterLeft, yEnd); + } + + //////////////// X TICKS //////////////// + } + + /** + * If the Y axis is not being shown, draw an extra tick + */ + if (this.Get('chart.noyaxis') && this.Get('chart.noxaxis') == false) { + if (xaxispos == 'center') { + this.context.moveTo(this.gutterLeft, (RGraph.GetHeight(this) / 2) - 3); + this.context.lineTo(this.gutterLeft, (RGraph.GetHeight(this) / 2) + 3); + } else { + this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom + 3); + } + } + + this.context.stroke(); + } + + + /** + * Draws the bars + */ + RGraph.Bar.prototype.Drawbars = function () + { + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.strokeStyle = this.Get('chart.strokecolor'); + this.context.fillStyle = this.Get('chart.colors')[0]; + var prevX = 0; + var prevY = 0; + var decimals = this.Get('chart.scale.decimals'); + + /** + * Work out the max value + */ + if (this.Get('chart.ymax')) { + + this.max = this.Get('chart.ymax'); + this.min = this.Get('chart.ymin'); + + this.scale = [ + (((this.max - this.min) * (1/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (2/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (3/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (4/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (5/5) + this.min)).toFixed(decimals) + ]; + } else { + + this.min = this.Get('chart.ymin'); + + for (i=0; i<this.data.length; ++i) { + if (typeof(this.data[i]) == 'object') { + var value = this.Get('chart.grouping') == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ; + + } else { + var value = Number(this.data[i]); + } + + this.max = Math.max(Math.abs(this.max), Math.abs(value)); + } + + this.scale = RGraph.getScale(this.max, this); + this.max = this.scale[4]; + + if (this.Get('chart.ymin')) { + + var decimals = this.Get('chart.scale.decimals'); + + this.scale[0] = ((Number(this.scale[4] - this.min) * 0.2) + this.min).toFixed(decimals); + this.scale[1] = ((Number(this.scale[4] - this.min) * 0.4) + this.min).toFixed(decimals); + this.scale[2] = ((Number(this.scale[4] - this.min) * 0.6) + this.min).toFixed(decimals); + this.scale[3] = ((Number(this.scale[4] - this.min) * 0.8) + this.min).toFixed(decimals); + this.scale[4] = ((Number(this.scale[4] - this.min) * 1.0) + this.min).toFixed(decimals); + + } else { + if (this.Get('chart.scale.decimals')) { + + var decimals = this.Get('chart.scale.decimals'); + + this.scale[0] = Number(this.scale[0]).toFixed(decimals); + this.scale[1] = Number(this.scale[1]).toFixed(decimals); + this.scale[2] = Number(this.scale[2]).toFixed(decimals); + this.scale[3] = Number(this.scale[3]).toFixed(decimals); + this.scale[4] = Number(this.scale[4]).toFixed(decimals); + } + } + } + + /** + * Draw horizontal bars here + */ + if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) { + RGraph.DrawBars(this); + } + + var variant = this.Get('chart.variant'); + + /** + * Draw the 3D axes is necessary + */ + if (variant == '3d') { + RGraph.Draw3DAxes(this); + } + + /** + * Get the variant once, and draw the bars, be they regular, stacked or grouped + */ + + // Get these variables outside of the loop + var xaxispos = this.Get('chart.xaxispos'); + var width = (this.canvas.width - this.gutterLeft - this.gutterRight ) / this.data.length; + var orig_height = height; + var hmargin = this.Get('chart.hmargin'); + var shadow = this.Get('chart.shadow'); + var shadowColor = this.Get('chart.shadow.color'); + var shadowBlur = this.Get('chart.shadow.blur'); + var shadowOffsetX = this.Get('chart.shadow.offsetx'); + var shadowOffsetY = this.Get('chart.shadow.offsety'); + var strokeStyle = this.Get('chart.strokecolor'); + var colors = this.Get('chart.colors'); + + for (i=0; i<this.data.length; ++i) { + + // Work out the height + //The width is up outside the loop + var height = ((RGraph.array_sum(this.data[i]) < 0 ? RGraph.array_sum(this.data[i]) + this.min : RGraph.array_sum(this.data[i]) - this.min) / (this.max - this.min) ) * (this.canvas.height - this.gutterTop - this.gutterBottom); + + // Half the height if the Y axis is at the center + if (xaxispos == 'center') { + height /= 2; + } + + var x = (i * width) + this.gutterLeft; + var y = xaxispos == 'center' ? ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - height + : this.canvas.height - height - this.gutterBottom; + + // xaxispos is top + if (xaxispos == 'top') { + y = this.gutterTop + Math.abs(height); + } + + + // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value + if (height < 0) { + y += height; + height = Math.abs(height); + } + + /** + * Turn on the shadow if need be + */ + if (shadow) { + this.context.shadowColor = shadowColor; + this.context.shadowBlur = shadowBlur; + this.context.shadowOffsetX = shadowOffsetX; + this.context.shadowOffsetY = shadowOffsetY; + } + + /** + * Draw the bar + */ + this.context.beginPath(); + if (typeof(this.data[i]) == 'number') { + + var barWidth = width - (2 * hmargin); + + // Set the fill color + this.context.strokeStyle = strokeStyle; + this.context.fillStyle = colors[0]; + + /** + * Sequential colors + */ + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = colors[i]; + } + + if (variant == 'sketch') { + + this.context.lineCap = 'round'; + + var sketchOffset = 3; + + this.context.beginPath(); + + this.context.strokeStyle = colors[0]; + + /** + * Sequential colors + */ + if (this.Get('chart.colors.sequential')) { + this.context.strokeStyle = colors[i]; + } + + // Left side + this.context.moveTo(x + hmargin + 2, y + height - 2); + this.context.lineTo(x + hmargin , y - 2); + + // The top + this.context.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0)); + this.context.bezierCurveTo(x + ((hmargin + width) * 0.33),y + 5 + (this.data[i] < 0 ? height - 10: 0),x + ((hmargin + width) * 0.66),y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0)); + + + // The right side + this.context.moveTo(x + hmargin + width - 2, y + -2); + this.context.lineTo(x + hmargin + width - 3, y + height - 3); + + for (var r=0.2; r<=0.8; r+=0.2) { + this.context.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1); + this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2)); + } + + this.context.stroke(); + + // Regular bar + } else if (variant == 'bar' || variant == '3d' || variant == 'glass' || variant == 'bevel') { + + if (RGraph.isIE8() && shadow) { + this.DrawIEShadow([x + hmargin, y, barWidth, height]); + } + + if (variant == 'glass') { + RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); + RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); + } else { + this.context.strokeRect(x + hmargin, y, barWidth, height); + this.context.fillRect(x + hmargin, y, barWidth, height); + } + + + // This bit draws the text labels that appear above the bars if requested + if (this.Get('chart.labels.above')) { + + // Turn off any shadow + if (shadow) { + RGraph.NoShadow(this); + } + + var yPos = y - 3; + + // Account for negative bars + if (this.data[i] < 0) { + yPos += height + 6 + (this.Get('chart.text.size') - 4); + } + + // Account for chart.xaxispos=top + if (this.Get('chart.xaxispos') == 'top') { + yPos = this.gutterTop + height + 6 + (typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 4); + } + + this.context.fillStyle = this.Get('chart.text.color'); + + RGraph.Text(this.context, + this.Get('chart.text.font'), + typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3, + x + hmargin + (barWidth / 2), + yPos, + RGraph.number_format(this, Number(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')),this.Get('chart.units.pre'),this.Get('chart.units.post')), + this.Get('chart.labels.above.angle') ? 'bottom' : null, + this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 && (this.Get('chart.xaxispos') != 'top') ? 'right' : 'left') : 'center', + null, + this.Get('chart.labels.above.angle') + ); + } + + // 3D effect + if (variant == '3d') { + + var prevStrokeStyle = this.context.strokeStyle; + var prevFillStyle = this.context.fillStyle; + + // Draw the top + this.context.beginPath(); + this.context.moveTo(x + hmargin, y); + this.context.lineTo(x + hmargin + 10, y - 5); + this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the right hand side + this.context.beginPath(); + this.context.moveTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5); + this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5); + this.context.lineTo(x + hmargin + barWidth, y + height); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the darker top section + this.context.beginPath(); + this.context.fillStyle = 'rgba(255,255,255,0.3)'; + this.context.moveTo(x + hmargin, y); + this.context.lineTo(x + hmargin + 10, y - 5); + this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the darker right side section + this.context.beginPath(); + this.context.fillStyle = 'rgba(0,0,0,0.4)'; + this.context.moveTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height); + this.context.lineTo(x + hmargin + barWidth, y + height); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + this.context.strokeStyle = prevStrokeStyle; + this.context.fillStyle = prevFillStyle; + + // Glass variant + } else if (variant == 'glass') { + + var grad = this.context.createLinearGradient( + x + hmargin, + y, + x + hmargin + (barWidth / 2), + y + ); + grad.addColorStop(0, 'rgba(255,255,255,0.9)'); + grad.addColorStop(1, 'rgba(255,255,255,0.5)'); + + this.context.beginPath(); + this.context.fillStyle = grad; + this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2); + this.context.fill(); + } + + // Dot chart + } else if (variant == 'dot') { + + this.context.beginPath(); + this.context.moveTo(x + (width / 2), y); + this.context.lineTo(x + (width / 2), y + height); + this.context.stroke(); + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.colors')[i]; + this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0); + + // Set the colour for the dots + this.context.fillStyle = this.Get('chart.colors')[0]; + + /** + * Sequential colors + */ + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = colors[i]; + } + + this.context.stroke(); + this.context.fill(); + + // Pyramid chart + } else if (variant == 'pyramid') { + + this.context.beginPath(); + var startY = (this.Get('chart.xaxispos') == 'center' ? (RGraph.GetHeight(this) / 2) : (RGraph.GetHeight(this) - this.gutterBottom)); + + this.context.moveTo(x + hmargin, startY); + this.context.lineTo( + x + hmargin + (barWidth / 2), + y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0) + ); + this.context.lineTo(x + hmargin + barWidth, startY); + + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Arrow chart + } else if (variant == 'arrow') { + + var startY = (this.Get('chart.xaxispos') == 'center' ? (RGraph.GetHeight(this) / 2) : (RGraph.GetHeight(this) - this.gutterBottom)); + + this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1; + this.context.lineCap = 'round'; + + this.context.beginPath(); + + this.context.moveTo(x + hmargin + (barWidth / 2), startY); + this.context.lineTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)); + this.context.arc(x + hmargin + (barWidth / 2), + y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0), + 5, + this.data[i] > 0 ? 0.78 : 5.6, + this.data[i] > 0 ? 0.79 : 5.48, + this.data[i] < 0); + + this.context.moveTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)); + this.context.arc(x + hmargin + (barWidth / 2), + y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0), + 5, + this.data[i] > 0 ? 2.355 : 4, + this.data[i] > 0 ? 2.4 : 3.925, + this.data[i] < 0); + + this.context.stroke(); + + this.context.lineWidth = 1; + + // Unknown variant type + } else { + alert('[BAR] Warning! Unknown chart.variant: ' + variant); + } + + this.coords.push([x + hmargin, y, width - (2 * hmargin), height]); + + + /** + * Stacked bar + */ + } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') { + + if (this.min) { + alert("[ERROR] Stacked Bar charts with a Y min are not supported"); + } + + var barWidth = width - (2 * hmargin); + var redrawCoords = [];// Necessary to draw if the shadow is enabled + var startY = 0; + var dataset = this.data[i]; + + for (j=0; j<dataset.length; ++j) { + + // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted + if (xaxispos == 'center') { + alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart."); + return; + } + + // Negative values not permitted for the stacked chart + if (this.data[i][j] < 0) { + alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.'); + return; + } + + /** + * Set the fill and stroke colors + */ + this.context.strokeStyle = strokeStyle + this.context.fillStyle = colors[j]; + + if (this.Get('chart.colors.reverse')) { + this.context.fillStyle = colors[this.data[i].length - j - 1]; + } + + var height = (dataset[j] / this.max) * (this.canvas.height - this.gutterTop - this.gutterBottom ); + + // If the X axis pos is in the center, we need to half the height + if (xaxispos == 'center') { + height /= 2; + } + + var totalHeight = (RGraph.array_sum(dataset) / this.max) * (this.canvas.height - hmargin - this.gutterTop - this.gutterBottom); + + /** + * Store the coords for tooltips + */ + this.coords.push([x + hmargin, y, width - (2 * hmargin), height]); + + // MSIE shadow + if (RGraph.isIE8() && shadow) { + this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]); + } + + this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height); + this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height); + + + if (j == 0) { + var startY = y; + var startX = x; + } + + /** + * Store the redraw coords if the shadow is enabled + */ + if (shadow) { + redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, colors[j]]); + } + + /** + * Stacked 3D effect + */ + if (variant == '3d') { + + var prevFillStyle = this.context.fillStyle; + var prevStrokeStyle = this.context.strokeStyle; + + + // Draw the top side + if (j == 0) { + this.context.beginPath(); + this.context.moveTo(startX + hmargin, y); + this.context.lineTo(startX + 10 + hmargin, y - 5); + this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5); + this.context.lineTo(startX + barWidth + hmargin, y); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + } + + // Draw the side section + this.context.beginPath(); + this.context.moveTo(startX + barWidth + hmargin, y); + this.context.lineTo(startX + barWidth + hmargin + 10, y - 5); + this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height); + this.context.lineTo(startX + barWidth + hmargin , y + height); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + // Draw the darker top side + if (j == 0) { + this.context.fillStyle = 'rgba(255,255,255,0.3)'; + this.context.beginPath(); + this.context.moveTo(startX + hmargin, y); + this.context.lineTo(startX + 10 + hmargin, y - 5); + this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5); + this.context.lineTo(startX + barWidth + hmargin, y); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + } + + // Draw the darker side section + this.context.fillStyle = 'rgba(0,0,0,0.4)'; + this.context.beginPath(); + this.context.moveTo(startX + barWidth + hmargin, y); + this.context.lineTo(startX + barWidth + hmargin + 10, y - 5); + this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height); + this.context.lineTo(startX + barWidth + hmargin , y + height); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + this.context.strokeStyle = prevStrokeStyle; + this.context.fillStyle = prevFillStyle; + } + + y += height; + } + + // This bit draws the text labels that appear above the bars if requested + if (this.Get('chart.labels.above')) { + + // Turn off any shadow + RGraph.NoShadow(this); + + this.context.fillStyle = this.Get('chart.text.color'); + RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (barWidth / 2) + this.Get('chart.hmargin'),startY - (this.Get('chart.shadow') && this.Get('chart.shadow.offsety') < 0 ? 7 : 4),String(this.Get('chart.units.pre') + RGraph.array_sum(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')) + this.Get('chart.units.post')),this.Get('chart.labels.above.angle') ? 'bottom' : null,this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 ? 'right' : 'left') : 'center',null,this.Get('chart.labels.above.angle')); + + // Turn any shadow back on + if (shadow) { + this.context.shadowColor = shadowColor; + this.context.shadowBlur = shadowBlur; + this.context.shadowOffsetX = shadowOffsetX; + this.context.shadowOffsetY = shadowOffsetY; + } + } + + + /** + * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the + * shadow spilling over to higher up bars + */ + if (shadow) { + + RGraph.NoShadow(this); + + for (k=0; k<redrawCoords.length; ++k) { + this.context.strokeStyle = strokeStyle; + this.context.fillStyle = redrawCoords[k][4]; + this.context.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]); + this.context.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]); + + this.context.stroke(); + this.context.fill(); + } + + // Reset the redraw coords to be empty + redrawCoords = []; + } + /** + * Grouped bar + */ + } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') { + + var redrawCoords = []; + this.context.lineWidth = this.Get('chart.linewidth'); + + for (j=0; j<this.data[i].length; ++j) { + // Set the fill and stroke colors + this.context.strokeStyle = strokeStyle; + this.context.fillStyle = colors[j]; + + var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length; + var height = ((this.data[i][j] + (this.data[i][j] < 0 ? this.min : (-1 * this.min) )) / (this.max - this.min) ) * (this.canvas.height - this.gutterTop - this.gutterBottom ); + + // If the X axis pos is in the center, we need to half the height + if (xaxispos == 'center') { + height /= 2; + } + + var startX = x + hmargin + (j * individualBarWidth); + + /** + * Determine the start positioning for the bar + */ + if (xaxispos == 'top') { + var startY = this.Get('chart.gutter.top'); + var height = Math.abs(height); + + } else if (xaxispos == 'center') { + var startY = this.gutterTop + (this.grapharea / 2) - height; + + } else { + var startY = this.canvas.height - this.gutterBottom - height; + var height = Math.abs(height); + } + + /** + * Draw MSIE shadow + */ + if (RGraph.isIE8() && shadow) { + this.DrawIEShadow([startX, startY, individualBarWidth, height]); + } + + this.context.strokeRect(startX, startY, individualBarWidth, height); + this.context.fillRect(startX, startY, individualBarWidth, height); + y += height; + + // This bit draws the text labels that appear above the bars if requested + if (this.Get('chart.labels.above')) { + + this.context.strokeStyle = 'rgba(0,0,0,0)'; + + // Turn off any shadow + if (shadow) { + RGraph.NoShadow(this); + } + + var yPos = y - 3; + + // Account for negative bars + if (this.data[i][j] < 0) { + yPos += height + 6 + (this.Get('chart.text.size') - 4); + } + + this.context.fillStyle = this.Get('chart.text.color'); + RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (individualBarWidth / 2),startY - 2,RGraph.number_format(this, this.data[i][j].toFixed(this.Get('chart.labels.above.decimals'))),null,this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 ? 'right' : 'left') : 'center', null, this.Get('chart.labels.above.angle')); + + // Turn any shadow back on + if (shadow) { + this.context.shadowColor = shadowColor; + this.context.shadowBlur = shadowBlur; + this.context.shadowOffsetX = shadowOffsetX; + this.context.shadowOffsetY = shadowOffsetY; + } + } + + /** + * Grouped 3D effect + */ + if (variant == '3d') { + var prevFillStyle = this.context.fillStyle; + var prevStrokeStyle = this.context.strokeStyle; + + // Draw the top side + this.context.beginPath(); + this.context.moveTo(startX, startY); + this.context.lineTo(startX + 10, startY - 5); + this.context.lineTo(startX + 10 + individualBarWidth, startY - 5); + this.context.lineTo(startX + individualBarWidth, startY); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + // Draw the side section + this.context.beginPath(); + this.context.moveTo(startX + individualBarWidth, startY); + this.context.lineTo(startX + individualBarWidth + 10, startY - 5); + this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height); + this.context.lineTo(startX + individualBarWidth , startY + height); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + + // Draw the darker top side + this.context.fillStyle = 'rgba(255,255,255,0.3)'; + this.context.beginPath(); + this.context.moveTo(startX, startY); + this.context.lineTo(startX + 10, startY - 5); + this.context.lineTo(startX + 10 + individualBarWidth, startY - 5); + this.context.lineTo(startX + individualBarWidth, startY); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + // Draw the darker side section + this.context.fillStyle = 'rgba(0,0,0,0.4)'; + this.context.beginPath(); + this.context.moveTo(startX + individualBarWidth, startY); + this.context.lineTo(startX + individualBarWidth + 10, startY - 5); + this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height); + this.context.lineTo(startX + individualBarWidth , startY + height); + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + this.context.strokeStyle = prevStrokeStyle; + this.context.fillStyle = prevFillStyle; + } + + this.coords.push([startX, startY, individualBarWidth, height]); + + // Facilitate shadows going to the left + if (this.Get('chart.shadow')) { + redrawCoords.push([startX, startY, individualBarWidth, height]); + } + } + + /** + * Redraw the bar if shadows are going to the left + */ + if (redrawCoords.length) { + + RGraph.NoShadow(this); + + this.context.lineWidth = this.Get('chart.linewidth'); + + this.context.beginPath(); + for (var j=0; j<redrawCoords.length; ++j) { + + this.context.fillStyle = this.Get('chart.colors')[j]; + this.context.strokeStyle = this.Get('chart.strokecolor'); + + this.context.fillRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]); + this.context.strokeRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]); + } + this.context.fill(); + this.context.stroke(); + + redrawCoords = []; + } + } + + this.context.closePath(); + } + + /** + * Turn off any shadow + */ + RGraph.NoShadow(this); + + + /** + * Install the onclick event handler + */ + if (this.Get('chart.tooltips')) { + + // Need to register this object for redrawing + RGraph.Register(this); + + RGraph.InstallBarTooltipEventListeners(this); + } + } + + /** + * Draws the labels for the graph + */ + RGraph.Bar.prototype.DrawLabels = function () + { + var context = this.context; + var text_angle = this.Get('chart.text.angle'); + var text_size = this.Get('chart.text.size'); + var labels = this.Get('chart.labels'); + + + // Draw the Y axis labels: + if (this.Get('chart.ylabels')) { + this.Drawlabels_top(); + this.Drawlabels_center(); + this.Drawlabels_bottom(); + } + + /** + * The X axis labels + */ + if (typeof(labels) == 'object' && labels) { + + var yOffset = 13 + Number(this.Get('chart.xlabels.offset')); + + /** + * Text angle + */ + var angle = 0; + var halign = 'center'; + + if (text_angle > 0) { + angle = -1 * text_angle; + halign = 'right'; + yOffset -= 5; + + if (this.Get('chart.xaxispos') == 'top') { + halign = 'left'; + yOffset += 5; + } + } + + // Draw the X axis labels + context.fillStyle = this.Get('chart.text.color'); + + // How wide is each bar + var barWidth = (RGraph.GetWidth(this) - this.gutterRight - this.gutterLeft) / labels.length; + + // Reset the xTickGap + xTickGap = (RGraph.GetWidth(this) - this.gutterRight - this.gutterLeft) / labels.length + + // Draw the X tickmarks + var i=0; + var font = this.Get('chart.text.font'); + + for (x=this.gutterLeft + (xTickGap / 2); x<=RGraph.GetWidth(this) - this.gutterRight; x+=xTickGap) { + RGraph.Text(context, font, + text_size, + x + (this.Get('chart.text.angle') == 90 ? 0 : 0), + this.Get('chart.xaxispos') == 'top' ? this.gutterTop - yOffset + text_size - 1: (RGraph.GetHeight(this) - this.gutterBottom) + yOffset, + String(labels[i++]), + (this.Get('chart.text.angle') == 90 ? 'center' : null), + halign, + null, + angle); + } + } + } + + /** + * Draws the X axis at the top + */ + RGraph.Bar.prototype.Drawlabels_top = function () + { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + this.context.strokeStyle = 'black'; + + if (this.Get('chart.xaxispos') == 'top') { + + var context = this.context; + var interval = (this.grapharea * (1/5) ); + var text_size = this.Get('chart.text.size'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; + var font = this.Get('chart.text.font'); + var numYLabels = this.Get('chart.ylabels.count'); + + if (this.Get('chart.ylabels.inside') == true) { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5; + var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right'; + var boxed = true; + } else { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : this.canvas.width - this.gutterRight + 5; + var boxed = false; + } + + /** + * Draw specific Y labels here so that the local variables can be reused + */ + if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) { + + var labels = RGraph.array_reverse(this.Get('chart.ylabels.specific')); + var grapharea = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + + for (var i=0; i<labels.length; ++i) { + + var y = this.gutterTop + (grapharea * (i / labels.length)) + (grapharea / labels.length); + + RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed); + } + + return; + } + + // 1(ish) label + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + + // 5 labels + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + } + + // 3 labels + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + } + } + + // 10 Y labels + if (numYLabels == 10) { + + interval = (this.grapharea / numYLabels ); + + for (var i=10; i>0; --i) { + RGraph.Text(context, font, text_size, xpos,this.gutterTop + ((this.grapharea / numYLabels) * i),'-' + RGraph.number_format(this,((this.scale[4] / numYLabels) * i).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed); + } + } + + /** + * Show the minimum value if its not zero + */ + if (this.Get('chart.ymin') != 0) { + RGraph.Text(context, + font, + text_size, + xpos, + this.gutterTop, + '-' + RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post), + 'center', + align, + boxed); + } + + } + + this.context.fill(); + this.context.stroke(); + } + + /** + * Draws the X axis in the middle + */ + RGraph.Bar.prototype.Drawlabels_center = function () + { + var font = this.Get('chart.text.font'); + var numYLabels = this.Get('chart.ylabels.count'); + + this.context.fillStyle = this.Get('chart.text.color'); + + if (this.Get('chart.xaxispos') == 'center') { + + /** + * Draw the top labels + */ + var interval = (this.grapharea * (1/10) ); + var text_size = this.Get('chart.text.size'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var context = this.context; + var align = ''; + var xpos = 0; + var boxed = false; + + this.context.fillStyle = this.Get('chart.text.color'); + this.context.strokeStyle = 'black'; + + if (this.Get('chart.ylabels.inside') == true) { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5; + var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right'; + var boxed = true; + } else { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5; + var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; + var boxed = false; + } + + + + + + + + + + + + + /** + * Draw specific Y labels here so that the local variables can be reused + */ + if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) { + + var labels = this.Get('chart.ylabels.specific'); + var grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + + // Draw the top halves labels + for (var i=0; i<labels.length; ++i) { + + var y = this.gutterTop + ((grapharea / 2) / labels.length) * i; + + RGraph.Text(context, font, text_size, xpos, y, String(labels[i]), 'center', align, boxed); + } + + // Draw the bottom halves labels + for (var i=labels.length-1; i>=0; --i) { + var y = this.gutterTop + (grapharea * ( (i+1) / (labels.length * 2) )) + (grapharea / 2); + + RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed); + } + + return; + } + + + + + + + + + + + + + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + } + + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + } + } else if (numYLabels == 10) { + // 10Y labels + interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, font, text_size, xpos,this.gutterTop + ((this.grapharea / (numYLabels * 2)) * i),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed); + } + } + /////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw the bottom (X axis) labels + */ + var interval = (this.grapharea) / 10; + + if (numYLabels == 3 || numYLabels == 5) { + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + } + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + } + + RGraph.Text(context, font, text_size, xpos, this.grapharea + this.gutterTop + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + + } else if (numYLabels == 10) { + + // Arbitrary number of Y labels + interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, font, text_size, xpos,this.gutterTop + (this.grapharea / 2) + ((this.grapharea / (numYLabels * 2)) * i) + (this.grapharea / (numYLabels * 2)),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (i+1)).toFixed((this.Get('chart.scale.decimals'))), '-' + units_pre, units_post),'center', align, boxed); + } + } + + + + /** + * Show the minimum value if its not zero + */ + if (this.Get('chart.ymin') != 0) { + RGraph.Text(context, + font, + text_size, + xpos, + this.gutterTop + (this.grapharea / 2), + RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post), + 'center', + align, + boxed); + } + } + } + + /** + * Draws the X axdis at the bottom (the default) + */ + RGraph.Bar.prototype.Drawlabels_bottom = function () + { + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + this.context.strokeStyle = 'black'; + + if (this.Get('chart.xaxispos') != 'center' && this.Get('chart.xaxispos') != 'top') { + + var interval = (this.grapharea * (1/5) ); + var text_size = this.Get('chart.text.size'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var context = this.context; + var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; + var font = this.Get('chart.text.font'); + var numYLabels = this.Get('chart.ylabels.count'); + + if (this.Get('chart.ylabels.inside') == true) { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5; + var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right'; + var boxed = true; + } else { + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5; + var boxed = false; + } + + /** + * Draw specific Y labels here so that the local variables can be reused + */ + if (this.Get('chart.ylabels.specific') && typeof(this.Get('chart.ylabels.specific')) == 'object') { + + var labels = this.Get('chart.ylabels.specific'); + var grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + + for (var i=0; i<labels.length; ++i) { + var y = this.gutterTop + (grapharea * (i / labels.length)); + + RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed); + } + + return; + } + + // 1 label + if (numYLabels == 3 || numYLabels == 5) { + + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + + // 5 labels + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + } + + // 3 labels + if (numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + } + } + + // 10 Y labels + if (numYLabels == 10) { + + interval = (this.grapharea / numYLabels ); + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ((this.grapharea / numYLabels) * i), RGraph.number_format(this,((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed); + } + } + + /** + * Show the minimum value if its not zero + */ + if (this.Get('chart.ymin') != 0) { + RGraph.Text(context, + font, + text_size, + xpos, + this.canvas.height - this.gutterBottom, + RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post), + 'center', + align, + boxed); + } + } + + this.context.fill(); + this.context.stroke(); + } + + + /** + * This function is used by MSIE only to manually draw the shadow + * + * @param array coords The coords for the bar + */ + RGraph.Bar.prototype.DrawIEShadow = function (coords) + { + var prevFillStyle = this.context.fillStyle; + var offsetx = this.Get('chart.shadow.offsetx'); + var offsety = this.Get('chart.shadow.offsety'); + + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.fillStyle = this.Get('chart.shadow.color'); + this.context.beginPath(); + + // Draw shadow here + this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]); + + this.context.fill(); + + // Change the fillstyle back to what it was + this.context.fillStyle = prevFillStyle; + } + + + /** + * Not used by the class during creating the graph, but is used by event handlers + * to get the coordinates (if any) of the selected bar + * + * @param object e The event object + * @param object OPTIONAL You can pass in the bar object instead of the + * function getting it from the canvas + */ + RGraph.Bar.prototype.getBar = function (e) + { + var canvas = e.target; + var obj = e.target.__object__; + + if (obj.__bar__) { + obj = obj.__bar__; + } + + var mouseCoords = RGraph.getMouseXY(e); + + // This facilitates you being able to pass in the bar object as a parameter instead of + // the function getting it from the object + if (arguments[1]) { + obj = arguments[1]; + } + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i<obj.coords.length; i++) { + + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + + if ( mouseX >= left + && mouseX <= left + width + && mouseY >= top + && mouseY <= top + height) { + + return [obj, left, top, width, height, i]; + } + } + + return null; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.bipolar.js b/schall/static/RGraph/libraries/RGraph.bipolar.js new file mode 100644 index 0000000..1d6883b --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.bipolar.js @@ -0,0 +1,787 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bi-polar/age frequency constructor. + * + * @param string id The id of the canvas + * @param array left The left set of data points + * @param array right The right set of data points + */ + RGraph.Bipolar = function (id, left, right) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.canvas.__object__ = this; + this.type = 'bipolar'; + this.coords = []; + this.max = 0; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // The left and right data respectively + this.left = left; + this.right = right; + this.data = [left, right]; + + this.properties = { + 'chart.margin': 2, + 'chart.xtickinterval': null, + 'chart.labels': [], + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.title.left': '', + 'chart.title.right': '', + 'chart.gutter.center': 60, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.colors': ['#0f0'], + 'chart.contextmenu': null, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.xmax': null, + 'chart.scale.decimals': null, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.axis.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.strokestyle': '#333' + } + + // Pad the arrays so they're the same size + while (this.left.length < this.right.length) this.left.push(0); + while (this.left.length > this.right.length) this.right.push(0); + } + + + /** + * The setter + * + * @param name string The name of the parameter to set + * @param value mixed The value of the paraneter + */ + RGraph.Bipolar.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * The getter + * + * @param name string The name of the parameter to get + */ + RGraph.Bipolar.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * Draws the graph + */ + RGraph.Bipolar.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + + + // Reset the data to what was initially supplied + this.left = this.data[0]; + this.right = this.data[1]; + + + /** + * Reset the coords array + */ + this.coords = []; + + this.GetMax(); + this.DrawAxes(); + this.DrawTicks(); + this.DrawLeftBars(); + this.DrawRightBars(); + + if (this.Get('chart.axis.color') != 'black') { + this.DrawAxes(); // Draw the axes again (if the axes color is not black) + } + + this.DrawLabels(); + this.DrawTitles(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + + /** + * Install the on* event handlers + */ + if (this.Get('chart.tooltips')) { + + + // Register the object so that it gets redrawn + RGraph.Register(this); + + + /** + * Install the window onclick handler + */ + + /** + * Install the window event handler + */ + var eventHandler_window_click = function () + { + RGraph.Redraw(); + } + window.addEventListener('click', eventHandler_window_click, false); + RGraph.AddEventListener('window_' + this.id, 'click', eventHandler_window_click); + + + + /** + * If the cursor is over a hotspot, change the cursor to a hand + */ + var eventHandler_canvas_mousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + + /** + * Get the mouse X/Y coordinates + */ + var mouseCoords = RGraph.getMouseXY(e); + var bar = obj.getBar(e); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i<obj.coords.length; i++) { + + var mouseX = mouseCoords[0]; // In relation to the canvas + var mouseY = mouseCoords[1]; // In relation to the canvas + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + + if (mouseX >= left && mouseX <= (left + width ) && mouseY >= top && mouseY <= (top + height) ) { + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = obj.Get('chart.tooltips')(i); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') { + var text = obj.Get('chart.tooltips')[i](i); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object') { + var text = obj.Get('chart.tooltips')[i]; + + } else { + var text = ''; + } + + if (text) { + canvas.style.cursor = 'pointer'; + } + return; + } + } + + canvas.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', eventHandler_canvas_mousemove, false); + RGraph.AddEventListener(this.id, 'mouseover', eventHandler_canvas_mousemove); + + + /** + * Install the onclick event handler for the tooltips + */ + var eventHandler_canvas_click = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id) + var obj = canvas.__object__; + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Clear(canvas); + obj.Draw(); + + /** + * Get the mouse X/Y coordinates + */ + var mouseCoords = RGraph.getMouseXY(e); + var bar = obj.getBar(e); + + /** + * This bit shows the tooltip (if required) + */ + if (bar) { + + var mouseX = mouseCoords[0]; // In relation to the canvas + var mouseY = mouseCoords[1]; // In relation to the canvas + var left = bar[0]; + var top = bar[1]; + var width = bar[2]; + var height = bar[3]; + var index = bar[4]; + + /** + * Show a tooltip if it's defined + */ + if (obj.Get('chart.tooltips')) { + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = obj.Get('chart.tooltips')(index); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[index]) == 'function') { + var text = obj.Get('chart.tooltips')[index](index); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object') { + var text = obj.Get('chart.tooltips')[index]; + + } else { + var text = ''; + } + + // Only now show a tooltip if one has been set + if (text) { + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(left, top, width, height); + obj.context.fillRect(left, top, width, height); + obj.context.stroke(); + obj.context.fill(); + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, index); + } + } + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + + return false; + } + this.canvas.addEventListener('click', eventHandler_canvas_click, false); + RGraph.AddEventListener(this.id, 'click', eventHandler_canvas_click); + + // This resets the bipolar graph + if (RGraph.Registry.Get('chart.tooltip')) { + RGraph.Registry.Get('chart.tooltip').style.display = 'none'; + RGraph.Registry.Set('chart.tooltip', null) + } + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Add the common .getShape() method + */ + this.getShape = this.getBar; + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the axes + */ + RGraph.Bipolar.prototype.DrawAxes = function () + { + // Draw the left set of axes + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.axis.color'); + + this.axisWidth = (RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2; + this.axisHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft + this.axisWidth, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft + this.axisWidth, this.gutterTop); + + this.context.stroke(); + + // Draw the right set of axes + this.context.beginPath(); + + var x = this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center'); + + this.context.moveTo(x, this.gutterTop); + this.context.lineTo(x, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + + this.context.stroke(); + } + + + /** + * Draws the tick marks on the axes + */ + RGraph.Bipolar.prototype.DrawTicks = function () + { + var numDataPoints = this.left.length; + var barHeight = ( (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom)- (this.left.length * (this.Get('chart.margin') * 2) )) / numDataPoints; + + // Draw the left Y tick marks + for (var i = RGraph.GetHeight(this) - this.gutterBottom; i >= this.gutterTop; i -= (barHeight + ( this.Get('chart.margin') * 2)) ) { + if (i < (RGraph.GetHeight(this) - this.gutterBottom) ) { + this.context.beginPath(); + this.context.moveTo(this.gutterLeft + this.axisWidth, i); + this.context.lineTo(this.gutterLeft + this.axisWidth + 3, i); + this.context.stroke(); + } + } + + //Draw the right axis Y tick marks + for (var i = RGraph.GetHeight(this) - this.gutterBottom; i >= this.gutterTop; i -= (barHeight + ( this.Get('chart.margin') * 2)) ) { + if (i < (RGraph.GetHeight(this) - this.gutterBottom) ) { + this.context.beginPath(); + this.context.moveTo(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center'), i); + this.context.lineTo(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center') - 3, i); + this.context.stroke(); + } + } + + var xInterval = this.axisWidth / 10; + + // Is chart.xtickinterval specified ? If so, use that. + if (typeof(this.Get('chart.xtickinterval')) == 'number') { + xInterval = this.Get('chart.xtickinterval'); + } + + + // Draw the left sides X tick marks + for (i=this.gutterLeft; i<(this.gutterLeft + this.axisWidth); i+=xInterval) { + this.context.beginPath(); + this.context.moveTo(i, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(i, (RGraph.GetHeight(this) - this.gutterBottom) + 4); + this.context.closePath(); + + this.context.stroke(); + } + + // Draw the right sides X tick marks + var stoppingPoint = RGraph.GetWidth(this) - this.gutterRight; + + for (i=(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center') + xInterval); i<=stoppingPoint; i+=xInterval) { + this.context.beginPath(); + this.context.moveTo(i, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(i, (RGraph.GetHeight(this) - this.gutterBottom) + 4); + this.context.closePath(); + + this.context.stroke(); + } + + // Store this for later + this.barHeight = barHeight; + } + + + /** + * Figures out the maximum value, or if defined, uses xmax + */ + RGraph.Bipolar.prototype.GetMax = function() + { + var max = 0; + var dec = this.Get('chart.scale.decimals'); + + // chart.xmax defined + if (this.Get('chart.xmax')) { + + max = this.Get('chart.xmax'); + + this.scale = []; + this.scale[0] = Number((max / 5) * 1).toFixed(dec); + this.scale[1] = Number((max / 5) * 2).toFixed(dec); + this.scale[2] = Number((max / 5) * 3).toFixed(dec); + this.scale[3] = Number((max / 5) * 4).toFixed(dec); + this.scale[4] = Number(max).toFixed(dec); + + this.max = max; + + + // Generate the scale ourselves + } else { + this.leftmax = RGraph.array_max(this.left); + this.rightmax = RGraph.array_max(this.right); + max = Math.max(this.leftmax, this.rightmax); + + this.scale = RGraph.getScale(max, this); + this.scale[0] = Number(this.scale[0]).toFixed(dec); + this.scale[1] = Number(this.scale[1]).toFixed(dec); + this.scale[2] = Number(this.scale[2]).toFixed(dec); + this.scale[3] = Number(this.scale[3]).toFixed(dec); + this.scale[4] = Number(this.scale[4]).toFixed(dec); + + this.max = this.scale[4]; + } + + // Don't need to return it as it is stored in this.max + } + + + /** + * Function to draw the left hand bars + */ + RGraph.Bipolar.prototype.DrawLeftBars = function () + { + // Set the stroke colour + this.context.strokeStyle = this.Get('chart.strokestyle'); + + for (i=0; i<this.left.length; ++i) { + + /** + * Turn on a shadow if requested + */ + if (this.Get('chart.shadow')) { + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + } + + this.context.beginPath(); + + // Set the colour + if (this.Get('chart.colors')[i]) { + this.context.fillStyle = this.Get('chart.colors')[i]; + } + + /** + * Work out the coordinates + */ + var width = ( (this.left[i] / this.max) * this.axisWidth); + var coords = [ + this.gutterLeft + this.axisWidth - width, + this.gutterTop + (i * ( this.axisHeight / this.left.length)) + this.Get('chart.margin'), + width, + this.barHeight + ]; + + // Draw the IE shadow if necessary + if (RGraph.isIE8() && this.Get('chart.shadow')) { + this.DrawIEShadow(coords); + } + + + this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]); + this.context.fillRect(coords[0], coords[1], coords[2], coords[3]); + + this.context.stroke(); + this.context.fill(); + + /** + * Add the coordinates to the coords array + */ + this.coords.push([ + coords[0], + coords[1], + coords[2], + coords[3] + ]); + } + + /** + * Turn off any shadow + */ + RGraph.NoShadow(this); + } + + + /** + * Function to draw the right hand bars + */ + RGraph.Bipolar.prototype.DrawRightBars = function () + { + // Set the stroke colour + this.context.strokeStyle = this.Get('chart.strokestyle'); + + /** + * Turn on a shadow if requested + */ + if (this.Get('chart.shadow')) { + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + } + + for (var i=0; i<this.right.length; ++i) { + this.context.beginPath(); + + // Set the colour + if (this.Get('chart.colors')[i]) { + this.context.fillStyle = this.Get('chart.colors')[i]; + } + + + var width = ( (this.right[i] / this.max) * this.axisWidth); + var coords = [ + this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center'), + this.Get('chart.margin') + (i * (this.axisHeight / this.right.length)) + this.gutterTop, + width, + this.barHeight + ]; + + // Draw the IE shadow if necessary + if (RGraph.isIE8() && this.Get('chart.shadow')) { + this.DrawIEShadow(coords); + } + this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]); + this.context.fillRect(coords[0], coords[1], coords[2], coords[3]); + + this.context.closePath(); + + /** + * Add the coordinates to the coords array + */ + this.coords.push([ + coords[0], + coords[1], + coords[2], + coords[3] + ]); + } + + this.context.stroke(); + + /** + * Turn off any shadow + */ + RGraph.NoShadow(this); + } + + + /** + * Draws the titles + */ + RGraph.Bipolar.prototype.DrawLabels = function () + { + this.context.fillStyle = this.Get('chart.text.color'); + + var labelPoints = new Array(); + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + + var max = Math.max(this.left.length, this.right.length); + + for (i=0; i<max; ++i) { + var barAreaHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + var barHeight = barAreaHeight / this.left.length; + var yPos = (i * barAreaHeight) + this.gutterTop; + + labelPoints.push(this.gutterTop + (i * barHeight) + (barHeight / 2) + 5); + } + + for (i=0; i<labelPoints.length; ++i) { + RGraph.Text(this.context, + this.Get('chart.text.font'), + this.Get('chart.text.size'), + this.gutterLeft + this.axisWidth + (this.Get('chart.gutter.center') / 2), + labelPoints[i], + String(this.Get('chart.labels')[i] ? this.Get('chart.labels')[i] : ''), + null, + 'center'); + } + + // Now draw the X labels for the left hand side + RGraph.Text(this.context,font,size,this.gutterLeft,RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')),null,'center'); + RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (1/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (2/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (3/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (4/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + + // Now draw the X labels for the right hand side + RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.2), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.4), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.6), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.8), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center'); + } + + /** + * Draws the titles + */ + RGraph.Bipolar.prototype.DrawTitles = function () + { + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.gutterLeft + 5, (this.gutterTop / 2) + 5, String(this.Get('chart.title.left')), 'center'); + RGraph.Text(this.context,this.Get('chart.text.font'), this.Get('chart.text.size'), RGraph.GetWidth(this) - this.gutterRight - 5, (this.gutterTop / 2) + 5, String(this.Get('chart.title.right')), 'center', 'right'); + + // Draw the main title for the whole chart + RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : null); + } + + + /** + * This function is used by MSIE only to manually draw the shadow + * + * @param array coords The coords for the bar + */ + RGraph.Bipolar.prototype.DrawIEShadow = function (coords) + { + var prevFillStyle = this.context.fillStyle; + var offsetx = this.Get('chart.shadow.offsetx'); + var offsety = this.Get('chart.shadow.offsety'); + + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.fillStyle = this.Get('chart.shadow.color'); + this.context.beginPath(); + + // Draw shadow here + this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2],coords[3]); + + this.context.fill(); + + // Change the fillstyle back to what it was + this.context.fillStyle = prevFillStyle; + } + + + /** + * Returns the appropriate focussed bar coordinates + * + * @param e object The event object + */ + RGraph.Bipolar.prototype.getBar = function (e) + { + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i<obj.coords.length; i++) { + + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + + if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) { + return [left,top,width,height,i]; + } + } + + return null; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.adjusting.js b/schall/static/RGraph/libraries/RGraph.common.adjusting.js new file mode 100644 index 0000000..6935b0e --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.adjusting.js @@ -0,0 +1,1167 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + RGraph.AllowAdjusting = function (obj) + { + var canvas = obj.canvas; + var context = obj.context; + + RGraph.Register(obj); + + if (obj.type == 'thermometer') { + var canvas_onmousedown = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = obj.coords; + + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + if ( mouseX > obj.coords[0] + && mouseX < (obj.coords[0] + obj.coords[2]) + ) { + + RGraph.Registry.Set('chart.thermometer.' + id + '.resizing', true); + obj.canvas.style.cursor = 'ns-resize'; + + /** + * Fire the RGraph event + */ + RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin'); + + // Fire the onmousemove handler + canvas_onmousemove(e); + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + + var canvas_onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = obj.coords; + + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + if (RGraph.Registry.Get('chart.thermometer.' + id + '.resizing')) { + + var capRadius = (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight) / 2; + var top = obj.gutterTop + capRadius; + var bottom = coords[1] + coords[3]; + var newvalue = obj.max - ((mouseY - top) / (bottom - obj.gutterTop - capRadius)) * (obj.max - obj.min); + + obj.value = Math.round(newvalue); + + /** + * Bounds checking + */ + if (obj.value > obj.max) { + obj.value = obj.max; + } else if (obj.value < obj.min) { + obj.value = obj.min; + } + + RGraph.Clear(canvas); + obj.Draw(); + + /** + * Fire the RGraph event + */ + RGraph.FireCustomEvent(e.target.__object__, 'onadjust'); + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + + var canvas_onmouseup = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = obj.coords; + + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + RGraph.Registry.Set('chart.thermometer.' + id + '.resizing', false); + obj.canvas.style.cursor = 'default'; + + /** + * Fire the RGraph event + */ + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + + + + + + + + + + + + + + + + } else if (obj.type == 'line') { + var canvas_onmousedown = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = obj.coords; + var mouseCoords = RGraph.getMouseXY(e); + + RGraph.Redraw(); + + for (var i=0; i<coords.length; ++i) { + + if ( mouseCoords[0] > coords[i][0] - 5 + && mouseCoords[1] > coords[i][1] - 5 + && mouseCoords[0] < coords[i][0] + 5 + && mouseCoords[1] < coords[i][1] + 5 + ) { + + var numDataSeries = obj.original_data.length; + var numDataPoints = obj.original_data[0].length; + var data_series = i / numDataPoints; + data_series = Math.floor(data_series); + + + + canvas.style.cursor = 'ns-resize'; + RGraph.FireCustomEvent(obj, 'onadjustbegin'); + RGraph.Registry.Set('chart.adjusting.line.' + id, [obj, i, [coords[i][0], coords[i][1]], data_series]); + + return; + } + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + var canvas_onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + var id = e.target.__object__.id; + + var state = RGraph.Registry.Get('chart.adjusting.line.' + id); + + if (state) { + var obj = state[0]; + var idx = state[1]; + var canvas = obj.canvas; + var context = obj.context; + var data_series = state[3]; + var points = obj.original_data[data_series]; + var mouseCoords = RGraph.getMouseXY(e); + var x = mouseCoords[0]; + var y = mouseCoords[1]; + var h = RGraph.GetHeight(obj); + + + // Don't allow adjusting to the gutter if out-of-bounds is NOT specified + if (!obj.Get('chart.outofbounds')) { + if (y >= (h - obj.gutterBottom)) { + y = h - obj.gutterBottom; + } else if (y <= obj.gutterTop) { + y = obj.gutterTop; + } + } + + // This accounts for a center X axis + if (obj.Get('chart.xaxispos') == 'center') { + y *= 2; + y -= (obj.gutterTop / 2); + } else if (obj.Get('chart.xaxispos') == 'top') { + y = RGraph.GetHeight(obj) - y; + } + + var pos = h - obj.gutterTop - obj.gutterBottom; + pos = pos - (y - obj.gutterTop); + var value = (obj.max / (h - obj.gutterTop - obj.gutterBottom)) * pos; + + if (obj.Get('chart.ylabels.invert')) { + value = obj.max - value; + } + + + + // Adjust the index so that it's applicable to the correct data series + for (var i=0; i<data_series; ++i) { + idx -= obj.original_data[0].length; + } + + obj.original_data[data_series][idx] = value; + + obj.Set('chart.ymax', obj.max); + canvas.style.cursor = 'ns-resize'; + RGraph.Redraw(); + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + + return; + + } else { + + var canvas = e.target; + var context = canvas.__object__.context; + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var x = mouseCoords[0]; + var y = mouseCoords[1]; + + for (var i=0; i<obj.coords.length; ++i) { + + if ( x > obj.coords[i][0] - 5 + && y > obj.coords[i][1] - 5 + && x < obj.coords[i][0] + 5 + && y < obj.coords[i][1] + 5 + ) { + + canvas.style.cursor = 'ns-resize'; + return; + } + } + } + + e.target.style.cursor = null; + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + var canvas_onmouseup = function (e) + { + var id = e.target.__object__.id; + + if (RGraph.Registry.Get('chart.adjusting.line.' + id)) { + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + + RGraph.Registry.Set('chart.adjusting.line.' + id, null); + e.target.style.cursor = null; + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + /** + * HProgress bar + */ + } else if (obj.type == 'hprogress') { + + + var canvas_onmousedown = function (e) + { + var id = e.target.__object__.id; + + RGraph.Registry.Set('chart.adjusting.hprogress.' + id, [true]); + + RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin'); + + canvas_onmousemove(e); + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + var canvas_onmousemove = function (e) + { + var id = e.target.__object__.id; + var state = RGraph.Registry.Get('chart.adjusting.hprogress.' + id); + + if (state && state.length) { + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + + if (obj.type == 'hprogress') { + + var coords = RGraph.getMouseXY(e); + coords[0] = Math.max(0, coords[0] - obj.gutterLeft); + var barWidth = canvas.width - obj.gutterLeft - obj.gutterRight; + + // Work out the new value + var value = (coords[0] / barWidth) * (obj.max - obj.Get('chart.min')); + value += obj.Get('chart.min'); + + obj.value = Math.max(0, value.toFixed()); + RGraph.Clear(obj.canvas); + obj.Draw(); + +/* + } else if (obj.type == 'vprogress') { + + var coords = RGraph.getMouseXY(e); + coords[1] = Math.max(0, coords[1] - obj.Get('chart.gutter')); + var barHeight = canvas.height - (2 * obj.Get('chart.gutter')); + + // Work out the new value + var value = ( (barHeight - coords[1]) / barHeight) * obj.max; + + obj.value = Math.max(0, value.toFixed()); + RGraph.Clear(obj.canvas); + obj.Draw(); +*/ + } + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + var canvas_onmouseup = function (e) + { + var id = e.target.__object__.id; + + if (RGraph.Registry.Get('chart.adjusting.hprogress.' + id)) { + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + + RGraph.Registry.Set('chart.adjusting.hprogress.' + id, null); + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + /** + * VProgress bar + */ + } else if (obj.type == 'vprogress') { + + + var canvas_onmousedown = function (e) + { + var id = e.target.__object__.id; + + RGraph.Registry.Set('chart.adjusting.vprogress.' + id, [true]); + + RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin'); + + canvas_onmousemove(e); + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + var canvas_onmousemove = function (e) + { + var id = e.target.__object__.id; + var state = RGraph.Registry.Get('chart.adjusting.vprogress.' + id); + + if (state && state.length) { + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + + if (obj.type == 'hprogress') { + + var coords = RGraph.getMouseXY(e); + coords[0] = Math.max(0, coords[0] - obj.gutterLeft); + var barWidth = canvas.width - obj.gutterLeft - obj.gutterRight; + + // Work out the new value + var value = (coords[0] / barWidth) * (obj.max - obj.Get('chart.min')); + value += obj.Get('chart.min'); + + obj.value = Math.max(0, value.toFixed(obj.Get('chart.scale.decimals'))); + + RGraph.Clear(obj.canvas); + obj.Draw(); + + } else if (obj.type == 'vprogress') { + + var coords = RGraph.getMouseXY(e); + coords[1] = Math.max(0, coords[1] - obj.gutterTop); + var barHeight = canvas.height - obj.gutterTop - obj.gutterBottom; + + // Work out the new value + var value = ( (barHeight - coords[1]) / barHeight) * obj.max; + + obj.value = Math.max(0, value.toFixed(obj.Get('chart.scale.decimals'))); + + RGraph.Clear(obj.canvas); + obj.Draw(); + } + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + var canvas_onmouseup = function (e) + { + var id = e.target.__object__.id; + + if (RGraph.Registry.Get('chart.adjusting.vprogress.' + id)) { + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + + RGraph.Registry.Set('chart.adjusting.vprogress.' + id, null); + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + /** + * Rose chart + */ + } else if (obj.type == 'rose') { + + + obj.Set('chart.ymax', obj.max); + + + var canvas_onmousemove = function (e) + { + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = RGraph.getMouseXY(e); + var segment = RGraph.Registry.Get('chart.adjusting.rose.' + id); + var x = Math.abs(coords[0] - obj.centerx); + var y = Math.abs(coords[1] - obj.centery); + var theta = Math.atan(y / x) * (180 / Math.PI); // theta is now in DEGREES + + + // Account for the correct quadrant + if (coords[0] >= obj.centerx && coords[1] < obj.centery) { + theta = 90 - theta; + } else if (coords[0] >= obj.centerx && coords[1] >= obj.centery) { + theta += 90; + } else if (coords[0] < obj.centerx && coords[1] >= obj.centery) { + theta = 90 - theta; + theta = 180 + theta; + + } else if (coords[0] < obj.centerx && coords[1] < obj.centery) { + theta = theta + 270; + } + + var Opp = y; + var Adj = x; + var Hyp = Math.abs(Adj / Math.sin(theta / (180 / Math.PI))); + + for (var i=0; i<obj.angles.length; ++i) { + if ( + theta > obj.angles[i][0] + && theta < obj.angles[i][1]) { + + if (RGraph.Registry.Get('chart.adjusting.rose.' + id) && i == segment[6]) { + var newvalue = (Hyp / (obj.radius - 25) ) * obj.max; + obj.data[i] = Math.min(newvalue, obj.max); + + RGraph.Clear(obj.canvas); + obj.Draw(); + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + } + + if (Hyp <= (obj.angles[i][2] + 5) && Hyp >= (obj.angles[i][2] - 5) ) { + canvas.style.cursor = 'move'; + return false; + + } else if (obj.Get('chart.tooltips') && Hyp <= (obj.angles[i][2] - 5) ) { + canvas.style.cursor = 'pointer'; + return false; + } + + } + } + + canvas.style.cursor = 'default'; + + return; + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + var canvas_onmousedown = function (e) + { + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var coords = RGraph.getMouseXY(e); + var segment = obj.getSegment(e, 5); + + if (segment && segment.length && !RGraph.Registry.Get('chart.adjusting.rose.' + id)) { + var x = Math.abs(coords[0] - obj.centerx); + var y = Math.abs(coords[1] - obj.centery); + var a = Math.atan(y / x) * (180 / Math.PI); // a is now in DEGREES + + // Account for the correct quadrant + if (coords[0] >= obj.centerx && coords[1] < obj.centery) { + a = 90 - a; + a += 270; + } else if (coords[0] >= obj.centerx && coords[1] >= obj.centery) { + // Nada + } else if (coords[0] < obj.centerx && coords[1] >= obj.centery) { + a = 90 - a; + a += 90; + } else if (coords[0] < obj.centerx && coords[1] < obj.centery) { + a += 180; + } + + var hyp = Math.abs(y / Math.sin(a / 57.3)); + + if (hyp >= (segment[2] - 10) ) { + + /** + * Hide any currently shown tooltip + */ + if (RGraph.Registry.Get('chart.tooltip')) { + RGraph.Registry.Get('chart.tooltip').style.display = 'none'; + RGraph.Registry.Set('chart.tooltip', null); + } + + RGraph.Registry.Set('chart.adjusting.rose.' + id, segment); + + RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin'); + + e.stopPropagation(); + } + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + var canvas_onmouseup = function (e) + { + var obj = e.target.__object__; + var id = obj.id; + + if (RGraph.Registry.Get('chart.adjusting.rose.' + id)) { + + + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + RGraph.Registry.Set('chart.adjusting.rose.' + id, null); + e.stopPropagation(); + + return false; + } + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + + + + + + + + + + + + + + + + + + + /** + * Bar chart + */ + } else if (obj.type == 'bar') { + + // Stacked bar charts not supported + if (obj.Get('chart.grouping') == 'stacked') { + alert('[BAR] Adjusting stacked bar charts is not supported'); + return; + } + + + var canvas = obj.canvas; + var context = obj.context; + + + var canvas_onmousemove = function (e) + { + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var mouse = RGraph.getMouseXY(e); + var mousex = mouse[0]; + var mousey = mouse[1]; // mousey, mousey... + + + // Loop through the coords to see if the mouse position is at the top of a bar + for (var i=0; i<obj.coords.length; ++i) { + + var barX = obj.coords[i][0]; + var barY = obj.coords[i][1]; + var barW = obj.coords[i][2]; + var barH = obj.coords[i][3]; + + if (mousex > barX && mousex < (barX + barW)) { + + /** + * Change the mouse pointer + */ + //if ( (obj.Get('chart.xaxispos') == 'bottom' && (mousey > (barY - 5) && mousey < (barY + 5))) + // || (((obj.Get('chart.xaxispos') == 'center' || obj.Get('chart.xaxispos') == 'top') && mousey > (barY + barH - 5) && mousey < (barY + barH + 5))) + // || ((obj.Get('chart.xaxispos') == 'center' && barX < (obj.canvas.height / 2) && mousey < (barY + 5) && mousey > barY)) + // ) { + // canvas.style.cursor = 'ns-resize'; + + //} else { + // canvas.style.cursor = 'default'; + //} + + + + var idx = RGraph.Registry.Get('chart.adjusting.bar.' + id) + + if (typeof(idx) == 'number') { + + // This accounts for a center X axis + if (obj.Get('chart.xaxispos') == 'center') { + obj.grapharea /= 2; + } + + var newheight = obj.grapharea - (mousey - obj.gutterTop); + var newvalue = (newheight / obj.grapharea) * obj.max; + + // Account for X axis at the top + if (obj.Get('chart.xaxispos') == 'top') { + newvalue = obj.max - newvalue; + } + + // Top and bottom boundaries + if (newvalue > obj.max) newvalue = obj.max; + + if (obj.Get('chart.xaxispos') == 'center') { + if (newvalue < (-1 * obj.max)) newvalue = (-1 * obj.max); + + } else { + if (newvalue < 0) newvalue = 0; + } + + ///////////////// This was fun to work out... ///////////////// + + var j, index; + + for (var j=0, index=0; j<obj.data.length; ++j,++index) { + + if (typeof(obj.data[j]) == 'object') { + + for (var k=0; k<obj.data[j].length && index <= idx; ++k, ++index) { + if (index == idx) { + + + if (obj.Get('chart.xaxispos') == 'top') { + newvalue *= -1; + } + + obj.data[j][k] = newvalue; + var b = true; + break; + } + } + + --index; + } else if (typeof(obj.data[j]) == 'number') { + + if (index == idx) { + + if (obj.Get('chart.xaxispos') == 'top') { + newvalue *= -1; + } + + obj.data[j] = newvalue; + + // No need to set b + break; + } + } + + if (b) { + break; + } + } + /////////////////////////////////////////////////////////////// + + RGraph.Clear(canvas); + obj.Draw(); + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + } + + return; + } + } + + canvas.style.cursor = 'default'; + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + + var canvas_onmousedown = function (e) + { + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var mouse = RGraph.getMouseXY(e); + var mousex = mouse[0]; + var mousey = mouse[1]; + + // Loop through the coords to see if the mouse position is at the top of a bar + for (var i=0; i<obj.coords.length; ++i) { + if (mousex > obj.coords[i][0] && mousex < (obj.coords[i][0] + obj.coords[i][2])) { + + RGraph.FireCustomEvent(obj, 'onadjustbegin'); + + obj.Set('chart.ymax', obj.max); + RGraph.Registry.Set('chart.adjusting.bar.' + id, i); + + canvas_onmousemove(e); + } + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + + var canvas_onmouseup = function (e) + { + var id = e.target.__object__.id; + + if (typeof(RGraph.Registry.Get('chart.adjusting.bar.' + id)) == 'number') { + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + + RGraph.Registry.Set('chart.adjusting.bar.' + id, null); + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + + + + + + + + + + + + + + /** + * The Radar chart + */ + } else if (obj.type == 'radar') { + + + var canvas = obj.canvas; + var context = obj.context; + + + var canvas_onmousemove = function (e) + { + var id = e.target.id; + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseDown = RGraph.Registry.Get('chart.adjusting.radar.' + id); + var mouseCoords = RGraph.getMouseXY(e); + + if (mouseDown) { + + canvas.style.cursor = 'move'; + + var dataset = mouseDown[0]; + var index = mouseDown[1]; + var dx = mouseCoords[0] - obj.centerx; + var dy = mouseCoords[1] - obj.centery; + var hyp = Math.sqrt((dx * dx) + (dy * dy)); + var newvalue = (hyp / (obj.size / 2)) * obj.max; + + newvalue = Math.min(obj.max, newvalue); + newvalue = Math.max(0, newvalue); + + obj.data[mouseDown[0]][mouseDown[1]] = newvalue; + RGraph.Clear(canvas); + obj.Draw(); + + /** + * Fire the onadjust event + */ + RGraph.FireCustomEvent(obj, 'onadjust'); + + } else { + + // Determine if the mouse is near a point, and if so, change the pointer + for (var ds = 0; ds<obj.coords.length; ds++) { + for (var i=0; i<obj.coords[ds].length; ++i) { + + var dx = Math.abs(mouseCoords[0] - obj.coords[ds][i][0]); + var dy = Math.abs(mouseCoords[1] - obj.coords[ds][i][1]); + var a = Math.atan(dy / dx); + + + var hyp = Math.sqrt((dx * dx) + (dy * dy)); + + if (hyp <= 5) { + canvas.style.cursor = 'move'; + return; + } + } + } + + canvas.style.cursor = 'default'; + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + var canvas_onmousedown = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var id = obj.id; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + + + // Determine if the mouse is near a point + for (var j=0; j<obj.coords.length; ++j) { + for (var i=0; i<obj.coords[j].length; ++i) { + + var dx = Math.abs(mouseCoords[0] - obj.coords[j][i][0]); + var dy = Math.abs(mouseCoords[1] - obj.coords[j][i][1]); + var a = Math.atan(dy / dx); + + + var hyp = Math.sqrt((dx * dx) + (dy * dy)); + + if (hyp <= 5) { + canvas.style.cursor = 'pointer'; + RGraph.FireCustomEvent(obj, 'onadjustbegin'); + + RGraph.Registry.Set('chart.adjusting.radar.' + id, [j, i, obj.coords[j][i][0] > obj.centerx, obj.coords[j][i][1] > obj.centery]); + return; + } + } + } + + canvas.style.cursor = 'default'; + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + var canvas_onmouseup = function (e) + { + var id = e.target.id; + + if (RGraph.Registry.Get('chart.adjusting.radar.' + id)) { + RGraph.FireCustomEvent(e.target.__object__, 'onadjustend'); + } + + RGraph.Registry.Set('chart.adjusting.radar.' + id, null); + canvas.style.cursor = 'default'; + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + + var canvas_onmouseout = function (e) + { + canvas_onmouseup(e); + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + /** + * Gantt chart + */ + } else if (obj.type == 'gantt') { + + + /** + * The onmousedown event handler + */ + var canvas_onmousedown = function (e) + { + var canvas = e.target; + var id = canvas.id; + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + for (var i=0; i<obj.coords.length; ++i) { + + var coordX = obj.coords[i][0]; + var coordY = obj.coords[i][1]; + var coordW = obj.coords[i][2]; + var coordH = obj.coords[i][3]; + + if (mouseX > coordX + && mouseX < (coordX + coordW) + && mouseY > coordY + && mouseY < (coordY + coordH) + ) { + + var mode = (mouseX >= (coordX + coordW - 5) ? 'resize' : 'move'); + + RGraph.Registry.Set('chart.adjusting.gantt', {'index': i,'object': obj,'mousex': mouseX,'mousey': mouseY,'event_start': obj.Get('chart.events')[i][0],'event_duration': obj.Get('chart.events')[i][1],'mode': mode}); + + RGraph.FireCustomEvent(obj, 'onadjustbegin'); + return; + } + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + /** + * Change the pointer + */ + var canvas_onmousemove = function (e) + { + var canvas = e.target; + var id = canvas.id; + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + for (var i=0; i<obj.coords.length; ++i) { + + var coordX = obj.coords[i][0]; + var coordY = obj.coords[i][1]; + var coordW = obj.coords[i][2]; + var coordH = obj.coords[i][3]; + + if (mouseX > coordX + && mouseX < (coordX + coordW) + && mouseY > coordY + && mouseY < (coordY + coordH) + ) { + + canvas.style.cursor = 'ew-resize'; + return; + } + } + + canvas.style.cursor = 'default'; + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + + + + + + + var window_onmousemove = function (e) + { + var conf = RGraph.Registry.Get('chart.adjusting.gantt'); + + if (conf) { + + var obj = conf['object']; + var id = obj.id; + var index = conf['index']; + var startX = conf['mousex']; + var startY = conf['mousey']; + var eventStart = conf['event_start']; + var duration = conf['event_duration']; + var mode = conf['mode']; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + RGraph.FireCustomEvent(obj, 'onadjust'); + + if (mode == 'resize') { + + /** + * Account for the right hand gutter. Appears to be a FF bug + */ + if (mouseX > (RGraph.GetWidth(obj) - obj.gutterRight)) { + mouseX = RGraph.GetWidth(obj) - obj.gutterRight; + } + + + var diff = ((mouseX - startX) / (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight)) * obj.Get('chart.xmax'); + diff = Math.round(diff); + + obj.Get('chart.events')[index][1] = duration + diff; + + if (obj.Get('chart.events')[index][1] < 0) { + obj.Get('chart.events')[index][1] = 1; + } + + } else { + + var diff = ((mouseX - startX) / (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight)) * obj.Get('chart.xmax'); + diff = Math.round(diff); + + if ( eventStart + diff > 0 + && (eventStart + diff + obj.Get('chart.events')[index][1]) < obj.Get('chart.xmax')) { + + obj.Get('chart.events')[index][0] = eventStart + diff; + + } else if (eventStart + diff < 0) { + obj.Get('chart.events')[index][0] = 0; + + } else if ((eventStart + diff + obj.Get('chart.events')[index][1]) > obj.Get('chart.xmax')) { + obj.Get('chart.events')[index][0] = obj.Get('chart.xmax') - obj.Get('chart.events')[index][1]; + } + } + + RGraph.Redraw(); + RGraph.FireCustomEvent(obj, 'onadjust'); + } + } + window.addEventListener('mousemove', window_onmousemove, false); + RGraph.AddEventListener('window_' + canvas.id, 'mousemove', window_onmousemove); + + + + + + + + + + + var window_onmouseup = function (e) + { + if (RGraph.Registry.Get('chart.adjusting.gantt')) { + + var conf = RGraph.Registry.Get('chart.adjusting.gantt'); + var obj = conf['object']; + var id = obj.id; + + RGraph.FireCustomEvent(obj, 'onadjustend'); + RGraph.Registry.Set('chart.adjusting.gantt', null); + } + } + window.addEventListener('mouseup', window_onmouseup, false); + RGraph.AddEventListener('window_' + canvas.id, 'mouseup', window_onmouseup); + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.annotate.js b/schall/static/RGraph/libraries/RGraph.common.annotate.js new file mode 100644 index 0000000..450cf06 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.annotate.js @@ -0,0 +1,340 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + + /** + * The function which controls the annotate feature + * + * @param object obj The graph object + */ + RGraph.Annotate = function (obj) + { + /** + * This installs some event handlers + */ + if (obj.Get('chart.annotatable')) { + + var canvas = obj.canvas; + var context = obj.context; + + /** + * Capture the mouse events so we can set whther the mouse is down or not + */ + var canvas_onmousedown = function (e) + { + if (e.button == 0) { + + e.target.__object__.Set('chart.mousedown', true); + + // Get the context + var obj = e.target.__object__; + var context = obj.context; + + // Don't want any "joining" lines or colour "bleeding" + context.beginPath(); + + // Accommodate Chrome + var coords = RGraph.getMouseXY(e); + var x = coords[0]; + var y = coords[1]; + + // Clear the annotation recording + RGraph.Registry.Set('annotate.actions', [obj.Get('chart.annotate.color')]); + + context.strokeStyle = obj.Get('chart.annotate.color'); + + context.moveTo(x, y); + + // Set the lineWidth + context.lineWidth = 1; + + RGraph.Registry.Set('started.annotating', false); + RGraph.Registry.Set('chart.annotating', obj); + + /** + * Fire the onannotatebegin event. It also fires an event called ononnotatestart for BC purposes + */ + RGraph.FireCustomEvent(obj, 'onannotatestart'); + RGraph.FireCustomEvent(obj, 'onannotatebegin'); + } + + return false; + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + /** + * This cancels annotating for ALL canvases + */ + var window_onmouseup = function (e) + { + var obj = RGraph.Registry.Get('chart.annotating'); + var tags = document.getElementsByTagName('canvas'); + + for (var i=0; i<tags.length; ++i) { + if (tags[i].__object__) { + tags[i].__object__.Set('chart.mousedown', false); + } + } + + if (e.button != 0 || !obj) { + return; + } + + // Store the annotations in browser storage if it's available + if (RGraph.Registry.Get('annotate.actions') && RGraph.Registry.Get('annotate.actions').length > 0 && window.localStorage) { + + var id = '__rgraph_annotations_' + e.target.id + '__'; + var annotations = window.localStorage[id] ? window.localStorage[id] + '|' : ''; + annotations += RGraph.Registry.Get('annotate.actions'); + + // Store the annotations information in HTML5 browser storage here + window.localStorage[id] = annotations; + } + + // Clear the recorded annotations + RGraph.Registry.Set('annotate.actions', []); + + /** + * Fire the annotate event + */ + + RGraph.FireCustomEvent(obj, 'onannotateend'); + } + window.addEventListener('mouseup', window_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'window_mouseup', window_onmouseup); + + /** + * Canvas onmouseup event + */ + var canvas_onmouseup = function (e) + { + var obj = e.target.__object__; + + RGraph.Registry.Set('chart.mousedown', false); + } + canvas.addEventListener('mouseup', canvas_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup); + + /** + * The canvas onmousemove function + */ + var canvas_onmousemove = function (e) + { + var e = RGraph.FixEventObject(e); + var obj = e.target.__object__; + var coords = RGraph.getMouseXY(e); + var x = coords[0]; + var y = coords[1]; + var width = canvas.width; + var height = canvas.height; + + obj.context.lineWidth = 1; + + // Don't allow annotating in the gutter + // + // CHANGED 20TH DECEMBER 2010 TO ALLOW ANNOTATING IN THE GUTTER + if (true) { + + canvas.style.cursor = 'crosshair'; + + if (obj.Get('chart.mousedown')) { + + // Special case for HBars and Gantts with their extra wide left gutter + if ( (obj.type != 'hbar' && obj.type != 'gantt') || x > obj.gutterLeft) { + + /** + * This is here to stop annotating in the gutter + */ + if (RGraph.Registry.Get('started.annotating') == false) { + context.moveTo(x, y); + RGraph.Registry.Set('started.annotating', true) + } + + context.lineTo(x, y); + + RGraph.Registry.Set('annotate.actions', RGraph.Registry.Get('annotate.actions') + '|' + x + ',' + y); + + context.stroke(); + + /** + * Fire the annotate event + */ + RGraph.FireCustomEvent(obj, 'onannotate'); + } + } + + } else { + canvas.style.cursor = 'default'; + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + RGraph.ReplayAnnotations(obj); + } + } + + + /** + * Shows the mini palette used for annotations + * + * @param object e The event object + */ + RGraph.Showpalette = function (e) + { + var isSafari = navigator.userAgent.indexOf('Safari') ? true : false; + + e = RGraph.FixEventObject(e); + + var canvas = e.target.parentNode.__canvas__; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var div = document.createElement('DIV'); + var coords = RGraph.getMouseXY(e); + + div.__object__ = obj; // The graph object + div.className = 'RGraph_palette'; + div.style.position = 'absolute'; + div.style.backgroundColor = 'white'; + div.style.border = '1px solid black'; + div.style.left = 0; + div.style.top = 0; + div.style.padding = '3px'; + div.style.paddingBottom = 0; + div.style.paddingRight = 0; + div.style.opacity = 0; + div.style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; + div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; + div.style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; + div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)'; + + var common_css = 'padding: 1px; display: inline; display: inline-block; width: 15px; height: 15px; margin-right: 3px; cursor: pointer;' + (isSafari ? 'margin-bottom: 3px' : ''); + var common_mouseover = ' onmouseover="this.style.border = \'1px black solid\'; this.style.padding = 0"'; + var common_mouseout = ' onmouseout="this.style.border = 0; this.style.padding = \'1px\'" '; + + var str = ''; + + var colors = ['red', 'blue', 'green', 'black', 'yellow', 'magenta', 'pink', 'cyan', 'purple', '#ddf', 'gray', '#36905c']; + + for (i=0; i<colors.length; ++i) { + str = str + '<span ' + common_mouseover + common_mouseout + ' style="background-color: ' + colors[i] + '; ' + common_css + '" onclick="this.parentNode.__object__.Set(\'chart.annotate.color\', this.style.backgroundColor); this.parentNode.style.display = \'none\'; RGraph.FireCustomEvent(this.parentNode.__object__, \'onannotatecolor\')"> </span>'; + + // This makes the colours go across two levels + if (i == 5) { + str += '<br />'; + } + } + + div.innerHTML = str; + document.body.appendChild(div); + + /** + * Now the div has been added to the document, move it up and left and set the width and height + */ + div.style.width = (div.offsetWidth) + 'px'; + div.style.height = (div.offsetHeight - (RGraph.isIE9up() ? 5 : 5)) + 'px'; + div.style.left = Math.max(0, e.pageX - div.offsetWidth - 2) + 'px'; + div.style.top = (e.pageY - div.offsetHeight - 2) + 'px'; + + /** + * Store the palette div in the registry + */ + RGraph.Registry.Set('palette', div); + + setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.2", 50); + setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.4", 100); + setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.6", 150); + setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.8", 200); + setTimeout("RGraph.Registry.Get('palette').style.opacity = 1", 250); + + RGraph.HideContext(); + + window.onclick = function () + { + RGraph.HidePalette(); + } + + // Should this be here? Yes. This function is being used as an event handler. + e.stopPropagation(); + return false; + } + + + /** + * Clears any annotation data from global storage + * + * @param string id The ID of the canvas + */ + RGraph.ClearAnnotations = function (id) + { + var canvas = document.getElementById(id); + var obj = canvas.__object__; + + if (window.localStorage && window.localStorage['__rgraph_annotations_' + id + '__'] && window.localStorage['__rgraph_annotations_' + id + '__'].length) { + window.localStorage['__rgraph_annotations_' + id + '__'] = []; + + RGraph.FireCustomEvent(obj, 'onannotateclear'); + } + } + + + /** + * Replays stored annotations + * + * @param object obj The graph object + */ + RGraph.ReplayAnnotations = function (obj) + { + // Check for support + if (!window.localStorage) { + return; + } + + var context = obj.context; + var annotations = window.localStorage['__rgraph_annotations_' + obj.id + '__']; + var i, len, move, coords; + + context.beginPath(); + context.lineWidth = 2; + + if (annotations && annotations.length) { + annotations = annotations.split('|'); + } else { + return; + } + + for (i=0, len=annotations.length; i<len; ++i) { + if (!annotations[i].match(/^[0-9]+,[0-9]+$/)) { + context.stroke(); + context.beginPath(); + context.strokeStyle = annotations[i]; + move = true; + continue; + } + + coords = annotations[i].split(','); + + if (move) { + context.moveTo(coords[0], coords[1]); + move = false; + } else { + context.lineTo(coords[0], coords[1]); + } + } + + context.stroke(); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.context.js b/schall/static/RGraph/libraries/RGraph.common.context.js new file mode 100644 index 0000000..ee6c6ae --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.context.js @@ -0,0 +1,579 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + + /** + * This gunction shows a context menu containing the parameters + * provided to it + * + * @param object canvas The canvas object + * @param array menuitems The context menu menuitems + * @param object e The event object + */ + RGraph.Contextmenu = function (canvas, menuitems, e) + { + e = RGraph.FixEventObject(e); + + /** + * Fire the custom RGraph event onbeforecontextmenu + */ + RGraph.FireCustomEvent(canvas.__object__, 'onbeforecontextmenu'); + + /** + * Hide any existing menu + */ + if (RGraph.Registry.Get('chart.contextmenu')) { + RGraph.HideContext(); + } + + // Hide any zoomed canvas + RGraph.HideZoomedCanvas(); + + /** + * Hide the palette if necessary + */ + RGraph.HidePalette(); + + /** + * This is here to ensure annotating is OFF + */ + canvas.__object__.Set('chart.mousedown', false); + + var x = e.pageX; + var y = e.pageY; + var div = document.createElement('div'); + var bg = document.createElement('div'); + + div.className = 'RGraph_contextmenu'; + div.__canvas__ = canvas; /* Store a reference to the canvas on the contextmenu object */ + div.style.position = 'absolute'; + div.style.left = 0; + div.style.top = 0; + div.style.border = '1px solid black'; + div.style.backgroundColor = 'white'; + div.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + div.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + div.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)'; + div.style.opacity = 0; + + bg.className = 'RGraph_contextmenu_background'; + bg.style.position = 'absolute'; + bg.style.backgroundColor = '#ccc'; + bg.style.borderRight = '1px solid #aaa'; + bg.style.top = 0; + bg.style.left = 0; + bg.style.width = '18px'; + bg.style.height = '100%'; + bg.style.opacity = 0; + + + div = document.body.appendChild(div); + bg = div.appendChild(bg); + + + /** + * Now add the context menu items + */ + for (i=0; i<menuitems.length; ++i) { + + var menuitem = document.createElement('div'); + + menuitem.__canvas__ = canvas; + menuitem.__contextmenu__ = div; + menuitem.className = 'RGraph_contextmenu_item'; + + if (menuitems[i]) { + menuitem.style.padding = '2px 5px 2px 23px'; + menuitem.style.fontFamily = 'Arial'; + menuitem.style.fontSize = '10pt'; + menuitem.style.fontWeight = 'normal'; + menuitem.innerHTML = menuitems[i][0]; + + if (RGraph.is_array(menuitems[i][1])) { + menuitem.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAQUlEQVQImY3NoQ2AMABE0ZewABMyGQ6mqWODzlAclBSFO8HZl8uf0FFxCHtwYkt4Y6ChYE44cGH9/fyae2p2LAleW9oVTQuVf6gAAAAASUVORK5CYII=)'; + menuitem.style.backgroundRepeat = 'no-repeat'; + menuitem.style.backgroundPosition = '97% center'; + } + + // Add the mouseover event + if (menuitems[i][1]) { + if (menuitem.addEventListener) { + menuitem.addEventListener("mouseover", function (e) {RGraph.HideContextSubmenu(); e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false); + menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false); + } else { + menuitem.attachEvent("onmouseover", function () {RGraph.HideContextSubmenu();event.srcElement.style.backgroundColor = '#eee';event.srcElement.style.cursor = 'pointer';} + , false); + menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false); + } + } else { + if (menuitem.addEventListener) { + menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false); + menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false); + } else { + menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false); + menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false); + } + } + + } else { + menuitem.style.borderBottom = '1px solid #ddd'; + menuitem.style.marginLeft = '25px'; + } + + div.appendChild(menuitem); + + /** + * Install the event handler that calls the menuitem + */ + if (menuitems[i] && menuitems[i][1] && typeof(menuitems[i][1]) == 'function') { + if (document.all && 0) { // MSIE bit no longer necessary + menuitem.attachEvent('onclick', menuitems[i][1]); + menuitem.attachEvent('onclick', function () {RGraph.HideContext();}); + } else { + menuitem.addEventListener('click', menuitems[i][1], false); + } + + // Submenu + } else if (menuitems[i] && menuitems[i][1] && RGraph.is_array(menuitems[i][1])) { + (function () + { + var tmp = menuitems[i][1]; // This is here because of "references vs primitives" and how they're passed around in Javascript + menuitem.addEventListener('mouseover', function (e) {RGraph.Contextmenu_submenu(canvas.__object__, tmp, e.target);}, false); + })(); + } + } + + /** + * Now all the menu items have been added, set the shadow width + * Shadow now handled by CSS3? + */ + div.style.width = (div.offsetWidth + 10) + 'px'; + div.style.height = (div.offsetHeight - 2) + 'px'; + + /** + * Set the background (the left bar) width if it's MSIE + */ + //if (document.all) { + // bg.style.height = (div.offsetHeight - 2) + 'px'; + //} + + // Show the menu to the left or the right (normal) of the cursor? + if (x + div.offsetWidth > document.body.offsetWidth) { + x -= div.offsetWidth; + } + + // Reposition the menu (now we have the real offsetWidth) + div.style.left = x + 'px'; + div.style.top = y + 'px'; + + /** + * Do a little fade in effect + */ + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.2", 50); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.4", 100); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.6", 150); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.8", 200); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 1", 250); + + // The fade in effect on the left gray bar + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.2", 50); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.4", 100); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.6", 150); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.8", 200); + setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 1", 250); + + // Store the context menu in the registry + RGraph.Registry.Set('chart.contextmenu', div); + RGraph.Registry.Set('chart.contextmenu.bg', bg); + RGraph.Registry.Get('chart.contextmenu').oncontextmenu = function () {return false;}; + RGraph.Registry.Get('chart.contextmenu.bg').oncontextmenu = function () {return false;}; + + /** + * Install the event handlers that hide the context menu + */ + canvas.addEventListener('click', function () {RGraph.HideContext();}, false); + + window.onclick = function (e) + { + RGraph.HideContext(); + + // Removed on 3/7/10 - stops a bug in conjunction with annotating which presents itself on the Rscatter + //RGraph.Redraw(); + + // Fire the onclick event again + if (e.target.onclick && e.button == 0) { + e.target.onclick(e); + } + } + + window.onresize = function () {RGraph.HideContext();} + + /** + * Add the __shape__ object to the context menu + */ + + /** + * Set the shape coords from the .getShape() method + */ + if (typeof(canvas.__object__.getShape) == 'function') { + RGraph.Registry.Get('chart.contextmenu').__shape__ = canvas.__object__.getShape(e); + } + + + e.stopPropagation(); + + /** + * Fire the (RGraph) oncontextmenu event + */ + RGraph.FireCustomEvent(canvas.__object__, 'oncontextmenu'); + + return false; + } + + + /** + * Hides the context menu if it's currently visible + */ + RGraph.HideContext = function () + { + var cm = RGraph.Registry.Get('chart.contextmenu'); + var cmbg = RGraph.Registry.Get('chart.contextmenu.bg'); + + //Hide any submenu currently being displayed + RGraph.HideContextSubmenu(); + + if (cm) { + cm.parentNode.removeChild(cm); + cmbg.parentNode.removeChild(cmbg); + + cm.style.visibility = 'hidden'; + cm.style.display = 'none'; + RGraph.Registry.Set('chart.contextmenu', null); + + cmbg.style.visibility = 'hidden'; + cmbg.style.display = 'none'; + RGraph.Registry.Set('chart.contextmenu.bg', null); + } + } + + + /** + * Hides the context menus SUBMENU if it's currently visible + */ + RGraph.HideContextSubmenu = function () + { + var sub = RGraph.Registry.Get('chart.contextmenu.submenu'); + + if (sub) { + sub.style.visibility = 'none'; + sub.style.display = 'none'; + RGraph.Registry.Set('chart.contextmenu.submenu', null); + } + } + + + /** + * Shows the context menu after making a few checks - not opera (doesn't support oncontextmenu, + * not safari (tempermentality), not chrome (hmmm) + */ + RGraph.ShowContext = function (obj) + { + RGraph.HidePalette(); + + if (obj.Get('chart.contextmenu') && obj.Get('chart.contextmenu').length) { + + var isOpera = navigator.userAgent.indexOf('Opera') >= 0; + var isSafari = navigator.userAgent.indexOf('Safari') >= 0; + var isChrome = navigator.userAgent.indexOf('Chrome') >= 0; + var isMacFirefox = navigator.userAgent.indexOf('Firefox') > 0 && navigator.userAgent.indexOf('Mac') > 0; + var isIE9 = navigator.userAgent.indexOf('MSIE 9') >= 0; + + if (((!isOpera && !isSafari) || isChrome) && !isMacFirefox) { + + obj.canvas.oncontextmenu = function (e) + { + e = RGraph.FixEventObject(e); + + if (e.ctrlKey) return true; + + RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e); + + return false; + } + + // Accomodate Opera and Safari - use double click event + } else { + + obj.canvas.addEventListener('dblclick', function (e) + { + if (e.ctrlKey) return true; + + if (!RGraph.Registry.Get('chart.contextmenu')) { + RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e); + } + }, false); + } + } + } + + + /** + * This draws a submenu should it be necessary + * + * @param object obj The graph object + * @param object menu The context menu + */ + RGraph.Contextmenu_submenu = function (obj, menuitems, parentMenuItem) + { + RGraph.HideContextSubmenu(); + + var canvas = obj.canvas; + var context = obj.context; + var menu = parentMenuItem.parentNode; + + var subMenu = document.createElement('DIV'); + subMenu.style.position = 'absolute'; + subMenu.style.width = '100px'; + subMenu.style.top = menu.offsetTop + parentMenuItem.offsetTop + 'px'; + subMenu.style.left = (menu.offsetLeft + menu.offsetWidth - (RGraph.isIE8() ? 9 : 0)) + 'px'; + subMenu.style.backgroundColor = 'white'; + subMenu.style.border = '1px solid black'; + subMenu.className = 'RGraph_contextmenu'; + subMenu.__contextmenu__ = menu; + subMenu.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + subMenu.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + subMenu.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + subMenu.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)'; + document.body.appendChild(subMenu); + + for (var i=0; i<menuitems.length; ++i) { + + var menuitem = document.createElement('DIV'); + + menuitem.__canvas__ = canvas; + menuitem.__contextmenu__ = menu; + menuitem.className = 'RGraph_contextmenu_item'; + + if (menuitems[i]) { + menuitem.style.padding = '2px 5px 2px 23px'; + menuitem.style.fontFamily = 'Arial'; + menuitem.style.fontSize = '10pt'; + menuitem.style.fontWeight = 'normal'; + menuitem.innerHTML = menuitems[i][0]; + + if (menuitems[i][1]) { + if (menuitem.addEventListener) { + menuitem.addEventListener("mouseover", function (e) {e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false); + menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false); + } else { + menuitem.attachEvent("onmouseover", function () {event.srcElement.style.backgroundColor = 'rgba(0,0,0,0.2)'; event.srcElement.style.cursor = 'pointer'}, false); + menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false); + } + } else { + if (menuitem.addEventListener) { + menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false); + menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false); + } else { + menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false); + menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false); + } + } + } else { + menuitem.style.borderBottom = '1px solid #ddd'; + menuitem.style.marginLeft = '25px'; + } + + subMenu.appendChild(menuitem); + + if (menuitems[i] && menuitems[i][1]) { + if (document.all) { + menuitem.attachEvent('onclick', menuitems[i][1]); + } else { + menuitem.addEventListener('click', menuitems[i][1], false); + } + } + } + + + var bg = document.createElement('DIV'); + bg.className = 'RGraph_contextmenu_background'; + bg.style.position = 'absolute'; + bg.style.backgroundColor = '#ccc'; + bg.style.borderRight = '1px solid #aaa'; + bg.style.top = 0; + bg.style.left = 0; + bg.style.width = '18px'; + bg.style.height = '100%'; + + bg = subMenu.appendChild(bg); + + RGraph.Registry.Set('chart.contextmenu.submenu', subMenu); + } + + + /** + * A function designed to be used in conjunction with thed context menu + * to allow people to get image versions of canvases. + * + * @param canvas Optionally you can pass in the canvas, which will be used + */ + RGraph.showPNG = function () + { + if (RGraph.isIE8()) { + alert('[RGRAPH PNG] Sorry, showing a PNG is not supported on MSIE8.'); + return; + } + + if (arguments[0] && arguments[0].id) { + var canvas = arguments[0]; + var event = arguments[1]; + + } else if (RGraph.Registry.Get('chart.contextmenu')) { + var canvas = RGraph.Registry.Get('chart.contextmenu').__canvas__; + + } else { + alert('[RGRAPH SHOWPNG] Could not find canvas!'); + } + + var obj = canvas.__object__; + + /** + * Create the gray background DIV to cover the page + */ + var bg = document.createElement('DIV'); + bg.id = '__rgraph_image_bg__'; + bg.style.position = 'fixed'; + bg.style.top = '-10px'; + bg.style.left = '-10px'; + bg.style.width = '5000px'; + bg.style.height = '5000px'; + bg.style.backgroundColor = 'rgb(204,204,204)'; + bg.style.opacity = 0; + document.body.appendChild(bg); + + + /** + * Create the div that the graph sits in + */ + var div = document.createElement('DIV'); + div.style.backgroundColor = 'white'; + div.style.opacity = 0; + div.style.border = '1px solid black'; + div.style.position = 'fixed'; + div.style.top = '20%'; + div.style.width = canvas.width + 'px'; + div.style.height = canvas.height + 35 + 'px'; + div.style.left = (document.body.clientWidth / 2) - (canvas.width / 2) + 'px'; + div.style.padding = '5px'; + + div.style.borderRadius = '10px'; + div.style.MozBorderRadius = '10px'; + div.style.WebkitBorderRadius = '10px'; + + div.style.boxShadow = '0 0 15px rgba(96,96,96,0.5)'; + div.style.MozBoxShadow = '0 0 15px rgba(96,96,96,0.5)'; + div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px'; + + div.__canvas__ = canvas; + div.__object__ = obj; + div.id = '__rgraph_image_div__'; + document.body.appendChild(div); + + + /** + * Add the HTML text inputs + */ + div.innerHTML += '<div style="position: absolute; margin-left: 10px; top: ' + canvas.height + 'px; width: ' + (canvas.width - 50) + 'px; height: 25px"><span style="display: inline; display: inline-block; width: 65px; text-align: right">URL:</span><textarea style="float: right; overflow: hidden; height: 15px; width: ' + (canvas.width - obj.gutterLeft - obj.gutterRight - 80) + 'px" onclick="this.select()" readonly="readonly" id="__rgraph_dataurl__">' + canvas.toDataURL() + '</textarea></div>'; + div.innerHTML += '<div style="position: absolute; top: ' + (canvas.height + 25) + 'px; left: ' + (obj.gutterLeft - 65 + (canvas.width / 2)) + 'px; width: ' + (canvas.width - obj.gutterRight) + 'px; font-size: 65%">A link using the URL: <a href="' + canvas.toDataURL() + '">View</a></div>' + + + + /** + * Create the image rendition of the graph + */ + var img = document.createElement('IMG'); + RGraph.Registry.Set('chart.png', img); + img.__canvas__ = canvas; + img.__object__ = obj; + img.id = '__rgraph_image_img__'; + img.className = 'RGraph_png'; + + img.src = canvas.toDataURL(); + + div.appendChild(img); + + setTimeout(function () {document.getElementById("__rgraph_dataurl__").select();}, 50); + + window.addEventListener('resize', function (e){var img = RGraph.Registry.Get('chart.png');img.style.left = (document.body.clientWidth / 2) - (img.width / 2) + 'px';}, false); + + bg.onclick = function (e) + { + var div = document.getElementById("__rgraph_image_div__"); + var bg = document.getElementById("__rgraph_image_bg__"); + + if (div) { + div.style.opacity = 0; + + div.parentNode.removeChild(div); + + div.id = ''; + div.style.display = 'none'; + div = null; + } + + if (bg) { + bg.style.opacity = 0; + + bg.id = ''; + bg.style.display = 'none'; + bg = null; + } + } + + window.addEventListener('resize', function (e) {bg.onclick(e);}, false) + + /** + * This sets the image as a global variable, circumventing repeated calls to document.getElementById() + */ + __rgraph_image_bg__ = bg; + __rgraph_image_div__ = div; + + + setTimeout('__rgraph_image_div__.style.opacity = 0.2', 50); + setTimeout('__rgraph_image_div__.style.opacity = 0.4', 100); + setTimeout('__rgraph_image_div__.style.opacity = 0.6', 150); + setTimeout('__rgraph_image_div__.style.opacity = 0.8', 200); + setTimeout('__rgraph_image_div__.style.opacity = 1', 250); + + setTimeout('__rgraph_image_bg__.style.opacity = 0.1', 50); + setTimeout('__rgraph_image_bg__.style.opacity = 0.2', 100); + setTimeout('__rgraph_image_bg__.style.opacity = 0.3', 150); + setTimeout('__rgraph_image_bg__.style.opacity = 0.4', 200); + setTimeout('__rgraph_image_bg__.style.opacity = 0.5', 250); + + + + img.onclick = function (e) + { + if (e.stopPropagation) e.stopPropagation(); + else event.cancelBubble = true; + } + + if (event && event.stopPropagation) { + event.stopPropagation(); + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.core.js b/schall/static/RGraph/libraries/RGraph.common.core.js new file mode 100644 index 0000000..990f0ca --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.core.js @@ -0,0 +1,2991 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + /** + * Initialise the various objects + */ + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + + RGraph.Registry = {}; + RGraph.Registry.store = []; + RGraph.Registry.store['chart.event.handlers'] = []; + RGraph.background = {}; + RGraph.objects = []; + RGraph.Resizing = {}; + RGraph.events = []; + + + + /** + * Returns five values which are used as a nice scale + * + * @param max int The maximum value of the graph + * @param obj object The graph object + * @return array An appropriate scale + */ + RGraph.getScale = function (max, obj) + { + /** + * Special case for 0 + */ + if (max == 0) { + return ['0.2', '0.4', '0.6', '0.8', '1.0']; + } + + var original_max = max; + + /** + * Manually do decimals + */ + if (max <= 1) { + if (max > 0.5) { + return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)]; + + } else if (max >= 0.1) { + return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5]; + + } else { + + var tmp = max; + var exp = 0; + + while (tmp < 1.01) { + exp += 1; + tmp *= 10; + } + + var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp]; + + + if (max <= ('5e-' + exp)) { + ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp]; + } + + return ret; + } + } + + // Take off any decimals + if (String(max).indexOf('.') > 0) { + max = String(max).replace(/\.\d+$/, ''); + } + + var interval = Math.pow(10, Number(String(Number(max)).length - 1)); + var topValue = interval; + + while (topValue < max) { + topValue += (interval / 2); + } + + // Handles cases where the max is (for example) 50.5 + if (Number(original_max) > Number(topValue)) { + topValue += (interval / 2); + } + + // Custom if the max is greater than 5 and less than 10 + if (max < 10) { + topValue = (Number(original_max) <= 5 ? 5 : 10); + } + + /** + * Added 02/11/2010 to create "nicer" scales + */ + if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) { + topValue = 10 * interval; + } + + return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue]; + } + + + /** + * Returns the maximum numeric value which is in an array + * + * @param array arr The array (can also be a number, in which case it's returned as-is) + * @param int Whether to ignore signs (ie negative/positive) + * @return int The maximum value in the array + */ + RGraph.array_max = function (arr) + { + var max = null; + + if (typeof(arr) == 'number') { + return arr; + } + + for (var i=0; i<arr.length; ++i) { + if (typeof(arr[i]) == 'number') { + + var val = arguments[1] ? Math.abs(arr[i]) : arr[i]; + + if (typeof(max) == 'number') { + max = Math.max(max, val); + } else { + max = val; + } + } + } + + return max; + } + + + /** + * Returns the maximum value which is in an array + * + * @param array arr The array + * @param int len The length to pad the array to + * @param mixed The value to use to pad the array (optional) + */ + RGraph.array_pad = function (arr, len) + { + if (arr.length < len) { + var val = arguments[2] ? arguments[2] : null; + + for (var i=arr.length; i<len; ++i) { + arr[i] = val; + } + } + + return arr; + } + + + /** + * An array sum function + * + * @param array arr The array to calculate the total of + * @return int The summed total of the arrays elements + */ + RGraph.array_sum = function (arr) + { + // Allow integers + if (typeof(arr) == 'number') { + return arr; + } + + var i, sum; + var len = arr.length; + + for(i=0,sum=0;i<len;sum+=arr[i++]); + return sum; + } + + + + /** + * A simple is_array() function + * + * @param mixed obj The object you want to check + * @return bool Whether the object is an array or not + */ + RGraph.is_array = function (obj) + { + return obj != null && obj.constructor.toString().indexOf('Array') != -1; + } + + + /** + * Converts degrees to radians + * + * @param int degrees The number of degrees + * @return float The number of radians + */ + RGraph.degrees2Radians = function (degrees) + { + return degrees * (Math.PI / 180); + } + + + /** + * This function draws an angled line. The angle is cosidered to be clockwise + * + * @param obj ctxt The context object + * @param int x The X position + * @param int y The Y position + * @param int angle The angle in RADIANS + * @param int length The length of the line + */ + RGraph.lineByAngle = function (context, x, y, angle, length) + { + context.arc(x, y, length, angle, angle, false); + context.lineTo(x, y); + context.arc(x, y, length, angle, angle, false); + } + + + /** + * This is a useful function which is basically a shortcut for drawing left, right, top and bottom alligned text. + * + * @param object context The context + * @param string font The font + * @param int size The size of the text + * @param int x The X coordinate + * @param int y The Y coordinate + * @param string text The text to draw + * @parm string The vertical alignment. Can be null. "center" gives center aligned text, "top" gives top aligned text. + * Anything else produces bottom aligned text. Default is bottom. + * @param string The horizontal alignment. Can be null. "center" gives center aligned text, "right" gives right aligned text. + * Anything else produces left aligned text. Default is left. + * @param bool Whether to show a bounding box around the text. Defaults not to + * @param int The angle that the text should be rotate at (IN DEGREES) + * @param string Background color for the text + * @param bool Whether the text is bold or not + * @param bool Whether the bounding box has a placement indicator + */ + RGraph.Text = function (context, font, size, x, y, text) + { + /** + * This calls the text function recursively to accommodate multi-line text + */ + if (typeof(text) == 'string' && text.match(/\r\n/)) { + + var arr = text.split('\r\n'); + + text = arr[0]; + arr = RGraph.array_shift(arr); + + var nextline = arr.join('\r\n') + + RGraph.Text(context, font, size, arguments[9] == -90 ? (x + (size * 1.5)) : x, y + (size * 1.5), nextline, arguments[6] ? arguments[6] : null, 'center', arguments[8], arguments[9], arguments[10], arguments[11], arguments[12]); + } + + + // Accommodate MSIE + if (RGraph.isIE8()) { + y += 2; + } + + + context.font = (arguments[11] ? 'Bold ': '') + size + 'pt ' + font; + + var i; + var origX = x; + var origY = y; + var originalFillStyle = context.fillStyle; + var originalLineWidth = context.lineWidth; + + // Need these now the angle can be specified, ie defaults for the former two args + if (typeof(arguments[6]) == null) arguments[6] = 'bottom'; // Vertical alignment. Default to bottom/baseline + if (typeof(arguments[7]) == null) arguments[7] = 'left'; // Horizontal alignment. Default to left + if (typeof(arguments[8]) == null) arguments[8] = null; // Show a bounding box. Useful for positioning during development. Defaults to false + if (typeof(arguments[9]) == null) arguments[9] = 0; // Angle (IN DEGREES) that the text should be drawn at. 0 is middle right, and it goes clockwise + if (typeof(arguments[12]) == null) arguments[12] = true; // Whether the bounding box has the placement indicator + + // The alignment is recorded here for purposes of Opera compatibility + if (navigator.userAgent.indexOf('Opera') != -1) { + context.canvas.__rgraph_valign__ = arguments[6]; + context.canvas.__rgraph_halign__ = arguments[7]; + } + + // First, translate to x/y coords + context.save(); + + context.canvas.__rgraph_originalx__ = x; + context.canvas.__rgraph_originaly__ = y; + + context.translate(x, y); + x = 0; + y = 0; + + // Rotate the canvas if need be + if (arguments[9]) { + context.rotate(arguments[9] / 57.3); + } + + // Vertical alignment - defaults to bottom + if (arguments[6]) { + var vAlign = arguments[6]; + + if (vAlign == 'center') { + context.translate(0, size / 2); + } else if (vAlign == 'top') { + context.translate(0, size); + } + } + + + // Hoeizontal alignment - defaults to left + if (arguments[7]) { + var hAlign = arguments[7]; + var width = context.measureText(text).width; + + if (hAlign) { + if (hAlign == 'center') { + context.translate(-1 * (width / 2), 0) + } else if (hAlign == 'right') { + context.translate(-1 * width, 0) + } + } + } + + + context.fillStyle = originalFillStyle; + + /** + * Draw a bounding box if requested + */ + context.save(); + context.fillText(text,0,0); + context.lineWidth = 0.5; + + if (arguments[8]) { + + var width = context.measureText(text).width; + var ieOffset = RGraph.isIE8() ? 2 : 0; + + context.translate(x, y); + context.strokeRect(0 - 3, 0 - 3 - size - ieOffset, width + 6, 0 + size + 6); + + /** + * If requested, draw a background for the text + */ + if (arguments[10]) { + + var offset = 3; + var ieOffset = RGraph.isIE8() ? 2 : 0; + var width = context.measureText(text).width + + //context.strokeStyle = 'gray'; + context.fillStyle = arguments[10]; + context.fillRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset)); + //context.strokeRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset)); + } + + /** + * Do the actual drawing of the text + */ + context.fillStyle = originalFillStyle; + context.fillText(text,0,0); + + if (arguments[12]) { + context.fillRect( + arguments[7] == 'left' ? 0 : (arguments[7] == 'center' ? width / 2 : width ) - 2, + arguments[6] == 'bottom' ? 0 : (arguments[6] == 'center' ? (0 - size) / 2 : 0 - size) - 2, + 4, + 4 + ); + } + } + context.restore(); + + // Reset the lineWidth + context.lineWidth = originalLineWidth; + + context.restore(); + } + + + /** + * Clears the canvas by setting the width. You can specify a colour if you wish. + * + * @param object canvas The canvas to clear + */ + RGraph.Clear = function (canvas) + { + var context = canvas.getContext('2d'); + var color = arguments[1]; + + if (RGraph.isIE8() && !color) { + color = 'white'; + } + + /** + * Can now clear the canvas back to fully transparent + */ + if (!color || (color && color == 'transparent')) { + + context.clearRect(0,0,canvas.width, canvas.height); + // context.fillStyle = 'rgba(0,0,0,0)'; + // context.globalCompositeOperation = 'source-in'; + // context = canvas.getContext('2d'); + //context.beginPath(); + //context.fillRect(-1000,-1000,canvas.width + 2000,canvas.height + 2000); + //context.fill(); + + // Reset the globalCompositeOperation + context.globalCompositeOperation = 'source-over'; + + } else { + + context.fillStyle = color; + context = canvas.getContext('2d'); + context.beginPath(); + + if (RGraph.isIE8()) { + context.fillRect(0,0,canvas.width,canvas.height); + } else { + context.fillRect(-10,-10,canvas.width + 20,canvas.height + 20); + } + + context.fill(); + } + + // Don't do this as it also clears any translation that may have occurred + //canvas.width = canvas.width; + + if (RGraph.ClearAnnotations) { + RGraph.ClearAnnotations(canvas.id); + } + + RGraph.FireCustomEvent(canvas.__object__, 'onclear'); + } + + + /** + * Draws the title of the graph + * + * @param object canvas The canvas object + * @param string text The title to write + * @param integer gutter The size of the gutter + * @param integer The center X point (optional - if not given it will be generated from the canvas width) + * @param integer Size of the text. If not given it will be 14 + */ + RGraph.DrawTitle = function (canvas, text, gutterTop) + { + var obj = canvas.__object__; + var context = canvas.getContext('2d'); + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + var size = arguments[4] ? arguments[4] : 12; + var bold = obj.Get('chart.title.bold'); + var centerx = (arguments[3] ? arguments[3] : ((obj.canvas.width - gutterLeft - gutterRight) / 2) + gutterLeft); + var keypos = obj.Get('chart.key.position'); + var vpos = obj.Get('chart.title.vpos'); + var hpos = obj.Get('chart.title.hpos'); + var bgcolor = obj.Get('chart.title.background'); + + // Account for 3D effect by faking the key position + if (obj.type == 'bar' && obj.Get('chart.variant') == '3d') { + keypos = 'gutter'; + } + + context.beginPath(); + context.fillStyle = obj.Get('chart.text.color') ? obj.Get('chart.text.color') : 'black'; + + /** + * Vertically center the text if the key is not present + */ + if (keypos && keypos != 'gutter') { + var vCenter = 'center'; + + } else if (!keypos) { + var vCenter = 'center'; + + } else { + var vCenter = 'bottom'; + } + + // if chart.title.vpos does not equal 0.5, use that + if (typeof(obj.Get('chart.title.vpos')) == 'number') { + vpos = obj.Get('chart.title.vpos') * gutterTop; + + if (obj.Get('chart.xaxispos') == 'top') { + vpos = obj.Get('chart.title.vpos') * gutterBottom + gutterTop + (obj.canvas.height - gutterTop - gutterBottom); + } + } else { + vpos = gutterTop - size - 5; + + if (obj.Get('chart.xaxispos') == 'top') { + vpos = obj.canvas.height - gutterBottom + size + 5; + } + } + + // if chart.title.hpos is a number, use that. It's multiplied with the (entire) canvas width + if (typeof(hpos) == 'number') { + centerx = hpos * canvas.width; + } + + // Set the colour + if (typeof(obj.Get('chart.title.color') != null)) { + var oldColor = context.fillStyle + var newColor = obj.Get('chart.title.color') + context.fillStyle = newColor ? newColor : 'black'; + } + + /** + * Default font is Verdana + */ + var font = obj.Get('chart.text.font'); + + /** + * Override the default font with chart.title.font + */ + if (typeof(obj.Get('chart.title.font')) == 'string') { + font = obj.Get('chart.title.font'); + } + + /** + * Draw the title itself + */ + RGraph.Text(context, font, size, centerx, vpos, text, vCenter, 'center', bgcolor != null, null, bgcolor, bold); + + // Reset the fill colour + context.fillStyle = oldColor; + } + + + /** + * This function returns the mouse position in relation to the canvas + * + * @param object e The event object. + */ + RGraph.getMouseXY = function (e) + { + var obj = (RGraph.isIE8() ? event.srcElement : e.target); + var x; + var y; + + if (RGraph.isIE8()) e = event; + + // Browser with offsetX and offsetY + if (typeof(e.offsetX) == 'number' && typeof(e.offsetY) == 'number') { + x = e.offsetX; + y = e.offsetY; + + // FF and other + } else { + x = 0; + y = 0; + + while (obj != document.body && obj) { + x += obj.offsetLeft; + y += obj.offsetTop; + + obj = obj.offsetParent; + } + + x = e.pageX - x; + y = e.pageY - y; + } + + return [x, y]; + } + + + /** + * This function returns a two element array of the canvas x/y position in + * relation to the page + * + * @param object canvas + */ + RGraph.getCanvasXY = function (canvas) + { + var x = 0; + var y = 0; + var obj = canvas; + + do { + + x += obj.offsetLeft; + y += obj.offsetTop; + + obj = obj.offsetParent; + + } while (obj && obj.tagName.toLowerCase() != 'body'); + + return [x, y]; + } + + + /** + * Registers a graph object (used when the canvas is redrawn) + * + * @param object obj The object to be registered + */ + RGraph.Register = function (obj) + { + var key = obj.id + '_' + obj.type; + + RGraph.objects[key] = obj; + } + + + /** + * Causes all registered objects to be redrawn + * + * @param string An optional string indicating which canvas is not to be redrawn + * @param string An optional color to use to clear the canvas + */ + RGraph.Redraw = function () + { + for (i in RGraph.objects) { + // TODO FIXME Maybe include more intense checking for whether the object is an RGraph object, eg obj.isRGraph == true ...? + if ( + typeof(i) == 'string' + && typeof(RGraph.objects[i]) == 'object' + && typeof(RGraph.objects[i].type) == 'string' + && RGraph.objects[i].isRGraph) { + + if (!arguments[0] || arguments[0] != RGraph.objects[i].id) { + RGraph.Clear(RGraph.objects[i].canvas, arguments[1] ? arguments[1] : null); + RGraph.objects[i].Draw(); + } + } + } + } + + + /** + * Loosly mimicks the PHP function print_r(); + */ + RGraph.pr = function (obj) + { + var str = ''; + var indent = (arguments[2] ? arguments[2] : ''); + + switch (typeof(obj)) { + case 'number': + if (indent == '') { + str+= 'Number: ' + } + str += String(obj); + break; + + case 'string': + if (indent == '') { + str+= 'String (' + obj.length + '):' + } + str += '"' + String(obj) + '"'; + break; + + case 'object': + // In case of null + if (obj == null) { + str += 'null'; + break; + } + + str += 'Object\n' + indent + '(\n'; + for (var i in obj) { + if (typeof(i) == 'string' || typeof(i) == 'number') { + str += indent + ' ' + i + ' => ' + RGraph.pr(obj[i], true, indent + ' ') + '\n'; + } + } + + var str = str + indent + ')'; + break; + + case 'function': + str += obj; + break; + + case 'boolean': + str += 'Boolean: ' + (obj ? 'true' : 'false'); + break; + } + + /** + * Finished, now either return if we're in a recursed call, or alert() + * if we're not. + */ + if (arguments[1]) { + return str; + } else { + alert(str); + } + } + + + /** + * The RGraph registry Set() function + * + * @param string name The name of the key + * @param mixed value The value to set + * @return mixed Returns the same value as you pass it + */ + RGraph.Registry.Set = function (name, value) + { + // Store the setting + RGraph.Registry.store[name] = value; + + // Don't really need to do this, but ho-hum + return value; + } + + + /** + * The RGraph registry Get() function + * + * @param string name The name of the particular setting to fetch + * @return mixed The value if exists, null otherwise + */ + RGraph.Registry.Get = function (name) + { + //return RGraph.Registry.store[name] == null ? null : RGraph.Registry.store[name]; + return RGraph.Registry.store[name]; + } + + + /** + * This function draws the background for the bar chart, line chart and scatter chart. + * + * @param object obj The graph object + */ + RGraph.background.Draw = function (obj) + { + var canvas = obj.canvas; + var context = obj.context; + var height = 0; + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + var variant = obj.Get('chart.variant'); + + context.fillStyle = obj.Get('chart.text.color'); + + // If it's a bar and 3D variant, translate + if (variant == '3d') { + context.save(); + context.translate(10, -5); + } + + // X axis title + if (typeof(obj.Get('chart.title.xaxis')) == 'string' && obj.Get('chart.title.xaxis').length) { + + var size = obj.Get('chart.text.size') + 2; + var font = obj.Get('chart.text.font'); + var bold = obj.Get('chart.title.xaxis.bold'); + + if (typeof(obj.Get('chart.title.xaxis.size')) == 'number') { + size = obj.Get('chart.title.xaxis.size'); + } + + if (typeof(obj.Get('chart.title.xaxis.font')) == 'string') { + font = obj.Get('chart.title.xaxis.font'); + } + + var hpos = ((obj.canvas.width - obj.gutterLeft - obj.gutterRight) / 2) + obj.gutterLeft; + var vpos = obj.canvas.height - obj.Get('chart.gutter.bottom') + 25; + + if (typeof(obj.Get('chart.title.xaxis.pos')) == 'number') { + vpos = obj.canvas.height - (gutterBottom * obj.Get('chart.title.xaxis.pos')); + } + + context.beginPath(); + RGraph.Text(context, + font, + size, + hpos, + vpos, + obj.Get('chart.title.xaxis'), + 'center', + 'center', + false, + false, + false, + bold); + context.fill(); + } + + // Y axis title + if (typeof(obj.Get('chart.title.yaxis')) == 'string' && obj.Get('chart.title.yaxis').length) { + + var size = obj.Get('chart.text.size') + 2; + var font = obj.Get('chart.text.font'); + var angle = 270; + var bold = obj.Get('chart.title.yaxis.bold'); + + if (typeof(obj.Get('chart.title.yaxis.pos')) == 'number') { + var yaxis_title_pos = obj.Get('chart.title.yaxis.pos') * obj.Get('chart.gutter.left'); + } else { + var yaxis_title_pos = ((obj.Get('chart.gutter.left') - 25) / obj.Get('chart.gutter.left')) * obj.Get('chart.gutter.left'); + } + + if (typeof(obj.Get('chart.title.yaxis.size')) == 'number') { + size = obj.Get('chart.title.yaxis.size'); + } + + if (typeof(obj.Get('chart.title.yaxis.font')) == 'string') { + font = obj.Get('chart.title.yaxis.font'); + } + + if (obj.Get('chart.title.yaxis.align') == 'right' || obj.Get('chart.title.yaxis.position') == 'right') { + angle = 90; + yaxis_title_pos = obj.canvas.width - yaxis_title_pos; + } else { + yaxis_title_pos = yaxis_title_pos; + } + + + context.beginPath(); + RGraph.Text(context, + font, + size, + yaxis_title_pos, + ((obj.canvas.height - obj.gutterTop - obj.gutterBottom) / 2) + obj.gutterTop, + obj.Get('chart.title.yaxis'), + 'center', + 'center', + false, + angle, + false, + bold); + context.fill(); + } + + obj.context.beginPath(); + + // Draw the horizontal bars + context.fillStyle = obj.Get('chart.background.barcolor1'); + height = (RGraph.GetHeight(obj) - gutterBottom); + + for (var i=gutterTop; i < height ; i+=80) { + obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, Math.min(40, RGraph.GetHeight(obj) - gutterBottom - i) ); + } + + context.fillStyle = obj.Get('chart.background.barcolor2'); + height = (RGraph.GetHeight(obj) - gutterBottom); + + for (var i= (40 + gutterTop); i < height; i+=80) { + obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, i + 40 > (RGraph.GetHeight(obj) - gutterBottom) ? RGraph.GetHeight(obj) - (gutterBottom + i) : 40); + } + + context.stroke(); + + + // Draw the background grid + if (obj.Get('chart.background.grid')) { + + // If autofit is specified, use the .numhlines and .numvlines along with the width to work + // out the hsize and vsize + if (obj.Get('chart.background.grid.autofit')) { + + /** + * Align the grid to the tickmarks + */ + if (obj.Get('chart.background.grid.autofit.align')) { + // Align the horizontal lines + obj.Set('chart.background.grid.autofit.numhlines', obj.Get('chart.ylabels.count')); + + // Align the vertical lines for the line + if (obj.type == 'line') { + if (obj.Get('chart.labels') && obj.Get('chart.labels').length) { + obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length - 1); + } else { + obj.Set('chart.background.grid.autofit.numvlines', obj.data[0].length - 1); + } + + // Align the vertical lines for the bar + } else if (obj.type == 'bar' && obj.Get('chart.labels') && obj.Get('chart.labels').length) { + obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length); + } + } + + var vsize = ((RGraph.GetWidth(obj) - gutterLeft - gutterRight)) / obj.Get('chart.background.grid.autofit.numvlines'); + var hsize = (RGraph.GetHeight(obj) - gutterTop - gutterBottom) / obj.Get('chart.background.grid.autofit.numhlines'); + + obj.Set('chart.background.grid.vsize', vsize); + obj.Set('chart.background.grid.hsize', hsize); + } + + context.beginPath(); + context.lineWidth = obj.Get('chart.background.grid.width') ? obj.Get('chart.background.grid.width') : 1; + context.strokeStyle = obj.Get('chart.background.grid.color'); + + // Draw the horizontal lines + if (obj.Get('chart.background.grid.hlines')) { + height = (RGraph.GetHeight(obj) - gutterBottom) + for (y=gutterTop; y<height; y+=obj.Get('chart.background.grid.hsize')) { + context.moveTo(gutterLeft, y); + context.lineTo(RGraph.GetWidth(obj) - gutterRight, y); + } + } + + if (obj.Get('chart.background.grid.vlines')) { + // Draw the vertical lines + var width = (RGraph.GetWidth(obj) - gutterRight) + for (x=gutterLeft; x<=width; x+=obj.Get('chart.background.grid.vsize')) { + context.moveTo(x, gutterTop); + context.lineTo(x, RGraph.GetHeight(obj) - gutterBottom); + } + } + + if (obj.Get('chart.background.grid.border')) { + // Make sure a rectangle, the same colour as the grid goes around the graph + context.strokeStyle = obj.Get('chart.background.grid.color'); + context.strokeRect(gutterLeft, gutterTop, RGraph.GetWidth(obj) - gutterLeft - gutterRight, RGraph.GetHeight(obj) - gutterTop - gutterBottom); + } + } + + context.stroke(); + + // If it's a bar and 3D variant, translate + if (variant == '3d') { + context.restore(); + } + + // Draw the title if one is set + if ( typeof(obj.Get('chart.title')) == 'string') { + + if (obj.type == 'gantt') { + gutterTop -= 10; + } + + RGraph.DrawTitle(canvas, + obj.Get('chart.title'), + gutterTop, + null, + obj.Get('chart.title.size') ? obj.Get('chart.title.size') : obj.Get('chart.text.size') + 2); + } + + context.stroke(); + } + + + /** + * Returns the day number for a particular date. Eg 1st February would be 32 + * + * @param object obj A date object + * @return int The day number of the given date + */ + RGraph.GetDays = function (obj) + { + var year = obj.getFullYear(); + var days = obj.getDate(); + var month = obj.getMonth(); + + if (month == 0) return days; + if (month >= 1) days += 31; + if (month >= 2) days += 28; + + // Leap years. Crude, but if this code is still being used + // when it stops working, then you have my permission to shoot + // me. Oh, you won't be able to - I'll be dead... + if (year >= 2008 && year % 4 == 0) days += 1; + + if (month >= 3) days += 31; + if (month >= 4) days += 30; + if (month >= 5) days += 31; + if (month >= 6) days += 30; + if (month >= 7) days += 31; + if (month >= 8) days += 31; + if (month >= 9) days += 30; + if (month >= 10) days += 31; + if (month >= 11) days += 30; + + return days; + } + + + + + + + + + + + + + + + + /** + * Draws the graph key (used by various graphs) + * + * @param object obj The graph object + * @param array key An array of the texts to be listed in the key + * @param colors An array of the colors to be used + */ + RGraph.DrawKey = function (obj, key, colors) + { + var canvas = obj.canvas; + var context = obj.context; + context.lineWidth = 1; + + context.beginPath(); + + /** + * Key positioned in the gutter + */ + var keypos = obj.Get('chart.key.position'); + var textsize = obj.Get('chart.text.size'); + + /** + * Change the older chart.key.vpos to chart.key.position.y + */ + if (typeof(obj.Get('chart.key.vpos')) == 'number') { + obj.Set('chart.key.position.y', obj.Get('chart.key.vpos') * this.Get('chart.gutter.top') ); + } + + /** + * Account for null values in the key + */ + var key_non_null = []; + var colors_non_null = []; + for (var i=0; i<key.length; ++i) { + if (key[i] != null) { + colors_non_null.push(colors[i]); + key_non_null.push(key[i]); + } + } + + key = key_non_null; + colors = colors_non_null; + + + + if (keypos && keypos == 'gutter') { + + RGraph.DrawKey_gutter(obj, key, colors); + + + /** + * In-graph style key + */ + } else if (keypos && keypos == 'graph') { + + RGraph.DrawKey_graph(obj, key, colors); + + } else { + alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos); + } + } + + + + + + /** + * This does the actual drawing of the key when it's in the graph + * + * @param object obj The graph object + * @param array key The key items to draw + * @param array colors An aray of colors that the key will use + */ + RGraph.DrawKey_graph = function (obj, key, colors) + { + var canvas = obj.canvas; + var context = obj.context; + var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size'); + var text_font = obj.Get('chart.text.font'); + + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + + var hpos = obj.Get('chart.yaxispos') == 'right' ? gutterLeft + 10 : RGraph.GetWidth(obj) - gutterRight - 10; + var vpos = gutterTop + 10; + var title = obj.Get('chart.title'); + var blob_size = text_size; // The blob of color + var hmargin = 8; // This is the size of the gaps between the blob of color and the text + var vmargin = 4; // This is the vertical margin of the key + var fillstyle = obj.Get('chart.key.background'); + var strokestyle = '#333'; + var height = 0; + var width = 0; + + + obj.coordsKey = []; + + + // Need to set this so that measuring the text works out OK + context.font = text_size + 'pt ' + obj.Get('chart.text.font'); + + // Work out the longest bit of text + for (i=0; i<key.length; ++i) { + width = Math.max(width, context.measureText(key[i]).width); + } + + width += 5; + width += blob_size; + width += 5; + width += 5; + width += 5; + + /** + * Now we know the width, we can move the key left more accurately + */ + if ( obj.Get('chart.yaxispos') == 'left' + || (obj.type == 'pie' && !obj.Get('chart.yaxispos')) + || (obj.type == 'hbar' && !obj.Get('chart.yaxispos')) + || (obj.type == 'hbar' && obj.Get('chart.yaxispos') == 'center') + || (obj.type == 'rscatter' && !obj.Get('chart.yaxispos')) + || (obj.type == 'radar' && !obj.Get('chart.yaxispos')) + || (obj.type == 'rose' && !obj.Get('chart.yaxispos')) + || (obj.type == 'funnel' && !obj.Get('chart.yaxispos')) + || (obj.type == 'vprogress' && !obj.Get('chart.yaxispos')) + || (obj.type == 'hprogress' && !obj.Get('chart.yaxispos')) + ) { + + hpos -= width; + } + + /** + * Horizontal alignment + */ + if (typeof(obj.Get('chart.key.halign')) == 'string') { + if (obj.Get('chart.key.halign') == 'left') { + hpos = gutterLeft + 10; + } else if (obj.Get('chart.key.halign') == 'right') { + hpos = RGraph.GetWidth(obj) - gutterRight - width; + } + } + + /** + * Specific location coordinates + */ + if (typeof(obj.Get('chart.key.position.x')) == 'number') { + hpos = obj.Get('chart.key.position.x'); + } + + if (typeof(obj.Get('chart.key.position.y')) == 'number') { + vpos = obj.Get('chart.key.position.y'); + } + + + // Stipulate the shadow for the key box + if (obj.Get('chart.key.shadow')) { + context.shadowColor = obj.Get('chart.key.shadow.color'); + context.shadowBlur = obj.Get('chart.key.shadow.blur'); + context.shadowOffsetX = obj.Get('chart.key.shadow.offsetx'); + context.shadowOffsetY = obj.Get('chart.key.shadow.offsety'); + } + + + + + // Draw the box that the key resides in + context.beginPath(); + context.fillStyle = obj.Get('chart.key.background'); + context.strokeStyle = 'black'; + + + if (arguments[3] != false) { + + context.lineWidth = obj.Get('chart.key.linewidth') ? obj.Get('chart.key.linewidth') : 1; + + // The older square rectangled key + if (obj.Get('chart.key.rounded') == true) { + context.beginPath(); + context.strokeStyle = strokestyle; + RGraph.strokedCurvyRect(context, hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key)),4); + + context.stroke(); + context.fill(); + + RGraph.NoShadow(obj); + + } else { + context.strokeRect(hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key))); + context.fillRect(hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key))); + } + } + + RGraph.NoShadow(obj); + + context.beginPath(); + + // Draw the labels given + for (var i=key.length - 1; i>=0; i--) { + + var j = Number(i) + 1; + + // Draw the blob of color + if (obj.Get('chart.key.color.shape') == 'circle') { + context.beginPath(); + context.strokeStyle = 'rgba(0,0,0,0)'; + context.fillStyle = colors[i]; + context.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0); + context.fill(); + + } else if (obj.Get('chart.key.color.shape') == 'line') { + context.beginPath(); + context.strokeStyle = colors[i]; + context.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2)); + context.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2)); + context.stroke(); + + } else { + context.fillStyle = colors[i]; + context.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1); + } + + context.beginPath(); + + context.fillStyle = 'black'; + + RGraph.Text(context, + text_font, + text_size, + hpos + blob_size + 5 + 5, + vpos + (5 * j) + (text_size * j), + key[i]); + + if (obj.Get('chart.key.interactive')) { + + var px = hpos + 5; + var py = vpos + (5 * j) + (text_size * j) - text_size; + var pw = width - 5 - 5 - 5; + var ph = text_size; + + + obj.coordsKey.push([px, py, pw, ph]); + } + + } + context.fill(); + + /** + * Install the interactivity event handler + */ + if (obj.Get('chart.key.interactive')) { + + RGraph.Register(obj); + + var key_mousemove = function (e) + { + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + for (var i=0; i<obj.coordsKey.length; ++i) { + + var px = obj.coordsKey[i][0]; + var py = obj.coordsKey[i][1]; + var pw = obj.coordsKey[i][2]; + var ph = obj.coordsKey[i][3]; + + if ( mouseX > (px-2) && mouseX < (px + pw + 2) && mouseY > (py - 2) && mouseY < (py + ph + 2) ) { + + // Necessary? + //var index = obj.coordsKey.length - i - 1; + + canvas.style.cursor = 'pointer'; + + + + return; + } + + canvas.style.cursor = 'default'; + + if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(canvas_onmousemove_func) == 'function') { + canvas_onmousemove_func(e); + } + } + } + canvas.addEventListener('mousemove', key_mousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', key_mousemove); + + + var key_click = function (e) + { + RGraph.Redraw(); + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + /** + * Hand over highlighting the pie chart key to another function + */ + if (obj.type == 'pie') { + return key_onclick_pie(e); + } + + RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); + + for (var i=0; i<obj.coordsKey.length; ++i) { + + var px = obj.coordsKey[i][0]; + var py = obj.coordsKey[i][1]; + var pw = obj.coordsKey[i][2]; + var ph = obj.coordsKey[i][3]; + + if ( mouseX > px && mouseX < (px + pw) && mouseY > py && mouseY < (py + ph) ) { + + /** + * Loop thru all objects. If they're objects with + * chart.key.interactive enabled, redraw them + */ + for (j in RGraph.objects) { + if (RGraph.objects[j] && RGraph.objects[j].Get && RGraph.objects[j].Get('chart.key.interactive')) { + + if (RGraph.objects[j].Get('chart.exploded')) { + RGraph.objects[j].Set('chart.exploded', []); + } + + RGraph.Clear(RGraph.objects[j].canvas); + RGraph.objects[j].Draw(); + } + } + var index = obj.coordsKey.length - i - 1; + + // HIGHLIGHT THE LINE HERE + context.beginPath(); + context.fillStyle = 'rgba(255,255,255,0.9)'; + context.fillRect(obj.Get('chart.gutter.left'),obj.Get('chart.gutter.top'),canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right'),canvas.height - obj.Get('chart.gutter.top') - obj.Get('chart.gutter.bottom')); + context.fill(); + + context.beginPath(); + context.strokeStyle = obj.Get('chart.colors')[index]; + context.lineWidth = obj.Get('chart.linewidth'); + if (obj.coords2 &&obj.coords2[index] &&obj.coords2[index].length) { + for (var j=0; j<obj.coords2[index].length; ++j) { + + var x = obj.coords2[index][j][0]; + var y = obj.coords2[index][j][1]; + + if (j == 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + } + } + context.stroke(); + + + context.lineWidth = 1; + context.beginPath(); + context.strokeStyle = 'black'; + context.fillStyle = 'white'; + + RGraph.SetShadow(obj, 'rgba(0,0,0,0.5)', 0,0,10); + + context.strokeRect(px - 2, py - 2, pw + 4, ph + 4); + context.fillRect(px - 2, py - 2, pw + 4, ph + 4); + + context.stroke(); + context.fill(); + + + RGraph.NoShadow(obj); + + + context.beginPath(); + context.fillStyle = obj.Get('chart.colors')[index]; + context.fillRect(px, py, blob_size, blob_size); + context.fill(); + + context.beginPath(); + context.fillStyle = obj.Get('chart.text.color'); + + RGraph.Text(context, + obj.Get('chart.text.font'), + obj.Get('chart.text.size'), + px + 5 + blob_size, + py + ph, + obj.Get('chart.key')[obj.Get('chart.key').length - i - 1] + ); + context.fill(); + + + canvas.style.cursor = 'pointer'; + + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + } + + canvas.style.cursor = 'default'; + } + } + canvas.addEventListener('click', key_click, false); + RGraph.AddEventListener(canvas.id, 'click', key_click); + + /** + * This function handles the Pie chart interactive key (the click event) + * + * @param object e The event object + */ + var key_onclick_pie = function (e) + { + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = e.target.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + //RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); + + for (var i=0; i<obj.coordsKey.length; ++i) { + + var px = obj.coordsKey[i][0]; + var py = obj.coordsKey[i][1]; + var pw = obj.coordsKey[i][2]; + var ph = obj.coordsKey[i][3]; + + if ( mouseX > (px - 2) && mouseX < (px + pw + 2) && mouseY > (py - 2) && mouseY < (py + ph + 2) ) { + + var index = obj.coordsKey.length - i - 1; + + + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + + + + // ========================================================================== + var highlight_key = function () + { + context.lineWidth = 1; + context.beginPath(); + context.strokeStyle = 'black'; + context.fillStyle = 'white'; + + RGraph.SetShadow(obj, 'rgba(0,0,0,0.5)', 0,0,10); + + context.strokeRect(px - 2, py - 2, pw + 4, ph + 4); + context.fillRect(px - 2, py - 2, pw + 4, ph + 4); + + context.stroke(); + context.fill(); + + + RGraph.NoShadow(obj); + + + context.beginPath(); + context.fillStyle = obj.Get('chart.colors')[index]; + context.fillRect(px, py, blob_size, blob_size); + context.fill(); + + context.beginPath(); + context.fillStyle = obj.Get('chart.text.color'); + + RGraph.Text(context, + obj.Get('chart.text.font'), + obj.Get('chart.text.size'), + px + 5 + blob_size, + py + ph, + obj.Get('chart.key')[obj.Get('chart.key').length - i - 1] + ); + context.fill(); + } + // ========================================================================== + + setTimeout(function (){obj.Get('chart.exploded')[index] = 2;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 20); + setTimeout(function (){obj.Get('chart.exploded')[index] = 4;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 40); + setTimeout(function (){obj.Get('chart.exploded')[index] = 6;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 60); + setTimeout(function (){obj.Get('chart.exploded')[index] = 8;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 80); + setTimeout(function (){obj.Get('chart.exploded')[index] = 10;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 100); + setTimeout(function (){obj.Get('chart.exploded')[index] = 12;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 120); + setTimeout(function (){obj.Get('chart.exploded')[index] = 14;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 140); + setTimeout(function (){obj.Get('chart.exploded')[index] = 16;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 160); + setTimeout(function (){obj.Get('chart.exploded')[index] = 18;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 180); + setTimeout(function (){obj.Get('chart.exploded')[index] = 20;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 200); + + /** + * This is here so that when calling the Redraw function the Pie chart + * is drawn unexploded + */ + setTimeout(function (){obj.Get('chart.exploded')[index] = 0;}, 250); + + return; + } else { + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + } + } + + //obj.Set('chart.exploded', []); + RGraph.Clear(obj.canvas); + obj.Draw(); + } + + /** + * The window onclick for the pie chart + */ + var key_interactive_click = function (e) + { + if (obj && obj.type == 'pie') { + obj.Set('chart.exploded', []); + } + RGraph.Clear(obj.canvas); + obj.Draw(); + } + window.addEventListener('click', key_interactive_click, false); + RGraph.AddEventListener('window_' + canvas.id, 'click', key_interactive_click); + } + } + + + + + + + /** + * This does the actual drawing of the key when it's in the gutter + * + * @param object obj The graph object + * @param array key The key items to draw + * @param array colors An aray of colors that the key will use + */ + RGraph.DrawKey_gutter = function (obj, key, colors) + { + var canvas = obj.canvas; + var context = obj.context; + var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size'); + var text_font = obj.Get('chart.text.font'); + + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + + var hpos = RGraph.GetWidth(obj) / 2; + var vpos = (gutterTop / 2) - 5; + var title = obj.Get('chart.title'); + var blob_size = text_size; // The blob of color + var hmargin = 8; // This is the size of the gaps between the blob of color and the text + var vmargin = 4; // This is the vertical margin of the key + var fillstyle = obj.Get('chart.key.background'); + var strokestyle = 'black'; + var length = 0; + + + + // Need to work out the length of the key first + context.font = text_size + 'pt ' + text_font; + for (i=0; i<key.length; ++i) { + length += hmargin; + length += blob_size; + length += hmargin; + length += context.measureText(key[i]).width; + } + length += hmargin; + + + + + /** + * Work out hpos since in the Pie it isn't necessarily dead center + */ + if (obj.type == 'pie') { + if (obj.Get('chart.align') == 'left') { + var hpos = obj.radius + gutterLeft; + + } else if (obj.Get('chart.align') == 'right') { + var hpos = obj.canvas.width - obj.radius - gutterRight; + + } else { + hpos = canvas.width / 2; + } + } + + + + + + /** + * This makes the key centered + */ + hpos -= (length / 2); + + + /** + * Override the horizontal/vertical positioning + */ + if (typeof(obj.Get('chart.key.position.x')) == 'number') { + hpos = obj.Get('chart.key.position.x'); + } + if (typeof(obj.Get('chart.key.position.y')) == 'number') { + vpos = obj.Get('chart.key.position.y'); + } + + + + /** + * Draw the box that the key sits in + */ + if (obj.Get('chart.key.position.gutter.boxed')) { + + if (obj.Get('chart.key.shadow')) { + context.shadowColor = obj.Get('chart.key.shadow.color'); + context.shadowBlur = obj.Get('chart.key.shadow.blur'); + context.shadowOffsetX = obj.Get('chart.key.shadow.offsetx'); + context.shadowOffsetY = obj.Get('chart.key.shadow.offsety'); + } + + + context.beginPath(); + context.fillStyle = fillstyle; + context.strokeStyle = strokestyle; + + if (obj.Get('chart.key.rounded')) { + RGraph.strokedCurvyRect(context, hpos, vpos - vmargin, length, text_size + vmargin + vmargin) + // Odd... RGraph.filledCurvyRect(context, hpos, vpos - vmargin, length, text_size + vmargin + vmargin); + } else { + context.strokeRect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin); + context.fillRect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin); + } + + context.stroke(); + context.fill(); + + + RGraph.NoShadow(obj); + } + + + /** + * Draw the blobs of color and the text + */ + for (var i=0, pos=hpos; i<key.length; ++i) { + pos += hmargin; + + // Draw the blob of color - line + if (obj.Get('chart.key.color.shape') =='line') { + + context.beginPath(); + context.strokeStyle = colors[i]; + context.moveTo(pos, vpos + (blob_size / 2)); + context.lineTo(pos + blob_size, vpos + (blob_size / 2)); + context.stroke(); + + // Circle + } else if (obj.Get('chart.key.color.shape') == 'circle') { + + context.beginPath(); + context.fillStyle = colors[i]; + context.moveTo(pos, vpos + (blob_size / 2)); + context.arc(pos + (blob_size / 2), vpos + (blob_size / 2), (blob_size / 2), 0, 6.28, 0); + context.fill(); + + + } else { + + context.beginPath(); + context.fillStyle = colors[i]; + context.fillRect(pos, vpos, blob_size, blob_size); + context.fill(); + } + + pos += blob_size; + + pos += hmargin; + + context.beginPath(); + context.fillStyle = 'black'; + RGraph.Text(context, text_font, text_size, pos, vpos + text_size - 1, key[i]); + context.fill(); + pos += context.measureText(key[i]).width; + } + } + + + /** + * Returns the key length, but accounts for null values + * + * @param array key The key elements + */ + RGraph.getKeyLength = function (key) + { + var len = 0; + + for (var i=0; i<key.length; ++i) { + if (key[i] != null) { + ++len; + } + } + + return len; + } + + + + + + + /** + * A shortcut for RGraph.pr() + */ + function pd(variable) + { + RGraph.pr(variable); + } + + function p(variable) + { + RGraph.pr(variable); + } + + /** + * A shortcut for console.log - as used by Firebug and Chromes console + */ + function cl (variable) + { + return console.log(variable); + } + + + /** + * Makes a clone of an object + * + * @param obj val The object to clone + */ + RGraph.array_clone = function (obj) + { + if(obj == null || typeof(obj) != 'object') { + return obj; + } + + var temp = []; + + for (var i=0;i<obj.length; ++i) { + + if (typeof(obj[i]) == 'number') { + temp[i] = (function (arg) {return Number(arg);})(obj[i]); + } else if (typeof(obj[i]) == 'string') { + temp[i] = (function (arg) {return String(arg);})(obj[i]); + } else { + temp[i] = RGraph.array_clone(obj[i]); + } + } + + return temp; + } + + + /** + * This function reverses an array + */ + RGraph.array_reverse = function (arr) + { + var newarr = []; + + for (var i=arr.length - 1; i>=0; i--) { + newarr.push(arr[i]); + } + + return newarr; + } + + + /** + * Formats a number with thousand seperators so it's easier to read + * + * @param integer num The number to format + * @param string The (optional) string to prepend to the string + * @param string The (optional) string to ap + * pend to the string + * @return string The formatted number + */ + RGraph.number_format = function (obj, num) + { + var i; + var prepend = arguments[2] ? String(arguments[2]) : ''; + var append = arguments[3] ? String(arguments[3]) : ''; + var output = ''; + var decimal = ''; + var decimal_seperator = obj.Get('chart.scale.point') ? obj.Get('chart.scale.point') : '.'; + var thousand_seperator = obj.Get('chart.scale.thousand') ? obj.Get('chart.scale.thousand') : ','; + RegExp.$1 = ''; + var i,j; + +if (typeof(obj.Get('chart.scale.formatter')) == 'function') { + return obj.Get('chart.scale.formatter')(obj, num); +} + + // Ignore the preformatted version of "1e-2" + if (String(num).indexOf('e') > 0) { + return String(prepend + String(num) + append); + } + + // We need then number as a string + num = String(num); + + // Take off the decimal part - we re-append it later + if (num.indexOf('.') > 0) { + num = num.replace(/\.(.*)/, ''); + decimal = RegExp.$1; + } + + // Thousand seperator + //var seperator = arguments[1] ? String(arguments[1]) : ','; + var seperator = thousand_seperator; + + /** + * Work backwards adding the thousand seperators + */ + var foundPoint; + for (i=(num.length - 1),j=0; i>=0; j++,i--) { + var character = num.charAt(i); + + if ( j % 3 == 0 && j != 0) { + output += seperator; + } + + /** + * Build the output + */ + output += character; + } + + /** + * Now need to reverse the string + */ + var rev = output; + output = ''; + for (i=(rev.length - 1); i>=0; i--) { + output += rev.charAt(i); + } + + // Tidy up + output = output.replace(/^-,/, '-'); + + // Reappend the decimal + if (decimal.length) { + output = output + decimal_seperator + decimal; + decimal = ''; + RegExp.$1 = ''; + } + + // Minor bugette + if (output.charAt(0) == '-') { + output = output.replace(/-/, ''); + prepend = '-' + prepend; + } + + return prepend + output + append; + } + + + /** + * Draws horizontal coloured bars on something like the bar, line or scatter + */ + RGraph.DrawBars = function (obj) + { + var hbars = obj.Get('chart.background.hbars'); + + /** + * Draws a horizontal bar + */ + obj.context.beginPath(); + + for (i=0; i<hbars.length; ++i) { + + // If null is specified as the "height", set it to the upper max value + if (hbars[i][1] == null) { + hbars[i][1] = obj.max; + + // If the first index plus the second index is greater than the max value, adjust accordingly + } else if (hbars[i][0] + hbars[i][1] > obj.max) { + hbars[i][1] = obj.max - hbars[i][0]; + } + + + // If height is negative, and the abs() value is greater than .max, use a negative max instead + if (Math.abs(hbars[i][1]) > obj.max) { + hbars[i][1] = -1 * obj.max; + } + + + // If start point is greater than max, change it to max + if (Math.abs(hbars[i][0]) > obj.max) { + hbars[i][0] = obj.max; + } + + // If start point plus height is less than negative max, use the negative max plus the start point + if (hbars[i][0] + hbars[i][1] < (-1 * obj.max) ) { + hbars[i][1] = -1 * (obj.max + hbars[i][0]); + } + + // If the X axis is at the bottom, and a negative max is given, warn the user + if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) { + alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center'); + } + + var ystart = (obj.grapharea - ((hbars[i][0] / obj.max) * obj.grapharea)); + var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / obj.max) * obj.grapharea; + + // Account for the X axis being in the center + if (obj.Get('chart.xaxispos') == 'center') { + ystart /= 2; + height /= 2; + } + + ystart += obj.Get('chart.gutter.top') + + var x = obj.Get('chart.gutter.left'); + var y = ystart - height; + var w = obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right'); + var h = height; + + // Accommodate Opera :-/ + if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) { + h *= -1; + y = y - h; + } + + /** + * Account for X axis at the top + */ + if (obj.Get('chart.xaxispos') == 'top') { + y = obj.canvas.height - y; + h *= -1; + } + + obj.context.fillStyle = hbars[i][2]; + obj.context.fillRect(x, y, w, h); + } + + obj.context.fill(); + } + + + /** + * Draws in-graph labels. + * + * @param object obj The graph object + */ + RGraph.DrawInGraphLabels = function (obj) + { + var canvas = obj.canvas; + var context = obj.context; + var labels = obj.Get('chart.labels.ingraph'); + var labels_processed = []; + + // Defaults + var fgcolor = 'black'; + var bgcolor = 'white'; + var direction = 1; + + if (!labels) { + return; + } + + /** + * Preprocess the labels array. Numbers are expanded + */ + for (var i=0; i<labels.length; ++i) { + if (typeof(labels[i]) == 'number') { + for (var j=0; j<labels[i]; ++j) { + labels_processed.push(null); + } + } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') { + labels_processed.push(labels[i]); + + } else { + labels_processed.push(''); + } + } + + /** + * Turn off any shadow + */ + RGraph.NoShadow(obj); + + if (labels_processed && labels_processed.length > 0) { + + for (var i=0; i<labels_processed.length; ++i) { + if (labels_processed[i]) { + var coords = obj.coords[i]; + + if (coords && coords.length > 0) { + var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]); + var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]); + var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25; + + + context.beginPath(); + context.fillStyle = 'black'; + context.strokeStyle = 'black'; + + + if (obj.type == 'bar') { + + /** + * X axis at the top + */ + if (obj.Get('chart.xaxispos') == 'top') { + length *= -1; + } + + if (obj.Get('chart.variant') == 'dot') { + context.moveTo(x, obj.coords[i][1] - 5); + context.lineTo(x, obj.coords[i][1] - 5 - length); + + var text_x = x; + var text_y = obj.coords[i][1] - 5 - length; + + } else if (obj.Get('chart.variant') == 'arrow') { + context.moveTo(x, obj.coords[i][1] - 5); + context.lineTo(x, obj.coords[i][1] - 5 - length); + + var text_x = x; + var text_y = obj.coords[i][1] - 5 - length; + + } else { + + context.arc(x, y, 2.5, 0, 6.28, 0); + context.moveTo(x, y); + context.lineTo(x, y - length); + + var text_x = x; + var text_y = y - length; + } + + context.stroke(); + context.fill(); + + + } else if (obj.type == 'line') { + + if ( + typeof(labels_processed[i]) == 'object' && + typeof(labels_processed[i][3]) == 'number' && + labels_processed[i][3] == -1 + ) { + + context.moveTo(x, y + 5); + context.lineTo(x, y + 5 + length); + + context.stroke(); + context.beginPath(); + + // This draws the arrow + context.moveTo(x, y + 5); + context.lineTo(x - 3, y + 10); + context.lineTo(x + 3, y + 10); + context.closePath(); + + var text_x = x; + var text_y = y + 5 + length; + + } else { + + var text_x = x; + var text_y = y - 5 - length; + + context.moveTo(x, y - 5); + context.lineTo(x, y - 5 - length); + + context.stroke(); + context.beginPath(); + + // This draws the arrow + context.moveTo(x, y - 5); + context.lineTo(x - 3, y - 10); + context.lineTo(x + 3, y - 10); + context.closePath(); + } + + context.fill(); + } + + // Taken out on the 10th Nov 2010 - unnecessary + //var width = context.measureText(labels[i]).width; + + context.beginPath(); + + // Fore ground color + context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black'; + + RGraph.Text(context, + obj.Get('chart.text.font'), + obj.Get('chart.text.size'), + text_x, + text_y, + (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i], + 'bottom', + 'center', + true, + null, + (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white'); + context.fill(); + } + } + } + } + } + + + /** + * This function "fills in" key missing properties that various implementations lack + * + * @param object e The event object + */ + RGraph.FixEventObject = function (e) + { + if (RGraph.isIE8()) { + + var e = event; + + e.pageX = (event.clientX + document.body.scrollLeft); + e.pageY = (event.clientY + document.body.scrollTop); + e.target = event.srcElement; + + if (!document.body.scrollTop && document.documentElement.scrollTop) { + e.pageX += parseInt(document.documentElement.scrollLeft); + e.pageY += parseInt(document.documentElement.scrollTop); + } + } + + // This is mainly for FF which doesn't provide offsetX + if (typeof(e.offsetX) == 'undefined' && typeof(e.offsetY) == 'undefined') { + var coords = RGraph.getMouseXY(e); + e.offsetX = coords[0]; + e.offsetY = coords[1]; + } + + // Any browser that doesn't implement stopPropagation() (MSIE) + if (!e.stopPropagation) { + e.stopPropagation = function () {window.event.cancelBubble = true;} + } + + return e; + } + + + /** + * Draw crosshairs if enabled + * + * @param object obj The graph object (from which we can get the context and canvas as required) + */ + RGraph.DrawCrosshairs = function (obj) + { + if (obj.Get('chart.crosshairs')) { + var canvas = obj.canvas; + var context = obj.context; + + // 5th November 2010 - removed now that tooltips are DOM2 based. + //if (obj.Get('chart.tooltips') && obj.Get('chart.tooltips').length > 0) { + //alert('[' + obj.type.toUpperCase() + '] Sorry - you cannot have crosshairs enabled with tooltips! Turning off crosshairs...'); + //obj.Set('chart.crosshairs', false); + //return; + //} + + //canvas.onmousemove = function (e) + var crosshairs_mousemove = function (e) + { + var e = RGraph.FixEventObject(e); + var canvas = obj.canvas; + var context = obj.context; + var width = canvas.width; + var height = canvas.height; + var adjustments = obj.Get('chart.tooltips.coords.adjust'); + + + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + + var mouseCoords = RGraph.getMouseXY(e); + var x = mouseCoords[0]; + var y = mouseCoords[1]; + + if (typeof(adjustments) == 'object' && adjustments[0] && adjustments[1]) { + x = x - adjustments[0]; + y = y - adjustments[1]; + } + + RGraph.Clear(canvas); + obj.Draw(); + + if ( x >= gutterLeft + && y >= gutterTop + && x <= (width - gutterRight) + && y <= (height - gutterBottom) + ) { + + var linewidth = obj.Get('chart.crosshairs.linewidth'); + context.lineWidth = linewidth ? linewidth : 1; + + context.beginPath(); + context.strokeStyle = obj.Get('chart.crosshairs.color'); + + // Draw a top vertical line + if (obj.Get('chart.crosshairs.vline')) { + context.moveTo(x, gutterTop); + context.lineTo(x, height - gutterBottom); + } + + // Draw a horizontal line + if (obj.Get('chart.crosshairs.hline')) { + context.moveTo(gutterLeft, y); + context.lineTo(width - gutterRight, y); + } + + context.stroke(); + + + /** + * Need to show the coords? + */ + if (obj.Get('chart.crosshairs.coords')) { + if (obj.type == 'scatter') { + + var xCoord = (((x - obj.Get('chart.gutter.left')) / (obj.canvas.width - gutterLeft - gutterRight)) * (obj.Get('chart.xmax') - obj.Get('chart.xmin'))) + obj.Get('chart.xmin'); + xCoord = xCoord.toFixed(obj.Get('chart.scale.decimals')); + var yCoord = obj.max - (((y - obj.Get('chart.gutter.top')) / (obj.canvas.height - gutterTop - gutterBottom)) * obj.max); + + if (obj.type == 'scatter' && obj.Get('chart.xaxispos') == 'center') { + yCoord = (yCoord - (obj.max / 2)) * 2; + } + + yCoord = yCoord.toFixed(obj.Get('chart.scale.decimals')); + + var div = RGraph.Registry.Get('chart.coordinates.coords.div'); + var mouseCoords = RGraph.getMouseXY(e); + var canvasXY = RGraph.getCanvasXY(canvas); + + if (!div) { + + div = document.createElement('DIV'); + div.__object__ = obj; + div.style.position = 'absolute'; + div.style.backgroundColor = 'white'; + div.style.border = '1px solid black'; + div.style.fontFamily = 'Arial, Verdana, sans-serif'; + div.style.fontSize = '10pt' + div.style.padding = '2px'; + div.style.opacity = 1; + div.style.WebkitBorderRadius = '3px'; + div.style.borderRadius = '3px'; + div.style.MozBorderRadius = '3px'; + document.body.appendChild(div); + + RGraph.Registry.Set('chart.coordinates.coords.div', div); + } + + // Convert the X/Y pixel coords to correspond to the scale + + div.style.opacity = 1; + div.style.display = 'inline'; + + if (!obj.Get('chart.crosshairs.coords.fixed')) { + div.style.left = Math.max(2, (e.pageX - div.offsetWidth - 3)) + 'px'; + div.style.top = Math.max(2, (e.pageY - div.offsetHeight - 3)) + 'px'; + } else { + div.style.left = canvasXY[0] + gutterLeft + 3 + 'px'; + div.style.top = canvasXY[1] + gutterTop + 3 + 'px'; + } + + div.innerHTML = '<span style="color: #666">' + obj.Get('chart.crosshairs.coords.labels.x') + ':</span> ' + xCoord + '<br><span style="color: #666">' + obj.Get('chart.crosshairs.coords.labels.y') + ':</span> ' + yCoord; + + canvas.addEventListener('mouseout', RGraph.HideCrosshairCoords, false); + + obj.canvas.__crosshairs_labels__ = div; + obj.canvas.__crosshairs_x__ = xCoord; + obj.canvas.__crosshairs_y__ = yCoord; + + } else { + alert('[RGRAPH] Showing crosshair coordinates is only supported on the Scatter chart'); + } + } + + /** + * Fire the oncrosshairs custom event + */ + RGraph.FireCustomEvent(obj, 'oncrosshairs'); + + } else { + RGraph.HideCrosshairCoords(); + } + } + canvas.addEventListener('mousemove', crosshairs_mousemove, false); + RGraph.AddEventListener(obj.id, 'mousemove', crosshairs_mousemove); + } + } + + /** + * Thisz function hides the crosshairs coordinates + */ + RGraph.HideCrosshairCoords = function () + { + var div = RGraph.Registry.Get('chart.coordinates.coords.div'); + + if ( div + && div.style.opacity == 1 + && div.__object__.Get('chart.crosshairs.coords.fadeout') + ) { + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.9;}, 50); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.8;}, 100); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.7;}, 150); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.6;}, 200); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.5;}, 250); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.4;}, 300); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.3;}, 350); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.2;}, 400); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.1;}, 450); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0;}, 500); + setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.display = 'none';}, 550); + } + } + + + /** + * Trims the right hand side of a string. Removes SPACE, TAB + * CR and LF. + * + * @param string str The string to trim + */ + RGraph.rtrim = function (str) + { + return str.replace(/( |\n|\r|\t)+$/, ''); + } + + + /** + * Draws the3D axes/background + */ + RGraph.Draw3DAxes = function (obj) + { + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + + var context = obj.context; + var canvas = obj.canvas; + + context.strokeStyle = '#aaa'; + context.fillStyle = '#ddd'; + + // Draw the vertical left side + context.beginPath(); + context.moveTo(gutterLeft, gutterTop); + context.lineTo(gutterLeft + 10, gutterTop - 5); + context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5); + context.lineTo(gutterLeft, canvas.height - gutterBottom); + context.closePath(); + + context.stroke(); + context.fill(); + + // Draw the bottom floor + context.beginPath(); + context.moveTo(gutterLeft, canvas.height - gutterBottom); + context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5); + context.lineTo(canvas.width - gutterRight + 10, canvas.height - gutterBottom - 5); + context.lineTo(canvas.width - gutterRight, canvas.height - gutterBottom); + context.closePath(); + + context.stroke(); + context.fill(); + } + + /** + * Turns off any shadow + * + * @param object obj The graph object + */ + RGraph.NoShadow = function (obj) + { + obj.context.shadowColor = 'rgba(0,0,0,0)'; + obj.context.shadowBlur = 0; + obj.context.shadowOffsetX = 0; + obj.context.shadowOffsetY = 0; + } + + + /** + * Sets the four shadow properties - a shortcut function + * + * @param object obj Your graph object + * @param string color The shadow color + * @param number offsetx The shadows X offset + * @param number offsety The shadows Y offset + * @param number blur The blurring effect applied to the shadow + */ + RGraph.SetShadow = function (obj, color, offsetx, offsety, blur) + { + obj.context.shadowColor = color; + obj.context.shadowOffsetX = offsetx; + obj.context.shadowOffsetY = offsety; + obj.context.shadowBlur = blur; + } + + + /** + * This function attempts to "fill in" missing functions from the canvas + * context object. Only two at the moment - measureText() nd fillText(). + * + * @param object context The canvas 2D context + */ + RGraph.OldBrowserCompat = function (context) + { + if (!context.measureText) { + + // This emulates the measureText() function + context.measureText = function (text) + { + var textObj = document.createElement('DIV'); + textObj.innerHTML = text; + textObj.style.backgroundColor = 'white'; + textObj.style.position = 'absolute'; + textObj.style.top = -100 + textObj.style.left = 0; + document.body.appendChild(textObj); + + var width = {width: textObj.offsetWidth}; + + textObj.style.display = 'none'; + + return width; + } + } + + if (!context.fillText) { + // This emulates the fillText() method + context.fillText = function (text, targetX, targetY) + { + return false; + } + } + + // If IE8, add addEventListener() + if (!context.canvas.addEventListener) { + window.addEventListener = function (ev, func, bubble) + { + return this.attachEvent('on' + ev, func); + } + + context.canvas.addEventListener = function (ev, func, bubble) + { + return this.attachEvent('on' + ev, func); + } + } + } + + + + /** + * This is a function that can be used to run code asynchronously, which can + * be used to speed up the loading of you pages. + * + * @param string func This is the code to run. It can also be a function pointer. + * The front page graphs show this function in action. Basically + * each graphs code is made in a function, and that function is + * passed to this function to run asychronously. + */ + RGraph.Async = function (func) + { + return setTimeout(func, arguments[1] ? arguments[1] : 1); + } + + + /** + * A custom random number function + * + * @param number min The minimum that the number should be + * @param number max The maximum that the number should be + * @param number How many decimal places there should be. Default for this is 0 + */ + RGraph.random = function (min, max) + { + var dp = arguments[2] ? arguments[2] : 0; + var r = Math.random(); + + return Number((((max - min) * r) + min).toFixed(dp)); + } + + + /** + * Draws a rectangle with curvy corners + * + * @param context object The context + * @param x number The X coordinate (top left of the square) + * @param y number The Y coordinate (top left of the square) + * @param w number The width of the rectangle + * @param h number The height of the rectangle + * @param number The radius of the curved corners + * @param boolean Whether the top left corner is curvy + * @param boolean Whether the top right corner is curvy + * @param boolean Whether the bottom right corner is curvy + * @param boolean Whether the bottom left corner is curvy + */ + RGraph.strokedCurvyRect = function (context, x, y, w, h) + { + // The corner radius + var r = arguments[5] ? arguments[5] : 3; + + // The corners + var corner_tl = (arguments[6] || arguments[6] == null) ? true : false; + var corner_tr = (arguments[7] || arguments[7] == null) ? true : false; + var corner_br = (arguments[8] || arguments[8] == null) ? true : false; + var corner_bl = (arguments[9] || arguments[9] == null) ? true : false; + + context.beginPath(); + + // Top left side + context.moveTo(x + (corner_tl ? r : 0), y); + context.lineTo(x + w - (corner_tr ? r : 0), y); + + // Top right corner + if (corner_tr) { + context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2, false); + } + + // Top right side + context.lineTo(x + w, y + h - (corner_br ? r : 0) ); + + // Bottom right corner + if (corner_br) { + context.arc(x + w - r, y - r + h, r, Math.PI * 2, Math.PI * 0.5, false); + } + + // Bottom right side + context.lineTo(x + (corner_bl ? r : 0), y + h); + + // Bottom left corner + if (corner_bl) { + context.arc(x + r, y - r + h, r, Math.PI * 0.5, Math.PI, false); + } + + // Bottom left side + context.lineTo(x, y + (corner_tl ? r : 0) ); + + // Top left corner + if (corner_tl) { + context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5, false); + } + + context.stroke(); + } + + + /** + * Draws a filled rectangle with curvy corners + * + * @param context object The context + * @param x number The X coordinate (top left of the square) + * @param y number The Y coordinate (top left of the square) + * @param w number The width of the rectangle + * @param h number The height of the rectangle + * @param number The radius of the curved corners + * @param boolean Whether the top left corner is curvy + * @param boolean Whether the top right corner is curvy + * @param boolean Whether the bottom right corner is curvy + * @param boolean Whether the bottom left corner is curvy + */ + RGraph.filledCurvyRect = function (context, x, y, w, h) + { + // The corner radius + var r = arguments[5] ? arguments[5] : 3; + + // The corners + var corner_tl = (arguments[6] || arguments[6] == null) ? true : false; + var corner_tr = (arguments[7] || arguments[7] == null) ? true : false; + var corner_br = (arguments[8] || arguments[8] == null) ? true : false; + var corner_bl = (arguments[9] || arguments[9] == null) ? true : false; + + context.beginPath(); + + // First draw the corners + + // Top left corner + if (corner_tl) { + context.moveTo(x + r, y + r); + context.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI, false); + } else { + context.fillRect(x, y, r, r); + } + + // Top right corner + if (corner_tr) { + context.moveTo(x + w - r, y + r); + context.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0, false); + } else { + context.moveTo(x + w - r, y); + context.fillRect(x + w - r, y, r, r); + } + + + // Bottom right corner + if (corner_br) { + context.moveTo(x + w - r, y + h - r); + context.arc(x + w - r, y - r + h, r, 0, Math.PI / 2, false); + } else { + context.moveTo(x + w - r, y + h - r); + context.fillRect(x + w - r, y + h - r, r, r); + } + + // Bottom left corner + if (corner_bl) { + context.moveTo(x + r, y + h - r); + context.arc(x + r, y - r + h, r, Math.PI / 2, Math.PI, false); + } else { + context.moveTo(x, y + h - r); + context.fillRect(x, y + h - r, r, r); + } + + // Now fill it in + context.fillRect(x + r, y, w - r - r, h); + context.fillRect(x, y + r, r + 1, h - r - r); + context.fillRect(x + w - r - 1, y + r, r + 1, h - r - r); + + context.fill(); + } + + + /** + * A crude timing function + * + * @param string label The label to use for the time + */ + RGraph.Timer = function (label) + { + var d = new Date(); + + // This uses the Firebug console + console.log(label + ': ' + d.getSeconds() + '.' + d.getMilliseconds()); + } + + + /** + * Hides the palette if it's visible + */ + RGraph.HidePalette = function () + { + var div = RGraph.Registry.Get('palette'); + + if (typeof(div) == 'object' && div) { + div.style.visibility = 'hidden'; + div.style.display = 'none'; + RGraph.Registry.Set('palette', null); + } + } + + + /** + * Hides the zoomed canvas + */ + RGraph.HideZoomedCanvas = function () + { + var interval = 15; + var frames = 10; + + if (typeof(__zoomedimage__) == 'object') { + obj = __zoomedimage__.obj; + } else { + return; + } + + if (obj.Get('chart.zoom.fade.out')) { + for (var i=frames,j=1; i>=0; --i, ++j) { + if (typeof(__zoomedimage__) == 'object') { + setTimeout("__zoomedimage__.style.opacity = " + String(i / 10), j * interval); + } + } + + if (typeof(__zoomedbackground__) == 'object') { + setTimeout("__zoomedbackground__.style.opacity = " + String(i / frames), j * interval); + } + } + + if (typeof(__zoomedimage__) == 'object') { + setTimeout("__zoomedimage__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0); + } + + if (typeof(__zoomedbackground__) == 'object') { + setTimeout("__zoomedbackground__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0); + } + } + + + /** + * Adds an event handler + * + * @param object obj The graph object + * @param string event The name of the event, eg ontooltip + * @param object func The callback function + */ + RGraph.AddCustomEventListener = function (obj, name, func) + { + if (typeof(RGraph.events[obj.id]) == 'undefined') { + RGraph.events[obj.id] = []; + } + + RGraph.events[obj.id].push([obj, name, func]); + + return RGraph.events[obj.id].length - 1; + } + + + /** + * Used to fire one of the RGraph custom events + * + * @param object obj The graph object that fires the event + * @param string event The name of the event to fire + */ + RGraph.FireCustomEvent = function (obj, name) + { + if (obj && obj.isRGraph) { + var id = obj.id; + + if ( typeof(id) == 'string' + && typeof(RGraph.events) == 'object' + && typeof(RGraph.events[id]) == 'object' + && RGraph.events[id].length > 0) { + + for(var j=0; j<RGraph.events[id].length; ++j) { + if (RGraph.events[id][j] && RGraph.events[id][j][1] == name) { + RGraph.events[id][j][2](obj); + } + } + } + } + } + + + /** + * Checks the browser for traces of MSIE8 + */ + RGraph.isIE8 = function () + { + return navigator.userAgent.indexOf('MSIE 8') > 0; + } + + + /** + * Checks the browser for traces of MSIE9 + */ + RGraph.isIE9 = function () + { + return navigator.userAgent.indexOf('MSIE 9') > 0; + } + + + /** + * Checks the browser for traces of MSIE9 + */ + RGraph.isIE9up = function () + { + navigator.userAgent.match(/MSIE (\d+)/); + + return Number(RegExp.$1) >= 9; + } + + + /** + * This clears a canvases event handlers. Used at the start of each graphs .Draw() method. + * + * @param string id The ID of the canvas whose event handlers will be cleared + */ + RGraph.ClearEventListeners = function (id) + { + for (var i=0; i<RGraph.Registry.Get('chart.event.handlers').length; ++i) { + + var el = RGraph.Registry.Get('chart.event.handlers')[i]; + + if (el && (el[0] == id || el[0] == ('window_' + id))) { + if (el[0].substring(0, 7) == 'window_') { + window.removeEventListener(el[1], el[2], false); + } else { + if (document.getElementById(id)) { + document.getElementById(id).removeEventListener(el[1], el[2], false); + } + } + + RGraph.Registry.Get('chart.event.handlers')[i] = null; + } + } + } + + + /** + * + */ + RGraph.AddEventListener = function (id, e, func) + { + var type = arguments[3] ? arguments[3] : 'unknown'; + + RGraph.Registry.Get('chart.event.handlers').push([id, e, func, type]); + } + + + /** + * This function suggests a gutter size based on the widest left label. Given that the bottom + * labels may be longer, this may be a little out. + * + * @param object obj The graph object + * @param array data An array of graph data + * @return int A suggested gutter setting + */ + RGraph.getGutterSuggest = function (obj, data) + { + var str = RGraph.number_format(obj, RGraph.array_max(RGraph.getScale(RGraph.array_max(data), obj)), obj.Get('chart.units.pre'), obj.Get('chart.units.post')); + + // Take into account the HBar + if (obj.type == 'hbar') { + + var str = ''; + var len = 0; + + for (var i=0; i<obj.Get('chart.labels').length; ++i) { + str = (obj.Get('chart.labels').length > str.length ? obj.Get('chart.labels')[i] : str); + } + } + + obj.context.font = obj.Get('chart.text.size') + 'pt ' + obj.Get('chart.text.font'); + + len = obj.context.measureText(str).width + 5; + + return (obj.type == 'hbar' ? len / 3 : len); + } + + + /** + * A basic Array shift gunction + * + * @param object The numerical array to work on + * @return The new array + */ + RGraph.array_shift = function (arr) + { + var ret = []; + + for (var i=1; i<arr.length; ++i) ret.push(arr[i]); + + return ret; + } + + + /** + * If you prefer, you can use the SetConfig() method to set the configuration information + * for your chart. You may find that setting the configuration this way eases reuse. + * + * @param object obj The graph object + * @param object config The graph configuration information + */ + RGraph.SetConfig = function (obj, c) + { + for (i in c) { + if (typeof(i) == 'string') { + obj.Set(i, c[i]); + } + } + + return obj; + } + + + /** + * This function gets the canvas height. Defaults to the actual + * height but this can be changed by setting chart.height. + * + * @param object obj The graph object + */ + RGraph.GetHeight = function (obj) + { + return obj.canvas.height; + } + + + /** + * This function gets the canvas width. Defaults to the actual + * width but this can be changed by setting chart.width. + * + * @param object obj The graph object + */ + RGraph.GetWidth = function (obj) + { + return obj.canvas.width; + } + + + /** + * Clears all the custom event listeners that have been registered + * + * @param string Limits the clearing to this object ID + */ + RGraph.RemoveAllCustomEventListeners = function () + { + var id = arguments[0]; + + if (id && RGraph.events[id]) { + RGraph.events[id] = []; + } else { + RGraph.events = []; + } + } + + + /** + * Clears a particular custom event listener + * + * @param object obj The graph object + * @param number i This is the index that is return by .AddCustomEventListener() + */ + RGraph.RemoveCustomEventListener = function (obj, i) + { + if ( typeof(RGraph.events) == 'object' + && typeof(RGraph.events[obj.id]) == 'object' + && typeof(RGraph.events[obj.id][i]) == 'object') { + + RGraph.events[obj.id][i] = null; + } + } + + + /** + * This draws the background + * + * @param object obj The graph object + */ + RGraph.DrawBackgroundImage = function (obj) + { + var img = new Image(); + img.__object__ = obj; + img.__canvas__ = obj.canvas; + img.__context__ = obj.context; + img.src = obj.Get('chart.background.image'); + + obj.__background_image__ = img; + + img.onload = function () + { + var obj = this.__object__; + + var gutterLeft = obj.Get('chart.gutter.left'); + var gutterRight = obj.Get('chart.gutter.right'); + var gutterTop = obj.Get('chart.gutter.top'); + var gutterBottom = obj.Get('chart.gutter.bottom'); + + RGraph.Clear(obj.canvas); + + obj.context.drawImage(this,gutterLeft,gutterTop, RGraph.GetWidth(obj) - gutterLeft - gutterRight, RGraph.GetHeight(obj) - gutterTop - gutterBottom); + + // Draw the graph + obj.Draw(); + } + + img.onerror = function () + { + var obj = this.__canvas__.__object__; + + // Show an error alert + alert('[ERROR] There was an error with the background image that you specified: ' + img.src); + + // Draw the graph, because the onload doesn't fire, and thus that won't draw the chart + obj.Draw(); + } + } + + + /** + * This resets the canvas. Keep in mind that any translate() that has been performed will also be reset. + * + * @param object canvas The canvas + */ + RGraph.Reset = function (canvas) + { + canvas.width = canvas.width; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.effects.js b/schall/static/RGraph/libraries/RGraph.common.effects.js new file mode 100644 index 0000000..cb63371 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.effects.js @@ -0,0 +1,1516 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + /** + * This is a library of a few functions that make it easier to do + * effects like fade-ins or eaxpansion. + */ + + /** + * Initialise the various objects + */ + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + RGraph.Effects = {} + RGraph.Effects.Fade = {} + RGraph.Effects.jQuery = {} + RGraph.Effects.jQuery.HBlinds = {} + RGraph.Effects.jQuery.VBlinds = {} + RGraph.Effects.jQuery.Slide = {} + RGraph.Effects.Pie = {} + RGraph.Effects.Bar = {} + RGraph.Effects.Line = {} + RGraph.Effects.Line.jQuery = {} + RGraph.Effects.Fuel = {} + RGraph.Effects.Rose = {} + RGraph.Effects.Odo = {} + RGraph.Effects.Gauge = {} + RGraph.Effects.Meter = {} + RGraph.Effects.HBar = {} + RGraph.Effects.Radar = {} + RGraph.Effects.Waterfall = {} + + + /** + * Fadein + * + * This function simply uses the CSS opacity property - initially set to zero and + * increasing to 1 over the period of 0.5 second + * + * @param object obj The graph object + */ + RGraph.Effects.Fade.In = function (obj) + { + var canvas = obj.canvas; + + // Initially the opacity should be zero + canvas.style.opacity = 0; + + // Draw the chart + RGraph.Clear(obj.canvas); + obj.Draw(); + + // Now fade the chart in + for (var i=1; i<=10; ++i) { + setTimeout('document.getElementById("' + canvas.id + '").style.opacity = ' + (i * 0.1), i * 50); + } + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], 500); + } + } + + + /** + * Fadeout + * + * This function is a reversal of the above function - fading out instead of in + * + * @param object obj The graph object + */ + RGraph.Effects.Fade.Out = function (obj) + { + var canvas = obj.canvas; + + // Draw the chart + RGraph.Clear(obj.canvas); + obj.Draw(); + + // Now fade the chart in + for (var i=10; i>=0; --i) { + setTimeout('document.getElementById("' + canvas.id + '").style.opacity = ' + (i * 0.1), (10 - i) * 50); + } + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], 500); + } + } + + + /** + * Expand + * + * This effect is like the tooltip effect of the same name. I starts in the middle + * and expands out to full size. + * + * @param object obj The graph object + */ + RGraph.Effects.jQuery.Expand = function (obj) + { + // Check for jQuery + if (typeof(jQuery) == 'undefined') { + alert('[ERROR] Could not find jQuery object - have you included the jQuery file?'); + } + + var canvas = obj.canvas; + + if (!canvas.__rgraph_div_placeholder__) { + var div = RGraph.Effects.ReplaceCanvasWithDIV(canvas); + canvas.__rgraph_div_placeholder__ = div; + } else { + div = canvas.__rgraph_div_placeholder__; + } + + canvas.style.position = 'relative'; + canvas.style.top = (canvas.height / 2) + 'px'; + canvas.style.left = (canvas.width / 2) + 'px'; + + + canvas.style.width = 0; + canvas.style.height = 0; + + + canvas.style.opacity = 0; + + RGraph.Clear(obj.canvas); + obj.Draw(); + + $('#' + obj.id).animate({ + opacity: 1, + width: parseInt(div.style.width) + 'px', + height: parseInt(div.style.height) + 'px', + left: '-=' + (obj.canvas.width / 2) + 'px', + top: '-=' + (obj.canvas.height / 2) + 'px' + }, 1000); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], 1000); + } + } + + + /** + * A function used to replace the canvas witha Div, which inturn holds the canvas. This way the page + * layout doesn't shift in the canvas is resized. + * + * @param object canvas The canvas to replace. + */ + RGraph.Effects.ReplaceCanvasWithDIV = function (canvas) + { + if (!canvas.replacementDIV) { + // Create the place holder DIV + var div = document.createElement('DIV'); + div.style.width = canvas.width + 'px'; + div.style.height = canvas.height + 'px'; + div.style.cssFloat = canvas.style.cssFloat; + div.style.left = canvas.style.left; + div.style.top = canvas.style.top; + //div.style.position = canvas.style.position; + div.style.display = 'inline-block'; + canvas.parentNode.insertBefore(div, canvas); + + + // Remove the canvas from the document + canvas.parentNode.removeChild(canvas); + + // Add it back in as a child of the place holder + div.appendChild(canvas); + + // Reset the positioning information on the canvas + canvas.style.position = 'relative'; + canvas.style.left = (div.offsetWidth / 2) + 'px'; + canvas.style.top = (div.offsetHeight / 2) + 'px'; + canvas.style.cssFloat = ''; + + // Add a reference to the canvas to the DIV so that repeated plays of the anumation + // don't keep replacing the canvas with a new DIV + canvas.replacementDIV = div; + + } else { + var div = canvas.replacementDIV; + } + + return div; + } + + + /** + * Snap + * + * Similar to the tooltip effect of the same name, this moves the canvas in from the top left corner + * + * @param object obj The graph object + */ + RGraph.Effects.jQuery.Snap = function (obj) + { + var delay = 500; + + var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas); + + obj.canvas.style.position = 'absolute'; + obj.canvas.style.top = 0; + obj.canvas.style.left = 0; + obj.canvas.style.width = 0; + obj.canvas.style.height = 0; + obj.canvas.style.opacity = 0; + + var targetLeft = div.offsetLeft; + var targetTop = div.offsetTop; + var targetWidth = div.offsetWidth; + var targetHeight = div.offsetHeight; + + RGraph.Clear(obj.canvas); + obj.Draw(); + + $('#' + obj.id).animate({ + opacity: 1, + width: targetWidth + 'px', + height: targetHeight + 'px', + left: targetLeft + 'px', + top: targetTop + 'px' + }, delay); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay + 50); + } + } + + + /** + * Reveal + * + * This effect issmilat to the Expand effect - the canvas is slowly revealed from + * the centre outwards + * + * @param object obj The chart object + */ + RGraph.Effects.jQuery.Reveal = function (obj) + { + var opts = arguments[1] ? arguments[1] : null; + var delay = 1000; + var canvas = obj.canvas; + var xy = RGraph.getCanvasXY(obj.canvas); + + var divs = [ + ['reveal_left', xy[0], xy[1], obj.canvas.width / 2, obj.canvas.height], + ['reveal_right',(xy[0] + (obj.canvas.width / 2)),xy[1],(obj.canvas.width / 2),obj.canvas.height], + ['reveal_top',xy[0],xy[1],obj.canvas.width,(obj.canvas.height / 2)], + ['reveal_bottom',xy[0],(xy[1] + (obj.canvas.height / 2)),obj.canvas.width,(obj.canvas.height / 2)] + ]; + + for (var i=0; i<divs.length; ++i) { + var div = document.createElement('DIV'); + div.id = divs[i][0]; + div.style.width = divs[i][3]+ 'px'; + div.style.height = divs[i][4] + 'px'; + div.style.left = divs[i][1] + 'px'; + div.style.top = divs[i][2] + 'px'; + div.style.position = 'absolute'; + div.style.backgroundColor = opts && typeof(opts['color']) == 'string' ? opts['color'] : 'white'; + document.body.appendChild(div); + } + + RGraph.Clear(obj.canvas); + obj.Draw(); + + $('#reveal_left').animate({width: 0}, delay); + $('#reveal_right').animate({left: '+=' + (obj.canvas.width / 2),width: 0}, delay); + $('#reveal_top').animate({height: 0}, delay); + $('#reveal_bottom').animate({top: '+=' + (obj.canvas.height / 2),height: 0}, delay); + + // Remove the DIVs from the DOM 100ms after the animation ends + setTimeout( + function () + { + document.body.removeChild(document.getElementById("reveal_top")) + document.body.removeChild(document.getElementById("reveal_bottom")) + document.body.removeChild(document.getElementById("reveal_left")) + document.body.removeChild(document.getElementById("reveal_right")) + } + , delay); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Horizontal Blinds (open) + * + * @params object obj The graph object + */ + RGraph.Effects.jQuery.HBlinds.Open = function (obj) + { + var canvas = obj.canvas; + var opts = arguments[1] ? arguments[1] : []; + var delay = 1000; + var color = opts['color'] ? opts['color'] : 'white'; + var xy = RGraph.getCanvasXY(canvas); + var height = canvas.height / 5; + + /** + * First draw the chart + */ + RGraph.Clear(obj.canvas); + obj.Draw(); + + for (var i=0; i<5; ++i) { + var div = document.createElement('DIV'); + div.id = 'blinds_' + i; + div.style.width = canvas.width + 'px'; + div.style.height = height + 'px'; + div.style.left = xy[0] + 'px'; + div.style.top = (xy[1] + (canvas.height * (i / 5))) + 'px'; + div.style.position = 'absolute'; + div.style.backgroundColor = color; + document.body.appendChild(div); + + $('#blinds_' + i).animate({height: 0}, delay); + } + + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Horizontal Blinds (close) + * + * @params object obj The graph object + */ + RGraph.Effects.jQuery.HBlinds.Close = function (obj) + { + var canvas = obj.canvas; + var opts = arguments[1] ? arguments[1] : []; + var delay = 1000; + var color = opts['color'] ? opts['color'] : 'white'; + var xy = RGraph.getCanvasXY(canvas); + var height = canvas.height / 5; + + for (var i=0; i<5; ++i) { + var div = document.createElement('DIV'); + div.id = 'blinds_' + i; + div.style.width = canvas.width + 'px'; + div.style.height = 0; + div.style.left = xy[0] + 'px'; + div.style.top = (xy[1] + (canvas.height * (i / 5))) + 'px'; + div.style.position = 'absolute'; + div.style.backgroundColor = color; + document.body.appendChild(div); + + $('#blinds_' + i).animate({height: height + 'px'}, delay); + } + + setTimeout(function () {RGraph.Clear(obj.canvas);}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Vertical Blinds (open) + * + * @params object obj The graph object + */ + RGraph.Effects.jQuery.VBlinds.Open = function (obj) + { + var canvas = obj.canvas; + var opts = arguments[1] ? arguments[1] : []; + var delay = 1000; + var color = opts['color'] ? opts['color'] : 'white'; + var xy = RGraph.getCanvasXY(canvas); + var width = canvas.width / 10; + + /** + * First draw the chart + */ + RGraph.Clear(obj.canvas); + obj.Draw(); + + for (var i=0; i<10; ++i) { + var div = document.createElement('DIV'); + div.id = 'blinds_' + i; + div.style.width = width + 'px'; + div.style.height = canvas.height + 'px'; + div.style.left = (xy[0] + (canvas.width * (i / 10))) + 'px'; + div.style.top = (xy[1]) + 'px'; + div.style.position = 'absolute'; + div.style.backgroundColor = color; + document.body.appendChild(div); + + $('#blinds_' + i).animate({width: 0}, delay); + } + + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_5'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_6'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_7'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_8'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_9'));}, delay + 100); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Vertical Blinds (close) + * + * @params object obj The graph object + */ + RGraph.Effects.jQuery.VBlinds.Close = function (obj) + { + var canvas = obj.canvas; + var opts = arguments[1] ? arguments[1] : []; + var delay = 1000; + var color = opts['color'] ? opts['color'] : 'white'; + var xy = RGraph.getCanvasXY(canvas); + var width = canvas.width / 10; + + // Don't draw the chart + + for (var i=0; i<10; ++i) { + var div = document.createElement('DIV'); + div.id = 'blinds_' + i; + div.style.width = 0; + div.style.height = canvas.height + 'px'; + div.style.left = (xy[0] + (canvas.width * (i / 10))) + 'px'; + div.style.top = (xy[1]) + 'px'; + div.style.position = 'absolute'; + div.style.backgroundColor = color; + document.body.appendChild(div); + + $('#blinds_' + i).animate({width: width}, delay); + } + + setTimeout(function () {RGraph.Clear(obj.canvas, color);}, delay + 100); + + if (opts['remove']) { + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_5'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_6'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_7'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_8'));}, delay + 100); + setTimeout(function () {document.body.removeChild(document.getElementById('blinds_9'));}, delay + 100); + } + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Pie chart grow + * + * Gradually increases the pie chart radius + * + * @params object obj The graph object + */ + RGraph.Effects.Pie.Grow = function (obj) + { + var canvas = obj.canvas; + var opts = arguments[1] ? arguments[1] : []; + var color = opts['color'] ? opts['color'] : 'white'; + var xy = RGraph.getCanvasXY(canvas); + + canvas.style.visibility = 'hidden'; + obj.Draw(); + var radius = obj.getRadius(); + RGraph.Clear(obj.canvas); + canvas.style.visibility = 'visible'; + + obj.Set('chart.radius', 0); + + RGraph.Effects.Animate(obj, {'chart.radius': radius}, arguments[2]); + } + + + /** + * Grow + * + * The Bar chart Grow effect gradually increases the values of the bars + * + * @param object obj The graph object + */ + RGraph.Effects.Bar.Grow = function (obj) + { + // Save the data + obj.original_data = RGraph.array_clone(obj.data); + + // Zero the data + obj.__animation_frame__ = 0; + + // Stop the scale from changing by setting chart.ymax (if it's not already set) + if (obj.Get('chart.ymax') == null) { + + var ymax = 0; + + for (var i=0; i<obj.data.length; ++i) { + if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') { + ymax = Math.max(ymax, RGraph.array_sum(obj.data[i])); + } else { + ymax = Math.max(ymax, obj.data[i]); + } + } + + ymax = RGraph.getScale(ymax)[4]; + + obj.Set('chart.ymax', ymax); + } + + function Grow () + { + var numFrames = 30; + + if (!obj.__animation_frame__) { + obj.__animation_frame__ = 0; + obj.__original_hmargin__ = obj.Get('chart.hmargin'); + obj.__hmargin__ = ((obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right')) / obj.data.length) / 2; + obj.Set('chart.hmargin', obj.__hmargin__); + } + + // Alter the Bar chart data depending on the frame + for (var j=0; j<obj.original_data.length; ++j) { + if (typeof(obj.data[j]) == 'object') { + for (var k=0; k<obj.data[j].length; ++k) { + obj.data[j][k] = (obj.__animation_frame__ / numFrames) * obj.original_data[j][k]; + } + } else { + obj.data[j] = (obj.__animation_frame__ / numFrames) * obj.original_data[j]; + } + } + + /** + * Increment the hmargin to the target + */ + obj.Set('chart.hmargin', ((1 - (obj.__animation_frame__ / numFrames)) * (obj.__hmargin__ - obj.__original_hmargin__)) + obj.__original_hmargin__); + + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (obj.__animation_frame__ < numFrames) { + obj.__animation_frame__ += 1; + + if (location.href.indexOf('?settimeout') > 0) { + setTimeout(Grow, 40); + } else { + RGraph.Effects.UpdateCanvas(Grow); + } + } + } + + RGraph.Effects.UpdateCanvas(Grow); + } + + + /** + * A wrapper function that encapsulate requestAnimationFrame + * + * @param function func The animation function + */ + RGraph.Effects.UpdateCanvas = function (func) + { + // Standard + if (typeof(window.requestAnimationFrame) == 'function') { + window.requestAnimationFrame(func); + // IE 10+ + } else if (typeof(window.msRequestAnimationFrame) == 'function') { + window.msRequestAnimationFrame(func); + // Chrome + } else if (typeof(window.webkitRequestAnimationFrame) == 'function') { + window.webkitRequestAnimationFrame(func); + // Firefox + } else if (window.mozRequestAnimationFrame && 0) { // Seems rather slow in FF6 - so disabled + window.mozRequestAnimationFrame(func); + // Default fallback to setTimeout + } else { + setTimeout(func, 16.666); + } + } + + + /** + * Grow + * + * The Fuel chart Grow effect gradually increases the values of the Fuel chart + * + * @param object obj The graph object + */ + RGraph.Effects.Fuel.Grow = function (fuelObj) + { + function Grow () + { + var numFrames = 35; + + if (!fuelObj.__animation_frame__) { + fuelObj.__animation_frame__ = 0; + fuelObj.__min__ = fuelObj.min; + fuelObj.__value__ = fuelObj.value; + } + + + fuelObj.value = fuelObj.__min__ + (((fuelObj.__value__ - fuelObj.__min__) / numFrames) * fuelObj.__animation_frame__); + + RGraph.Clear(fuelObj.canvas); + fuelObj.Draw(); + + if (fuelObj.__animation_frame__ < numFrames) { + fuelObj.__animation_frame__ += 1; + RGraph.Effects.UpdateCanvas(Grow); + } + } + + RGraph.Effects.UpdateCanvas(Grow); + } + + + /** + * The Animate function. Similar to the jQuery Animate() function - simply pass it a + * map of the properties and their target values, and this function will animate + * them to get to those values. + * + * @param object obj The chart object + * @param object map A map (an associative array) of the properties and their target values. + * @param An optional function which will be called when the animation is complete + */ + RGraph.Effects.Animate = function (obj, map) + { + obj.Draw(); + + RGraph.Effects.__total_frames__ = (map && map['frames']) ? map['frames'] : 30; + + function Animate_Iterator (func) + { + var id = [obj.id + '_' + obj.type]; + + // Very first time in - initialise the arrays + if (typeof(RGraph.Effects.__current_frame__ ) == 'undefined') { + RGraph.Effects.__current_frame__ = new Array(); + RGraph.Effects.__original_values__ = new Array(); + RGraph.Effects.__diffs__ = new Array(); + RGraph.Effects.__steps__ = new Array(); + RGraph.Effects.__callback__ = new Array(); + } + + // Initialise the arrays for THIS animation (not necessrily the first in the page) + if (!RGraph.Effects.__current_frame__[id]) { + RGraph.Effects.__current_frame__[id] = RGraph.Effects.__total_frames__; + RGraph.Effects.__original_values__[id] = {}; + RGraph.Effects.__diffs__[id] = {}; + RGraph.Effects.__steps__[id] = {}; + RGraph.Effects.__callback__[id] = func; + } + + for (var i in map) { + if (typeof(map[i]) == 'string' || typeof(map[i]) == 'number') { + + // If this the first frame, record the proginal value + if (RGraph.Effects.__current_frame__[id] == RGraph.Effects.__total_frames__) { + RGraph.Effects.__original_values__[id][i] = obj.Get(i); + RGraph.Effects.__diffs__[id][i] = map[i] - RGraph.Effects.__original_values__[id][i]; + RGraph.Effects.__steps__[id][i] = RGraph.Effects.__diffs__[id][i] / RGraph.Effects.__total_frames__; + } + + obj.Set(i, obj.Get(i) + RGraph.Effects.__steps__[id][i]); + + RGraph.Clear(obj.canvas); + obj.Draw(); + } + } + + // If the current frame number is above zero, run the animation iterator again + if (--RGraph.Effects.__current_frame__[id] > 0) { + //setTimeout(Animate_Iterator, 100) + RGraph.Effects.UpdateCanvas(Animate_Iterator); + + // Optional callback + } else { + + if (typeof(RGraph.Effects.__callback__[id]) == 'function') { + (RGraph.Effects.__callback__[id])(obj); + } + + // Get rid of the arrays + RGraph.Effects.__current_frame__[id] = null; + RGraph.Effects.__original_values__[id] = null; + RGraph.Effects.__diffs__[id] = null; + RGraph.Effects.__steps__[id] = null; + RGraph.Effects.__callback__[id] = null; + + } + } + + Animate_Iterator(arguments[2]); + } + + + /** + * Slide in + * + * This function is a wipe that can be used when switching the canvas to a new graph + * + * @param object obj The graph object + */ + RGraph.Effects.jQuery.Slide.In = function (obj) + { + RGraph.Clear(obj.canvas); + obj.Draw(); + + var canvas = obj.canvas; + var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas); + var delay = 1000; + div.style.overflow= 'hidden'; + var direction = typeof(arguments[1]) == 'object' && typeof(arguments[1]['direction']) == 'string' ? arguments[1]['direction'] : 'left'; + + canvas.style.position = 'relative'; + + if (direction == 'left') { + canvas.style.left = (0 - div.offsetWidth) + 'px'; + canvas.style.top = 0; + } else if (direction == 'top') { + canvas.style.left = 0; + canvas.style.top = (0 - div.offsetHeight) + 'px'; + } else if (direction == 'bottom') { + canvas.style.left = 0; + canvas.style.top = div.offsetHeight + 'px'; + } else { + canvas.style.left = div.offsetWidth + 'px'; + canvas.style.top = 0; + } + + $('#' + obj.id).animate({left:0,top:0}, delay); + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Slide out + * + * This function is a wipe that can be used when switching the canvas to a new graph + * + * @param object obj The graph object + */ + RGraph.Effects.jQuery.Slide.Out = function (obj) + { + var canvas = obj.canvas; + var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas); + var delay = 1000; + div.style.overflow= 'hidden'; + var direction = typeof(arguments[1]) == 'object' && typeof(arguments[1]['direction']) == 'string' ? arguments[1]['direction'] : 'left'; + + canvas.style.position = 'relative'; + canvas.style.left = 0; + canvas.style.top = 0; + + if (direction == 'left') { + $('#' + obj.id).animate({left: (0 - canvas.width) + 'px'}, delay); + } else if (direction == 'top') { + $('#' + obj.id).animate({left: 0, top: (0 - div.offsetHeight) + 'px'}, delay); + } else if (direction == 'bottom') { + $('#' + obj.id).animate({top: (0 + div.offsetHeight) + 'px'}, delay); + } else { + $('#' + obj.id).animate({left: (0 + canvas.width) + 'px'}, delay); + } + + /** + * Callback + */ + if (typeof(arguments[2]) == 'function') { + setTimeout(arguments[2], delay); + } + } + + + /** + * Unfold + * + * This effect gradually increases the X/Y coordinatesfrom 0 + * + * @param object obj The chart object + */ + RGraph.Effects.Line.Unfold = function (obj) + { + obj.Set('chart.animation.factor', obj.Get('chart.animation.unfold.initial')); + RGraph.Effects.Animate(obj, {'chart.animation.factor': 1}, arguments[2]); + } + + + /** + * Unfold + * + * This effect gradually increases the radiuss and decrease the margin of the Rose chart + * + * @param object obj The chart object + * @param Not used - pass null + * @param function An optional callback function + */ + RGraph.Effects.Rose.Grow = function (obj) + { + var original_margin = obj.Get('chart.margin'); + var margin = (360 / obj.data.length) / 2; + + obj.Set('chart.margin', margin); + obj.Set('chart.animation.grow.factor', 0); + + RGraph.Effects.Animate(obj, {'chart.margin': original_margin, 'chart.animation.grow.factor': 1, 'frames': 45}, arguments[2]); + } + + + /** + * UnfoldFromCenter + * + * Line chart unfold from center + */ + RGraph.Effects.Line.UnfoldFromCenter = function (obj) + { + var numFrames = 30; + + var original_opacity = obj.canvas.style.opacity; + obj.canvas.style.opacity = 0; + obj.Draw(); + var center_value = obj.scale[4] / 2; + obj.Set('chart.ymax', Number(obj.scale[4])); + RGraph.Clear(obj.canvas); + obj.canvas.style.opacity = original_opacity; + var original_data = RGraph.array_clone(obj.original_data); + var original_blur = obj.Get('chart.shadow.blur'); + obj.Set('chart.shadow.blur', 0); + var callback = arguments[2]; + + if (!obj.__increments__) { + + obj.__increments__ = new Array(); + + for (var dataset=0; dataset<original_data.length; ++dataset) { + + obj.__increments__[dataset] = new Array(); + + for (var i=0; i<original_data[dataset].length; ++i) { + obj.__increments__[dataset][i] = (original_data[dataset][i] - center_value) / numFrames; + + obj.original_data[dataset][i] = center_value; + } + } + } + + function UnfoldFromCenter () + { + RGraph.Clear(obj.canvas); + obj.Draw(); + + for (var dataset=0; dataset<original_data.length; ++dataset) { + for (var i=0; i<original_data[dataset].length; ++i) { + obj.original_data[dataset][i] += obj.__increments__[dataset][i]; + } + } + + if (--numFrames > 0) { + RGraph.Effects.UpdateCanvas(UnfoldFromCenter); + } else { + obj.original_data = RGraph.array_clone(original_data); + obj.__increments__ = null; + obj.Set('chart.shadow.blur', original_blur); + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (typeof(callback) == 'function') { + callback(obj); + } + } + } + + UnfoldFromCenter(); + } + + + /** + * FoldToCenter + * + * Line chart FoldTocenter + */ + RGraph.Effects.Line.FoldToCenter = function (obj) + { + var totalFrames = 30; + var numFrame = totalFrames; + obj.Draw(); + var center_value = obj.scale[4] / 2; + obj.Set('chart.ymax', Number(obj.scale[4])); + RGraph.Clear(obj.canvas); + var original_data = RGraph.array_clone(obj.original_data); + obj.Set('chart.shadow.blur', 0); + var callback = arguments[2]; + + function FoldToCenter () + { + for (var i=0; i<obj.data.length; ++i) { + if (obj.data[i].length) { + for (var j=0; j<obj.data[i].length; ++j) { + if (obj.original_data[i][j] > center_value) { + obj.original_data[i][j] = ((original_data[i][j] - center_value) * (numFrame/totalFrames)) + center_value; + } else { + obj.original_data[i][j] = center_value - ((center_value - original_data[i][j]) * (numFrame/totalFrames)); + } + } + } + } + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (numFrame-- > 0) { + RGraph.Effects.UpdateCanvas(FoldToCenter); + } else if (typeof(callback) == 'function') { + callback(obj); + } + } + + RGraph.Effects.UpdateCanvas(FoldToCenter); + } + + + /** + * Odo Grow + * + * This effect gradually increases the represented value + * + * @param object obj The chart object + * @param Not used - pass null + * @param function An optional callback function + */ + RGraph.Effects.Odo.Grow = function (obj) + { + var numFrames = 30; + var origValue = Number(obj.currentValue); + var newValue = obj.value; + var diff = newValue - origValue; + var step = (diff / numFrames); + var callback = arguments[2]; + + function Grow () + { + if (obj.currentValue != newValue) { + obj.value = Number(obj.currentValue) + step; + } + + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (numFrames-- > 0) { + //setTimeout(Grow, 100); + RGraph.Effects.UpdateCanvas(Grow); + } else if (callback) { + callback(obj) + } + } + + //setTimeout(Grow, 100); + RGraph.Effects.UpdateCanvas(Grow); + } + + + /** + * Meter Grow + * + * This effect gradually increases the represented value + * + * @param object obj The chart object + * @param Not used - pass null + * @param function An optional callback function + */ + RGraph.Effects.Meter.Grow = function (obj) + { + if (!obj.currentValue) { + obj.currentValue = obj.min; + } + + var totalFrames = 60; + var numFrame = 0; + var diff = obj.value - obj.currentValue; + var step = diff / totalFrames + var callback = arguments[2]; + + function Grow_meter_inner () + { + obj.value = obj.currentValue + step; + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (numFrame++ < totalFrames) { + RGraph.Effects.UpdateCanvas(Grow_meter_inner); + } else if (typeof(callback) == 'function') { + callback(obj); + } + } + + RGraph.Effects.UpdateCanvas(Grow_meter_inner); + } + + + /** + * Grow + * + * The HBar chart Grow effect gradually increases the values of the bars + * + * @param object obj The graph object + */ + RGraph.Effects.HBar.Grow = function (obj) + { + // Save the data + obj.original_data = RGraph.array_clone(obj.data); + + // Zero the data + obj.__animation_frame__ = 0; + + // Stop the scale from changing by setting chart.ymax (if it's not already set) + if (obj.Get('chart.xmax') == 0) { + + var xmax = 0; + + for (var i=0; i<obj.data.length; ++i) { + if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') { + xmax = Math.max(xmax, RGraph.array_sum(obj.data[i])); + } else if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'grouped') { + xmax = Math.max(xmax, RGraph.array_max(obj.data[i])); + } else { + xmax = Math.max(xmax, RGraph.array_max(obj.data[i])); + } + } + + xmax = RGraph.getScale(xmax)[4]; + + obj.Set('chart.xmax', xmax); + } + + /** + * Turn off shadow blur for the duration of the animation + */ + if (obj.Get('chart.shadow.blur') > 0) { + var __original_shadow_blur__ = obj.Get('chart.shadow.blur'); + obj.Set('chart.shadow.blur', 0); + } + + function Grow () + { + var numFrames = 30; + + if (!obj.__animation_frame__) { + obj.__animation_frame__ = 0; + obj.__original_vmargin__ = obj.Get('chart.vmargin'); + obj.__vmargin__ = ((obj.canvas.height - obj.Get('chart.gutter.top') - obj.Get('chart.gutter.bottom')) / obj.data.length) / 2; + obj.Set('chart.vmargin', obj.__vmargin__); + } + + // Alter the Bar chart data depending on the frame + for (var j=0; j<obj.original_data.length; ++j) { + + // This stops the animatioon from being completely linear + var easing = Math.pow(Math.sin((obj.__animation_frame__ * (90 / numFrames)) / (180 / Math.PI)), 4); + + if (typeof(obj.data[j]) == 'object') { + for (var k=0; k<obj.data[j].length; ++k) { + obj.data[j][k] = (obj.__animation_frame__ / numFrames) * obj.original_data[j][k] * easing; + } + } else { + obj.data[j] = (obj.__animation_frame__ / numFrames) * obj.original_data[j] * easing; + } + } + + /** + * Increment the vmargin to the target + */ + obj.Set('chart.vmargin', ((1 - (obj.__animation_frame__ / numFrames)) * (obj.__vmargin__ - obj.__original_vmargin__)) + obj.__original_vmargin__); + + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (obj.__animation_frame__ < numFrames) { + obj.__animation_frame__ += 1; + + RGraph.Effects.UpdateCanvas(Grow); + + // Turn any shadow blur back on + } else { + if (typeof(__original_shadow_blur__) == 'number' && __original_shadow_blur__ > 0) { + obj.Set('chart.shadow.blur', __original_shadow_blur__); + RGraph.Clear(obj.canvas); + obj.Draw(); + } + } + } + + RGraph.Effects.UpdateCanvas(Grow); + } + + + /** + * Trace + * + * This effect is for the Line chart, uses the jQuery library and slowly + * uncovers the Line , but you can see the background of the chart. This effect + * is quite new (1/10/2011) and as such should be used with caution. + * + * @param object obj The graph object + * @param object Not used + * @param int A number denoting how long (in millseconds) the animation should last for. Defauld + * is 1500 + */ + RGraph.Effects.Line.jQuery.Trace = function (obj) + { + RGraph.Clear(obj.canvas); + obj.Draw(); + + /** + * Create the DIV that the second canvas will sit in + */ + var div = document.createElement('DIV'); + var xy = RGraph.getCanvasXY(obj.canvas); + div.id = '__rgraph_trace_animation_' + RGraph.random(0, 4351623) + '__'; + div.style.left = xy[0] + 'px'; + div.style.top = xy[1] + 'px'; + div.style.width = obj.Get('chart.gutter.left'); + div.style.height = obj.canvas.height + 'px'; + div.style.position = 'absolute'; + div.style.overflow = 'hidden'; + document.body.appendChild(div); + + /** + * Make the second canvas + */ + var id = '__rgraph_line_reveal_animation_' + RGraph.random(0, 99999999) + '__'; + var canvas2 = document.createElement('CANVAS'); + canvas2.width = obj.canvas.width; + canvas2.height = obj.canvas.height; + canvas2.style.position = 'absolute'; + canvas2.style.left = 0; + canvas2.style.top = 0; + + canvas2.id = id; + div.appendChild(canvas2); + + var reposition_canvas2 = function (e) + { + var xy = RGraph.getCanvasXY(obj.canvas); + + div.style.left = xy[0] + 'px'; + div.style.top = xy[1] + 'px'; + } + window.addEventListener('resize', reposition_canvas2, false) + + /** + * Make a copy of the original Line object + */ + var obj2 = new RGraph.Line(id, RGraph.array_clone(obj.original_data)); + + for (i in obj.properties) { + if (typeof(i) == 'string' && obj.properties[i]) { + obj2.Set(i, obj.properties[i]); + } + } + + obj2.Set('chart.labels', []); + obj2.Set('chart.background.grid', false); + obj2.Set('chart.ylabels', false); + obj2.Set('chart.noaxes', true); + obj2.Set('chart.title', ''); + obj2.Set('chart.title.xaxis', ''); + obj2.Set('chart.title.yaxis', ''); + obj2.Draw(); + + /** + * This effectively hides the line + */ + obj.Set('chart.line.visible', false); + obj.Set('chart.colors', ['rgba(0,0,0,0)']); + if (obj.Get('chart.filled')) { + obj.Set('chart.fillstyle', 'rgba(0,0,0,0)'); + } + + RGraph.Clear(obj.canvas); + obj.Draw(); + + + /** + * Get rid of the second canvas and turn the line back on + * on the original. + */ + RGraph.Effects.Line.Trace_callback = function () + { + // Remove the window resize listener + window.removeEventListener('resize', reposition_canvas2, false); + + div.parentNode.removeChild(div); + div.removeChild(canvas2); + obj.Set('chart.line.visible', true); + + // Revert the filled status back to as it was + obj.Set('chart.filled', RGraph.array_clone(obj2.Get('chart.filled'))); + obj.Set('chart.fillstyle', RGraph.array_clone(obj2.Get('chart.fillstyle'))); + obj.Set('chart.colors', RGraph.array_clone(obj2.Get('chart.colors'))); + + RGraph.Clear(obj.canvas); + obj.Draw(); + } + + + $('#' + div.id).animate({ + width: obj.canvas.width + 'px' + }, arguments[2] ? arguments[2] : 1500, RGraph.Effects.Line.Trace_callback); + } + + + + /** + * RoundRobin + * + * This effect does two things: + * 1. Gradually increases the size of each segment + * 2. Gradually increases the size of the radius from 0 + * + * @param object obj The graph object + */ + RGraph.Effects.Pie.RoundRobin = function (obj) + { + var callback = arguments[2] ? arguments[2] : null; + var opt = arguments[1]; + obj.canvas.style.visibility = 'hidden'; + obj.Draw(); + obj.__animation_frame__ = 0; + + var targetRadius = typeof(obj.Get('chart.radius')) == 'number' ? obj.Get('chart.radius') : obj.getRadius(); + RGraph.Clear(obj.canvas); + + obj.canvas.style.visibility = 'visible'; + + function RoundRobin_inner () + { + // Increase this for a longer animation, decrease it for a shorter animation + var numFrames = 90; + + obj.Set('chart.effect.roundrobin.multiplier', Math.pow(Math.sin((obj.__animation_frame__ * (90 / numFrames)) / (180 / Math.PI)), 2) * (obj.__animation_frame__ / numFrames) ); + + if (!opt || opt['radius']) { + obj.Set('chart.radius', targetRadius * obj.Get('chart.effect.roundrobin.multiplier')); + } + + RGraph.Clear(obj.canvas) + obj.Draw(); + + if (obj.__animation_frame__ < numFrames) { + obj.__animation_frame__ += 1; + RGraph.Effects.UpdateCanvas(RoundRobin_inner); + + } else if (callback) { + callback(obj); + } + } + + RGraph.Effects.UpdateCanvas(RoundRobin_inner); + } + + + /** + * Implode (pie chart) + * + * Here the segments are initially exploded - and gradually + * contract inwards to create the Pie chart + * + * @param object obj The Pie chart object + */ + RGraph.Effects.Pie.Implode = function (obj) + { + var numFrames = 90; + var distance = Math.min(obj.canvas.width, obj.canvas.height); + + function Implode_inner () + { + obj.Set('chart.exploded', Math.sin(numFrames / 57.3) * distance); + RGraph.Clear(obj.canvas) + obj.Draw(); + + if (numFrames > 0) { + numFrames--; + RGraph.Effects.UpdateCanvas(Implode_inner); + } else { + // Finish off the animation + obj.Set('chart.exploded', 0); + RGraph.Clear(obj.canvas); + obj.Draw(); + } + } + + RGraph.Effects.UpdateCanvas(Implode_inner); + } + + + /** + * Gauge Grow + * + * This effect gradually increases the represented value + * + * @param object obj The chart object + * @param Not used - pass null + * @param function An optional callback function + */ + RGraph.Effects.Gauge.Grow = function (obj) + { + var numFrames = 30; + var origValue = Number(obj.currentValue); + + if (obj.currentValue == null) { + obj.currentValue = obj.min; + origValue = obj.min; + } + + var newValue = obj.value; + var diff = newValue - origValue; + var step = (diff / numFrames); + + + function Grow () + { + if (obj.currentValue != newValue) { + obj.value = Number(obj.currentValue) + step; + } + + if (obj.value > obj.max) { + obj.value = obj.max; + } + + if (obj.value < obj.min) { + obj.value = obj.min; + } + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (numFrames-- > 0) { + //setTimeout(Grow, 100); + RGraph.Effects.UpdateCanvas(Grow); + } + } + + //setTimeout(Grow, 100); + RGraph.Effects.UpdateCanvas(Grow); + } + + + /** + * Radar chart grow + * + * This effect gradually increases the magnitude of the points on the radar chart + * + * @param object obj The chart object + * @param null Not used + * @param function An optional callback that is run when the effect is finished + */ + RGraph.Effects.Radar.Grow = function (obj) + { + var totalframes = 30; + var framenum = totalframes; + var data = obj.data; + var callback = arguments[2]; + obj.data = []; + + function Grow_inner () + { + for (var i=0; i<data.length; ++i) { + + if (obj.data[i] == null) { + obj.data[i] = []; + } + + for (var j=0; j<data[i].length; ++j) { + obj.data[i][j] = ((totalframes - framenum)/totalframes) * data[i][j]; + } + } + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (framenum > 0) { + framenum--; + RGraph.Effects.UpdateCanvas(Grow_inner); + } else if (typeof(callback) == 'function') { + callback(obj); + } + } + + RGraph.Effects.UpdateCanvas(Grow_inner); + } + + + /** + * Waterfall Grow + * + * @param object obj The chart object + * @param null Not used + * @param function An optional function which is called when the animation is finished + */ + RGraph.Effects.Waterfall.Grow = function (obj) + { + var totalFrames = 45; + var numFrame = 0; + var data = RGraph.array_clone(obj.data); + var callback = arguments[2]; + + //Reset The data to zeros + for (var i=0; i<obj.data.length; ++i) { + obj.data[i] /= totalFrames; + + } + + /** + * Fix the scale + */ + if (obj.Get('chart.ymax') == null) { + var max = RGraph.getScale(obj.getMax(data))[4] + obj.Set('chart.ymax', max); + } + + obj.Set('chart.multiplier.x', 0); + obj.Set('chart.multiplier.w', 0); + + function Grow_inner () + { + for (var i=0; i<obj.data.length; ++i) { + obj.data[i] = data[i] * (numFrame/totalFrames); + } + + var multiplier = Math.pow(Math.sin(((numFrame / totalFrames) * 90) / 57.3), 20); + obj.Set('chart.multiplier.x', (numFrame / totalFrames) * multiplier); + obj.Set('chart.multiplier.w', (numFrame / totalFrames) * multiplier); + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (numFrame++ < totalFrames) { + RGraph.Effects.UpdateCanvas(Grow_inner); + } else if (typeof(callback) == 'function') { + callback(obj); + } + } + + RGraph.Effects.UpdateCanvas(Grow_inner) + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.resizing.js b/schall/static/RGraph/libraries/RGraph.common.resizing.js new file mode 100644 index 0000000..ee96747 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.resizing.js @@ -0,0 +1,471 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + + /** + * This is an array of CSS properties that should be preserved when adding theplaceholder DIV + */ + __rgraph_resizing_preserve_css_properties__ = []; + + /** + * This function can be used to allow resizing + * + * @param object obj Your graph object + */ + RGraph.AllowResizing = function (obj) + { + if (obj.Get('chart.resizable')) { + var canvas = obj.canvas; + var context = obj.context; + var resizeHandle = 15; + RGraph.Resizing.canvas = canvas; + RGraph.Resizing.placeHolders = []; + + /** + * Add the original width and height to the canvas + */ + if (!canvas.__original_width__ && !canvas.__original_height__) { + canvas.__original_width__ = canvas.width; + canvas.__original_height__ = canvas.height; + } + + + var adjustX = (typeof(obj.Get('chart.resize.handle.adjust')) == 'object' && typeof(obj.Get('chart.resize.handle.adjust')[0]) == 'number' ? obj.Get('chart.resize.handle.adjust')[0] : 0); + var adjustY = (typeof(obj.Get('chart.resize.handle.adjust')) == 'object' && typeof(obj.Get('chart.resize.handle.adjust')[1]) == 'number' ? obj.Get('chart.resize.handle.adjust')[1] : 0); + + + /** + * Draw the resize handle + */ + var textWidth = context.measureText('Reset').width + 2; + + + // Draw the white background for the resize handle - OPTIONAL default is rgba(0,0,0,0); + var bgcolor = obj.Get('chart.resize.handle.background'); + + if (!bgcolor) { + bgcolor = 'rgba(0,0,0,0)'; + } + + context.beginPath(); + context.fillStyle = bgcolor; + context.moveTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - resizeHandle); + context.fillRect(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - resizeHandle + adjustY, 2 * resizeHandle, resizeHandle); + context.fill(); + + + obj.context.beginPath(); + obj.context.strokeStyle = 'gray'; + obj.context.fillStyle = 'rgba(0,0,0,0)'; + obj.context.lineWidth = 1; + obj.context.fillRect(obj.canvas.width - resizeHandle + adjustX, obj.canvas.height - resizeHandle - 2 + adjustY, resizeHandle, resizeHandle + 2); + obj.context.fillRect(obj.canvas.width - resizeHandle - textWidth + adjustX, obj.canvas.height - resizeHandle + adjustY, resizeHandle + textWidth, resizeHandle + 2); + + + // Draw the arrows + + // Vertical line + obj.context.moveTo(obj.canvas.width - (resizeHandle / 2) + adjustX, obj.canvas.height - resizeHandle + adjustY); + obj.context.lineTo(obj.canvas.width - (resizeHandle / 2) + adjustX, obj.canvas.height + adjustY); + + + // Horizontal line + obj.context.moveTo(obj.canvas.width + adjustX, obj.canvas.height - (resizeHandle / 2) + adjustY); + obj.context.lineTo(obj.canvas.width - resizeHandle + adjustX, obj.canvas.height - (resizeHandle / 2) + adjustY); + + context.fill(); + context.stroke(); + + + // Top arrow head + context.fillStyle = 'gray'; + context.beginPath(); + context.moveTo(canvas.width - (resizeHandle / 2) + adjustX, canvas.height - resizeHandle + adjustY); + context.lineTo(canvas.width - (resizeHandle / 2) + 3 + adjustX, canvas.height - resizeHandle + 3 + adjustY); + context.lineTo(canvas.width - (resizeHandle / 2) - 3 + adjustX, canvas.height - resizeHandle + 3 + adjustY); + context.closePath(); + context.fill(); + + // Bottom arrow head + context.beginPath(); + context.moveTo(canvas.width - (resizeHandle / 2) + adjustX, canvas.height + adjustY); + context.lineTo(canvas.width - (resizeHandle / 2) + 3 + adjustX, canvas.height - 3 + adjustY); + context.lineTo(canvas.width - (resizeHandle / 2) - 3 + adjustX, canvas.height - 3 + adjustY); + context.closePath(); + context.fill(); + + // Left arrow head + context.beginPath(); + context.moveTo(canvas.width - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY); + context.lineTo(canvas.width - resizeHandle + 3 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY); + context.lineTo(canvas.width - resizeHandle + 3 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY); + context.closePath(); + context.fill(); + + // Right arrow head + context.beginPath(); + context.moveTo(canvas.width + adjustX, canvas.height - (resizeHandle / 2) + adjustY); + context.lineTo(canvas.width - 3 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY); + context.lineTo(canvas.width - 3 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY); + context.closePath(); + context.fill(); + + // Square at the centre of the arrows + context.beginPath(); + context.fillStyle = 'white'; + context.moveTo(canvas.width + adjustX, canvas.height - (resizeHandle / 2) + adjustY); + context.strokeRect(canvas.width - (resizeHandle / 2) - 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY, 4, 4); + context.fillRect(canvas.width - (resizeHandle / 2) - 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY, 4, 4); + context.fill(); + context.stroke(); + + + // Draw the "Reset" button + context.beginPath(); + context.fillStyle = 'gray'; + context.moveTo(canvas.width - resizeHandle - 3 + adjustX, canvas.height - resizeHandle / 2 + adjustY); + context.lineTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY); + context.lineTo(canvas.width - resizeHandle - resizeHandle + 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY); + context.lineTo(canvas.width - resizeHandle - resizeHandle + 2 + adjustX, canvas.height - (resizeHandle / 2) + 2 + adjustY); + context.lineTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY); + context.stroke(); + context.fill(); + + context.beginPath(); + context.moveTo(canvas.width - resizeHandle - resizeHandle - 1 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY); + context.lineTo(canvas.width - resizeHandle - resizeHandle - 1 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY); + context.stroke(); + context.fill(); + + + + var window_onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = RGraph.Resizing.canvas; + var newWidth = RGraph.Resizing.originalw - (RGraph.Resizing.originalx - e.pageX);// - 5 + var newHeight = RGraph.Resizing.originalh - (RGraph.Resizing.originaly - e.pageY);// - 5 + + if (RGraph.Resizing.mousedown) { + if (newWidth > (canvas.__original_width__ / 2)) RGraph.Resizing.div.style.width = newWidth + 'px'; + if (newHeight > (canvas.__original_height__ / 2)) RGraph.Resizing.div.style.height = newHeight + 'px'; + + RGraph.FireCustomEvent(canvas.__object__, 'onresize'); + } + } + window.addEventListener('mousemove', window_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'window_mousemove', window_onmousemove); + + /** + * The window onmouseup function + */ + var MouseupFunc = function (e) + { + if (!RGraph.Resizing || !RGraph.Resizing.div || !RGraph.Resizing.mousedown) { + return; + } + + if (RGraph.Resizing.div) { + + var div = RGraph.Resizing.div; + var canvas = div.__canvas__; + var coords = RGraph.getCanvasXY(div.__canvas__); + + var parentNode = canvas.parentNode; + + if (canvas.style.position != 'absolute') { + // Create a DIV to go in the canvases place + var placeHolderDIV = document.createElement('DIV'); + placeHolderDIV.style.width = RGraph.Resizing.originalw + 'px'; + placeHolderDIV.style.height = RGraph.Resizing.originalh + 'px'; + //placeHolderDIV.style.backgroundColor = 'red'; + placeHolderDIV.style.display = 'inline-block'; // Added 5th Nov 2010 + placeHolderDIV.style.position = canvas.style.position; + placeHolderDIV.style.left = canvas.style.left; + placeHolderDIV.style.top = canvas.style.top; + placeHolderDIV.style.cssFloat = canvas.style.cssFloat; + + parentNode.insertBefore(placeHolderDIV, canvas); + } + + + // Now set the canvas to be positioned absolutely + canvas.style.backgroundColor = 'white'; + canvas.style.position = 'absolute'; + canvas.style.border = '1px dashed gray'; + canvas.style.left = (RGraph.Resizing.originalCanvasX - 1) + 'px'; + canvas.style.top = (RGraph.Resizing.originalCanvasY - 1) + 'px'; + + canvas.width = parseInt(div.style.width); + canvas.height = parseInt(div.style.height); + + + + /** + * Fire the onresize event + */ + RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw'); + + canvas.__object__.Draw(); + + // Get rid of transparent semi-opaque DIV + RGraph.Resizing.mousedown = false; + div.style.display = 'none'; + document.body.removeChild(div); + } + + /** + * If there is zoom enabled in thumbnail mode, lose the zoom image + */ + if (RGraph.Registry.Get('chart.zoomed.div') || RGraph.Registry.Get('chart.zoomed.img')) { + RGraph.Registry.Set('chart.zoomed.div', null); + RGraph.Registry.Set('chart.zoomed.img', null); + } + + /** + * Fire the onresize event + */ + RGraph.FireCustomEvent(canvas.__object__, 'onresizeend'); + } + + + var window_onmouseup = MouseupFunc; + window.addEventListener('onmouseup', window_onmouseup, false); + RGraph.AddEventListener(canvas.id, 'window_mouseup', window_onmouseup); + + + var canvas_onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var coords = RGraph.getMouseXY(e); + var canvas = e.target; + var context = canvas.getContext('2d'); + //var orig_cursor = canvas.style.cursor; // Used for playing well with tooltips + + RGraph.Resizing.title = canvas.title; + + if ( (coords[0] > (canvas.width - resizeHandle) + && coords[0] < canvas.width + && coords[1] > (canvas.height - resizeHandle) + && coords[1] < canvas.height)) { + + canvas.style.cursor = 'move'; + + if (navigator.userAgent.indexOf('Chrome') > 0 || document.all) { + canvas.title = 'Resize the graph'; + } + + } else if ( coords[0] > (canvas.width - resizeHandle - resizeHandle) + && coords[0] < canvas.width - resizeHandle + && coords[1] > (canvas.height - resizeHandle) + && coords[1] < canvas.height) { + + canvas.style.cursor = 'pointer'; + + if (navigator.userAgent.indexOf('Chrome') > 0 || document.all) { + canvas.title = 'Reset graph to original size'; + } + + } else { + + // orig_cursor + canvas.style.cursor = 'default'; + canvas.title = ''; + } + } + canvas.addEventListener('mousemove', canvas_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove); + + + + var canvas_onmouseout = function (e) + { + e.target.style.cursor = 'default'; + e.target.title = ''; + } + canvas.addEventListener('mouseout', canvas_onmouseout, false); + RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout); + + + + var canvas_onmousedown = function (e) + { + e = RGraph.FixEventObject(e); + + var coords = RGraph.getMouseXY(e); + var canvasCoords = RGraph.getCanvasXY(e.target); + var canvas = e.target; + + if ( coords[0] > (obj.canvas.width - resizeHandle) + && coords[0] < obj.canvas.width + && coords[1] > (obj.canvas.height - resizeHandle) + && coords[1] < obj.canvas.height) { + + RGraph.FireCustomEvent(obj, 'onresizebegin'); + + // Save the existing border + if (canvas.__original_css_border__ == null) { + canvas.__original_css_border__ = canvas.style.border; + } + + RGraph.Resizing.mousedown = true; + + + /** + * Create the semi-opaque DIV + */ + + var div = document.createElement('DIV'); + div.style.position = 'absolute'; + div.style.left = canvasCoords[0] + 'px'; + div.style.top = canvasCoords[1] + 'px'; + div.style.width = canvas.width + 'px'; + div.style.height = canvas.height + 'px'; + div.style.border = '1px dotted black'; + div.style.backgroundColor = 'gray'; + div.style.opacity = 0.5; + div.__canvas__ = e.target; + + document.body.appendChild(div); + RGraph.Resizing.div = div; + RGraph.Resizing.placeHolders.push(div); + + // Hide the previous resize indicator layers. This is only necessary it seems for the Meter chart + for (var i=0; i<(RGraph.Resizing.placeHolders.length - 1); ++i) { + RGraph.Resizing.placeHolders[i].style.display = 'none'; + } + + // This is a repetition of the window.onmouseup function (No need to use DOM2 here) + div.onmouseup = function (e) + { + MouseupFunc(e); + } + + + // No need to use DOM2 here + RGraph.Resizing.div.onmouseover = function (e) + { + e = RGraph.FixEventObject(e); + e.stopPropagation(); + } + + // The mouse + RGraph.Resizing.originalx = e.pageX; + RGraph.Resizing.originaly = e.pageY; + + RGraph.Resizing.originalw = obj.canvas.width; + RGraph.Resizing.originalh = obj.canvas.height; + + RGraph.Resizing.originalCanvasX = RGraph.getCanvasXY(obj.canvas)[0]; + RGraph.Resizing.originalCanvasY = RGraph.getCanvasXY(obj.canvas)[1]; + } + + + /** + * This facilitates the reset button + */ + if ( coords[0] > (canvas.width - resizeHandle - resizeHandle) + && coords[0] < canvas.width - resizeHandle + && coords[1] > (canvas.height - resizeHandle) + && coords[1] < canvas.height) { + + /** + * Fire the onresizebegin event + */ + RGraph.FireCustomEvent(canvas.__object__, 'onresizebegin'); + + // Restore the original width and height + canvas.width = canvas.__original_width__; + canvas.height = canvas.__original_height__; + + // Lose the border + canvas.style.border = canvas.__original_css_border__; + //canvas.__original_css_border__ = null; + + // Add 1 pixel to the top/left because the border is going + canvas.style.left = (parseInt(canvas.style.left)) + 'px'; + canvas.style.top = (parseInt(canvas.style.top)) + 'px'; + + + RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw'); + + // Redraw the canvas + canvas.__object__.Draw(); + + // Set the width and height on the DIV + if (RGraph.Resizing.div) { + RGraph.Resizing.div.style.width = canvas.__original_width__ + 'px'; + RGraph.Resizing.div.style.height = canvas.__original_height__ + 'px'; + } + + /** + * Fire the resize event + */ + RGraph.FireCustomEvent(canvas.__object__, 'onresize'); + RGraph.FireCustomEvent(canvas.__object__, 'onresizeend'); + } + } + canvas.addEventListener('mousedown', canvas_onmousedown, false); + RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown); + + + /** + * This function facilitates the reset button + * + * NOTE: 31st December 2010 - doesn't appear to be being used any more + */ + + /* + canvas.onclick = function (e) + { + var coords = RGraph.getMouseXY(e); + var canvas = e.target; + + if ( coords[0] > (canvas.width - resizeHandle - resizeHandle) + && coords[0] < canvas.width - resizeHandle + && coords[1] > (canvas.height - resizeHandle) + && coords[1] < canvas.height) { + + // Restore the original width and height + canvas.width = canvas.__original_width__; + canvas.height = canvas.__original_height__; + + // Lose the border + canvas.style.border = ''; + + // Add 1 pixel to the top/left because the border is going + canvas.style.left = (parseInt(canvas.style.left) + 1) + 'px'; + canvas.style.top = (parseInt(canvas.style.top) + 1) + 'px'; + + // Fire the onresizebeforedraw event + RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw'); + + // Redraw the canvas + canvas.__object__.Draw(); + + // Set the width and height on the DIV + RGraph.Resizing.div.style.width = canvas.__original_width__ + 'px'; + RGraph.Resizing.div.style.height = canvas.__original_height__ + 'px'; + + // Fire the resize event + RGraph.FireCustomEvent(canvas.__object__, 'onresize'); + } + } + */ + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.tooltips.js b/schall/static/RGraph/libraries/RGraph.common.tooltips.js new file mode 100644 index 0000000..3df3ded --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.tooltips.js @@ -0,0 +1,847 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + /** + * This is used in two functions, hence it's here + */ + RGraph.tooltips = {}; + RGraph.tooltips.padding = '3px'; + RGraph.tooltips.font_face = 'Tahoma'; + RGraph.tooltips.font_size = '10pt'; + + + /** + * Shows a tooltip next to the mouse pointer + * + * @param canvas object The canvas element object + * @param text string The tooltip text + * @param int x The X position that the tooltip should appear at. Combined with the canvases offsetLeft + * gives the absolute X position + * @param int y The Y position the tooltip should appear at. Combined with the canvases offsetTop + * gives the absolute Y position + * @param int idx The index of the tooltip in the graph objects tooltip array + */ + RGraph.Tooltip = function (canvas, text, x, y, idx) + { + /** + * chart.tooltip.override allows you to totally take control of rendering the tooltip yourself + */ + if (typeof(canvas.__object__.Get('chart.tooltips.override')) == 'function') { + return canvas.__object__.Get('chart.tooltips.override')(canvas, text, x, y, idx); + } + + /** + * This facilitates the "id:xxx" format + */ + text = RGraph.getTooltipTextFromDIV(text); + + /** + * First clear any exising timers + */ + var timers = RGraph.Registry.Get('chart.tooltip.timers'); + + if (timers && timers.length) { + for (i=0; i<timers.length; ++i) { + clearTimeout(timers[i]); + } + } + RGraph.Registry.Set('chart.tooltip.timers', []); + + /** + * Hide the context menu if it's currently shown + */ + if (canvas.__object__.Get('chart.contextmenu')) { + RGraph.HideContext(); + } + + // Redraw the canvas? + if (canvas.__object__.Get('chart.tooltips.highlight')) { + RGraph.Redraw(canvas.id); + } + + var effect = canvas.__object__.Get('chart.tooltips.effect').toLowerCase(); + + if (effect == 'snap' && RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__canvas__.id == canvas.id) { + + if ( canvas.__object__.type == 'line' + || canvas.__object__.type == 'radar' + || canvas.__object__.type == 'scatter' + || canvas.__object__.type == 'rscatter' + ) { + + var tooltipObj = RGraph.Registry.Get('chart.tooltip'); + + + tooltipObj.style.width = null; + tooltipObj.style.height = null; + + tooltipObj.innerHTML = text; + tooltipObj.__text__ = text; + + /** + * Now that the new content has been set, re-set the width & height + */ + RGraph.Registry.Get('chart.tooltip').style.width = RGraph.getTooltipWidth(text, canvas.__object__) + 'px'; + RGraph.Registry.Get('chart.tooltip').style.height = RGraph.Registry.Get('chart.tooltip').offsetHeight + 'px'; + + /** + * Now (25th September 2011) use jQuery if it's available + */ + if (typeof(jQuery) == 'function' && typeof($) == 'function') { + $('#' + tooltipObj.id).animate({ + opacity: 1, + width: tooltipObj.offsetWidth + 'px', + height: tooltipObj.offsetHeight + 'px', + left: x + 'px', + top: (y - tooltipObj.offsetHeight) + 'px' + }, 150); + } else { + var currentx = parseInt(RGraph.Registry.Get('chart.tooltip').style.left); + var currenty = parseInt(RGraph.Registry.Get('chart.tooltip').style.top); + + var diffx = x - currentx - ((x + RGraph.Registry.Get('chart.tooltip').offsetWidth) > document.body.offsetWidth ? RGraph.Registry.Get('chart.tooltip').offsetWidth : 0); + var diffy = y - currenty - RGraph.Registry.Get('chart.tooltip').offsetHeight; + + // Position the tooltip + setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.2)) + 'px"', 25); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.4)) + 'px"', 50); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.6)) + 'px"', 75); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.8)) + 'px"', 100); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 1.0)) + 'px"', 125); + + setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.2)) + 'px"', 25); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.4)) + 'px"', 50); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.6)) + 'px"', 75); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.8)) + 'px"', 100); + setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 1.0)) + 'px"', 125); + } + + } else { + + alert('[TOOLTIPS] The "snap" effect is only supported on the Line, Rscatter, Scatter and Radar charts (tried to use it with type: ' + canvas.__object__.type); + } + + /** + * Fire the tooltip event + */ + RGraph.FireCustomEvent(canvas.__object__, 'ontooltip'); + + return; + } + + /** + * Hide any currently shown tooltip + */ + RGraph.HideTooltip(); + + + /** + * Show a tool tip + */ + var tooltipObj = document.createElement('DIV'); + tooltipObj.className = canvas.__object__.Get('chart.tooltips.css.class'); + tooltipObj.style.display = 'none'; + tooltipObj.style.position = 'absolute'; + tooltipObj.style.left = 0; + tooltipObj.style.top = 0; + tooltipObj.style.backgroundColor = 'rgba(255,255,239,0.9)'; + tooltipObj.style.color = 'black'; + if (!document.all) tooltipObj.style.border = ''; + tooltipObj.style.visibility = 'visible'; + tooltipObj.style.paddingLeft = RGraph.tooltips.padding; + tooltipObj.style.paddingRight = RGraph.tooltips.padding; + tooltipObj.style.fontFamily = RGraph.tooltips.font_face; + tooltipObj.style.fontSize = RGraph.tooltips.font_size; + tooltipObj.style.zIndex = 3; + tooltipObj.style.borderRadius = '5px'; + tooltipObj.style.MozBorderRadius = '5px'; + tooltipObj.style.WebkitBorderRadius = '5px'; + tooltipObj.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px'; + tooltipObj.style.MozBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px'; + tooltipObj.style.boxShadow = 'rgba(96,96,96,0.5) 0 0 15px'; + tooltipObj.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)'; + tooltipObj.style.opacity = 0; + tooltipObj.style.overflow = 'hidden'; + tooltipObj.innerHTML = text; + tooltipObj.__text__ = text; // This is set because the innerHTML can change when it's set + tooltipObj.__canvas__ = canvas; + tooltipObj.style.display = 'inline'; + tooltipObj.id = '__rgraph_tooltip_' + canvas.id + '_' + idx; + + if (typeof(idx) == 'number') { + tooltipObj.__index__ = idx; + + /** + * Just for the line chart + */ + if (canvas.__object__.type == 'line') { + var index2 = idx; + + while (index2 >= canvas.__object__.data[0].length) { + index2 -= canvas.__object__.data[0].length; + } + + tooltipObj.__index2__ = index2; + } + } + + document.body.appendChild(tooltipObj); + + var width = tooltipObj.offsetWidth; + var height = tooltipObj.offsetHeight; + + if ((y - height - 2) > 0) { + y = y - height - 2; + } else { + y = y + 2; + } + + /** + * Set the width on the tooltip so it doesn't resize if the window is resized + */ + tooltipObj.style.width = width + 'px'; + //tooltipObj.style.height = 0; // Initially set the tooltip height to nothing + + /** + * If the mouse is towards the right of the browser window and the tooltip would go outside of the window, + * move it left + */ + if ( (x + width) > document.body.offsetWidth ) { + x = x - width - 7; + var placementLeft = true; + + if (canvas.__object__.Get('chart.tooltips.effect') == 'none') { + x = x - 3; + } + + tooltipObj.style.left = x + 'px'; + tooltipObj.style.top = y + 'px'; + + } else { + x += 5; + + tooltipObj.style.left = x + 'px'; + tooltipObj.style.top = y + 'px'; + } + + + if (effect == 'expand') { + + tooltipObj.style.left = (x + (width / 2)) + 'px'; + tooltipObj.style.top = (y + (height / 2)) + 'px'; + leftDelta = (width / 2) / 10; + topDelta = (height / 2) / 10; + + tooltipObj.style.width = 0; + tooltipObj.style.height = 0; + //tooltipObj.style.boxShadow = ''; + //tooltipObj.style.MozBoxShadow = ''; + //tooltipObj.style.WebkitBoxShadow = ''; + //tooltipObj.style.borderRadius = 0; + //tooltipObj.style.MozBorderRadius = 0; + //tooltipObj.style.WebkitBorderRadius = 0; + tooltipObj.style.opacity = 1; + + // Progressively move the tooltip to where it should be (the x position) + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 250)); + + // Progressively move the tooltip to where it should be (the Y position) + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 250)); + + // Progressively grow the tooltip width + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.1) + "px'; }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.2) + "px'; }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.3) + "px'; }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.4) + "px'; }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.5) + "px'; }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.6) + "px'; }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.7) + "px'; }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.8) + "px'; }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.9) + "px'; }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250)); + + // Progressively grow the tooltip height + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.1) + "px'; }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.2) + "px'; }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.3) + "px'; }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.4) + "px'; }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.5) + "px'; }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.6) + "px'; }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.7) + "px'; }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.8) + "px'; }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.9) + "px'; }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250)); + + // When the animation is finished, set the tooltip HTML + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250)); + + } else if (effect == 'contract') { + + tooltipObj.style.left = (x - width) + 'px'; + tooltipObj.style.top = (y - (height * 2)) + 'px'; + tooltipObj.style.cursor = 'pointer'; + + leftDelta = width / 10; + topDelta = height / 10; + + tooltipObj.style.width = (width * 5) + 'px'; + tooltipObj.style.height = (height * 5) + 'px'; + + tooltipObj.style.opacity = 0.2; + + // Progressively move the tooltip to where it should be (the x position) + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 250)); + + // Progressively move the tooltip to where it should be (the Y position) + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 250)); + + // Progressively shrink the tooltip width + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.5) + "px'; }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.0) + "px'; }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.5) + "px'; }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.0) + "px'; }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.5) + "px'; }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.0) + "px'; }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.5) + "px'; }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.0) + "px'; }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 1.5) + "px'; }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250)); + + // Progressively shrink the tooltip height + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.5) + "px'; }", 25)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.0) + "px'; }", 50)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.5) + "px'; }", 75)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.0) + "px'; }", 100)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.5) + "px'; }", 125)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.0) + "px'; }", 150)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.5) + "px'; }", 175)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.0) + "px'; }", 200)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 1.5) + "px'; }", 225)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250)); + + // When the animation is finished, set the tooltip HTML + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250)); + + /** + * This resets the pointer + */ + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.cursor = 'default'; }", 275)); + + } else if (effect == 'snap') { + + /******************************************************* + * Move the tooltip + *******************************************************/ + for (var i=1; i<=10; ++i) { + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = '" + (x * 0.1 * i) + "px'; }", 15 * i)); + RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = '" + (y * 0.1 * i) + "px'; }", 15 * i)); + } + + tooltipObj.style.left = 0 - tooltipObj.offsetWidth + 'px'; + tooltipObj.style.top = 0 - tooltipObj.offsetHeight + 'px'; + + } else if (effect != 'fade' && effect != 'expand' && effect != 'none' && effect != 'snap' && effect != 'contract') { + alert('[COMMON] Unknown tooltip effect: ' + effect); + } + + if (effect != 'none') { + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.1; }", 25); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.2; }", 50); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.3; }", 75); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.4; }", 100); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.5; }", 125); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.6; }", 150); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.7; }", 175); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.8; }", 200); + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.9; }", 225); + } + + setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 1;}", effect == 'none' ? 50 : 250); + + /** + * If the tooltip it self is clicked, cancel it + */ + tooltipObj.onmousedown = function (e) + { + e = RGraph.FixEventObject(e) + e.stopPropagation(); + } + + tooltipObj.onclick = function (e) + { + if (e.button == 0) { + e = RGraph.FixEventObject(e); + e.stopPropagation(); + } + } + + /** + * Install the function for hiding the tooltip. + */ + document.body.onmousedown = function (event) + { + var tooltip = RGraph.Registry.Get('chart.tooltip'); + + if (tooltip) { + RGraph.HideTooltip(); + + // Redraw if highlighting is enabled + if (tooltip.__canvas__.__object__.Get('chart.tooltips.highlight')) { + RGraph.Redraw(); + } + } + } + + /** + * If the window is resized, hide the tooltip + */ + window.onresize = function () + { + var tooltip = RGraph.Registry.Get('chart.tooltip'); + + if (tooltip) { + tooltip.parentNode.removeChild(tooltip); + tooltip.style.display = 'none'; + tooltip.style.visibility = 'hidden'; + RGraph.Registry.Set('chart.tooltip', null); + + // Redraw the graph if necessary + if (canvas.__object__.Get('chart.tooltips.highlight')) { + RGraph.Clear(canvas); + canvas.__object__.Draw(); + } + } + } + + /** + * Keep a reference to the tooltip + */ + RGraph.Registry.Set('chart.tooltip', tooltipObj); + + /** + * Fire the tooltip event + */ + RGraph.FireCustomEvent(canvas.__object__, 'ontooltip'); + } + + + /** + * + */ + RGraph.getTooltipTextFromDIV = function (text) + { + var result = /^id:(.*)/.exec(text); + + if (result && result[1] && document.getElementById(result[1])) { + text = document.getElementById(result[1]).innerHTML; + } + + return text; + } + + + /** + * This function handles the tooltip text being a string, function, or an array of functions. + */ + RGraph.parseTooltipText = function (tooltips, idx) + { + // Get the tooltip text + if (typeof(tooltips) == 'function') { + var text = tooltips(idx); + + } else if (typeof(tooltips) == 'object' && tooltips && typeof(tooltips[idx]) == 'function') { + var text = tooltips[idx](idx); + + } else if (typeof(tooltips) == 'object' && tooltips) { + var text = String(tooltips[idx]); + + } else { + var text = ''; + } + + if (text == 'undefined') { + text = ''; + } + + return text; + } + + + /** + * + */ + RGraph.getTooltipWidth = function (text, obj) + { + var div = document.createElement('DIV'); + div.className = obj.Get('chart.tooltips.css.class'); + div.style.paddingLeft = RGraph.tooltips.padding; + div.style.paddingRight = RGraph.tooltips.padding; + div.style.fontFamily = RGraph.tooltips.font_face; + div.style.fontSize = RGraph.tooltips.font_size; + div.style.visibility = 'hidden'; + div.style.position = 'absolute'; + div.style.top = '300px'; + div.style.left = 0; + div.style.display = 'inline'; + div.innerHTML = RGraph.getTooltipTextFromDIV(text); + document.body.appendChild(div); + + return div.offsetWidth; + } + + + /** + * Hides the currently shown tooltip + */ + RGraph.HideTooltip = function () + { + var tooltip = RGraph.Registry.Get('chart.tooltip'); + + if (tooltip) { + tooltip.parentNode.removeChild(tooltip); + tooltip.style.display = 'none'; + tooltip.style.visibility = 'hidden'; + RGraph.Registry.Set('chart.tooltip', null); + } + } + + + + + + + /** + * The BAR chart onmousemove event Bar chart tooltips can now + * be based around the onmousemove event + */ + RGraph.InstallBarTooltipEventListeners = function (obj) + { + /** + * Install the window onclick handler + */ + var window_onclick_func = function (){RGraph.Redraw();}; + window.addEventListener('click', window_onclick_func, false); + RGraph.AddEventListener('window_' + obj.id, 'click', window_onclick_func); + + + var canvas_onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(e.target.id); + var obj = canvas.__object__; + + if (obj.__bar__) { + var lineObj = obj; + obj = obj.__bar__; + obj.__line__ = lineObj; + } + + var barCoords = obj.getBar(e); + + /** + * If there are bar coords AND the bar has height + */ + + if (barCoords && barCoords[4] > 0) { + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), barCoords[5]); + + + if (text) { + canvas.style.cursor = 'pointer'; + } else { + canvas.style.cursor = 'default'; + } + + /** + * Hide the currently displayed tooltip if the index is the same + */ + if ( RGraph.Registry.Get('chart.tooltip') + && RGraph.Registry.Get('chart.tooltip').__canvas__.id != obj.id + && obj.Get('chart.tooltips.event') == 'onmousemove') { + + RGraph.Redraw(); + RGraph.HideTooltip(); + } + + /** + * This facilitates the tooltips using the onmousemove event + */ + + if ( obj.Get('chart.tooltips.event') == 'onmousemove' + && ( + (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ != barCoords[5]) + || !RGraph.Registry.Get('chart.tooltip') + ) + && text) { + /** + * Show a tooltip if it's defined + */ + RGraph.Redraw(obj); + + if (obj.Get('chart.tooltips.highlight')) { + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); + obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); + + obj.context.stroke(); + obj.context.fill(); + } + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]); + } + } else if (obj.__line__ && obj.__line__.getPoint && obj.__line__.getPoint(e)) { + canvas.style.cursor = 'pointer'; + } else { + canvas.style.cursor = 'default'; + } + } + RGraph.AddEventListener(obj.id, 'mousemove', canvas_onmousemove); + obj.canvas.addEventListener('mousemove', canvas_onmousemove, false); + + + + /** + * Install the onclick event handler for the tooltips + */ + if (obj.Get('chart.tooltips.event') == 'onclick') { + + var canvas_onclick = function (e) + { + var e = RGraph.FixEventObject(e); + + // If the button pressed isn't the left, we're not interested + if (e.button != 0) return; + + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + + if (obj.__bar__) { + obj = obj.__bar__; + } + + var barCoords = obj.getBar(e); + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Redraw(); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + if (barCoords) { + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), barCoords[5]); + + /** + * Show a tooltip if it's defined + */ + if (text && text != 'undefined') { + if (obj.Get('chart.tooltips.highlight')) { + + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); + obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]); + obj.context.stroke(); + obj.context.fill(); + } + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]); + } + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + } + RGraph.AddEventListener(obj.id, 'click', canvas_onclick); + obj.canvas.addEventListener('click', canvas_onclick, false); + } + } + + + + /** + * The LINE chart tooltips event handlers + * + * @param object obj The graph object + */ + RGraph.InstallLineTooltipEventListeners = function (obj) + { + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var point = obj.getPoint(e); + + if (obj.Get('chart.tooltips.highlight')) { + RGraph.Register(obj); + } + + if ( point + && typeof(point[0]) == 'object' + && typeof(point[1]) == 'number' + && typeof(point[2]) == 'number' + && typeof(point[3]) == 'number' + ) { + + // point[0] is the graph object + var xCoord = point[1]; + var yCoord = point[2]; + var idx = point[3]; + + if ((obj.Get('chart.tooltips')[idx] || typeof(obj.Get('chart.tooltips')) == 'function')) { + + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx); + + + // No tooltip text - do nada + if (text.match(/^id:(.*)$/) && RGraph.getTooltipTextFromDIV(text).substring(0,3) == 'id:') { + return; + } + + // Chnage the pointer to a hand + canvas.style.cursor = 'pointer'; + + /** + * If the tooltip is the same one as is currently visible (going by the array index), don't do squat and return. + */ + + if ( RGraph.Registry.Get('chart.tooltip') + && RGraph.Registry.Get('chart.tooltip').__index__ == idx + && RGraph.Registry.Get('chart.tooltip').__canvas__.id == canvas.id + && RGraph.Registry.Get('chart.tooltip').__event__ + && RGraph.Registry.Get('chart.tooltip').__event__ == 'mousemove') { + + return; + } + + /** + * Redraw the graph + */ + if (obj.Get('chart.tooltips.highlight') || obj.__bar__) { + + RGraph.Redraw(); + + if (obj.__bar__) { + RGraph.Clear(obj.canvas); + obj.__bar__.Draw(); + } + } + + // SHOW THE CORRECT TOOLTIP + RGraph.Tooltip(canvas, text, e.pageX, obj.Get('chart.tooltips.hotspot.xonly') ? (point[2] + RGraph.getCanvasXY(canvas)[1]) : e.pageY, idx); + + // Store the tooltip index on the tooltip object + RGraph.Registry.Get('chart.tooltip').__index__ = Number(idx); + + /** + * This converts idx into the index that is not greater than the + * number of items in the data array + */ + while (idx >= obj.data[0].length) { + idx -= obj.data[0].length + } + + RGraph.Registry.Get('chart.tooltip').__index2__ = idx; + + /** + * Set the source event + */ + RGraph.Registry.Get('chart.tooltip').__event__ = 'mousemove'; + + /** + * Highlight the graph + */ + if (obj.Get('chart.tooltips.highlight')) { + + context.beginPath(); + context.moveTo(xCoord, yCoord); + context.arc(xCoord, yCoord, 2, 0, 6.28, 0); + context.strokeStyle = obj.Get('chart.highlight.stroke'); + context.fillStyle = obj.Get('chart.highlight.fill'); + context.stroke(); + context.fill(); + } + + e.stopPropagation(); + return; + } + } + + /** + * Not over a hotspot? + */ + canvas.style.cursor = 'default'; + } + obj.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(obj.id, 'mousemove', canvas_onmousemove_func); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.common.zoom.js b/schall/static/RGraph/libraries/RGraph.common.zoom.js new file mode 100644 index 0000000..2f797b9 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.common.zoom.js @@ -0,0 +1,886 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'}; + + + /** + * Installs the event handlers for zooming an area of the graph + * + * @param object obj Your graph object + */ + RGraph.ZoomArea = function (obj) + { + if (obj.Get('chart.zoom.mode') == 'area') { + + var canvas = obj.canvas; + var context = obj.context; + + obj.canvas.style.cursor = 'crosshair'; + + RGraph.Register(obj); + + + canvas.onmousedown = function(e) + { + var canvas = e.target; + var coords = RGraph.getMouseXY(e); + var obj = canvas.__object__; + + if (RGraph.Registry.Get('chart.zoomed.area.div')) { + RGraph.Registry.Get('chart.zoomed.area.div').style.display = 'none'; + } + + if (typeof(RGraph.Registry.Get('chart.zoomed.area.divs')) == null) { + RGraph.Registry.Set('chart.zoomed.area.divs', []); + + } else { + + var divs = RGraph.Registry.Get('chart.zoomed.area.divs'); + + if (divs && divs.length) { + for (var i=0; i<divs.length; ++i) { + if (divs[i]) { + divs[i].style.display = 'none'; + divs[i] = null; + } + } + } + } + + /** + * Create the indicator DIV + */ + var canvasXY = RGraph.getCanvasXY(canvas); + var areaDiv = document.createElement('DIV'); + areaDiv.__canvas__ = canvas; + areaDiv.style.position = 'absolute'; + areaDiv.style.left = canvasXY[0] + coords[0] + 'px' + areaDiv.style.top = canvasXY[1] + coords[1] + 'px' + areaDiv.style.width = 0; + areaDiv.style.height = 0; + areaDiv.style.border = '1px solid black'; + areaDiv.style.backgroundColor = 'rgba(220,220,220,0.3)'; + areaDiv.style.display = 'none'; + + areaDiv.onmouseover = function (e) + { + setTimeout(function () {e.target.style.opacity = 0.8;}, 50); + setTimeout(function () {e.target.style.opacity = 0.6;}, 100); + setTimeout(function () {e.target.style.opacity = 0.4;}, 150); + setTimeout(function () {e.target.style.opacity = 0.2;}, 200); + setTimeout(function () {e.target.style.opacity = 0;}, 250); + setTimeout(function () {e.target.style.display = 'none';}, 300); + setTimeout(function () {e.target = null;}, 350); + } + + document.body.appendChild(areaDiv); + + + RGraph.Registry.Set('chart.zoomed.area.div', null); + RGraph.Registry.Set('chart.zoomed.area.mousedown', coords); + RGraph.Registry.Set('chart.zoomed.area.areadiv', areaDiv); + } + + + var window_onmousemove = function (e) + { + var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown'); + + if (startCoords && startCoords.length) { + + var coords = RGraph.getMouseXY(e); + var canvasXY = RGraph.getCanvasXY(e.target); + var obj = e.target.__object__; + var context = obj.context; + var canvas = obj.canvas; + var startx = startCoords[0]; + var starty = startCoords[1]; + var endx = coords[0]; + var endy = coords[1]; + var width = endx - startx; + var height = endy - starty; + var areaDiv = RGraph.Registry.Get('chart.zoomed.area.areadiv'); + + if (width >= 0 && height >= 0) { + + if (width > 5 && height > 5) { + areaDiv.style.width = (width - 15) + 'px'; + areaDiv.style.height = (height - 15) + 'px'; + areaDiv.style.display = 'inline'; + } else { + areaDiv.style.display = 'none'; + } + + } else if (width < -5 || height < -5) { + + var canvasCoords = RGraph.getCanvasXY(canvas); + var noticeDiv = document.createElement('DIV'); + + noticeDiv.style.position = 'absolute'; + noticeDiv.style.top = 0; + noticeDiv.style.left = 0; + noticeDiv.style.zIndex = 99; + noticeDiv.style.border = '1px dashed black'; + noticeDiv.style.backgroundColor = '#ffc1c1'; + noticeDiv.style.MozBoxShadow = '0 0 5px #999'; + noticeDiv.style.WebkitBoxShadow = '0 0 5px #999'; + noticeDiv.style.boxShadow = '0 0 5px #999'; + noticeDiv.style.padding = '2px'; + noticeDiv.innerHTML = 'Zoom goes right and down'; + document.body.appendChild(noticeDiv); + + // Reposition the warning + noticeDiv.style.top = canvasCoords[1] + startCoords[1] - noticeDiv.offsetHeight + 'px'; + noticeDiv.style.left = canvasCoords[0] + startCoords[0] - (noticeDiv.offsetWidth / 2) + 'px'; + noticeDiv.style.width = noticeDiv.offsetWidth + 'px'; + + setTimeout(function () {noticeDiv.style.opacity = 0.8;}, 2100); + setTimeout(function () {noticeDiv.style.opacity = 0.6;}, 2200); + setTimeout(function () {noticeDiv.style.opacity = 0.4;}, 2300); + setTimeout(function () {noticeDiv.style.opacity = 0.2;}, 2400); + setTimeout(function () {noticeDiv.style.display = 'none';}, 2500); + setTimeout(function () {noticeDiv = null;}, 2600); + } + } + } + window.addEventListener('mousemove', window_onmousemove, false); + RGraph.AddEventListener(canvas.id, 'window_mousemove', window_onmousemove); + + + canvas.onmouseup = function (e) + { + RGraph.FixEventObject(e); + + var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown'); + var coords = RGraph.getMouseXY(e); + + // Do the zooming here + if (RGraph.Registry.Get('chart.zoomed.area.mousedown')) { + + //RGraph.Redraw(); + + RGraph.Registry.Get('chart.zoomed.area.areadiv').style.display = 'none'; + RGraph.Registry.Get('chart.zoomed.area.areadiv').style.left = '-1000px'; + RGraph.Registry.Get('chart.zoomed.area.areadiv').style.top = '-1000px'; + RGraph.Registry.Set('chart.zoomed.area.areadiv', null); + + if (coords[0] < startCoords[0] || coords[1] < startCoords[1]) { + RGraph.Registry.Set('chart.zoomed.area.mousedown', false); + return; + } + + var canvas = e.target; + var obj = canvas.__object__; + var context = obj.context; + var canvasCoords = RGraph.getCanvasXY(e.target); + var coords = RGraph.getMouseXY(e); + var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown'); + var startx = startCoords[0]; + var starty = startCoords[1]; + var endx = coords[0] - 15; + var endy = coords[1] - 15; + var width = Math.abs(endx - startx); + var height = Math.abs(endy - starty); + var factor = obj.Get('chart.zoom.factor') - 1; + + var img = document.createElement('IMG'); + img.src = canvas.toDataURL(); + img.style.position = 'relative'; + img.style.left = (factor + 1) * -1 * startx + 'px'; + img.style.top = (factor + 1) * -1 * starty + 'px'; + img.width = (factor + 1) * canvas.width; + img.height = (factor + 1) * canvas.height; + + var div = document.createElement('DIV'); + div.__object__ = obj; + div.className = 'RGraph_zoomed_area'; + div.style.position = 'absolute'; + div.style.backgroundColor = 'white'; + div.style.cursor = 'move'; + + div.style.top = e.pageY + 'px'; + div.style.left = e.pageX + 'px'; + div.origWidth = width; + div.origHeight = height; + div.style.width = width + 'px'; + div.style.height = height + 'px'; + div.style.border = '1px solid black'; + div.style.boxShadow = '0 0 15px black'; + div.style.MozBoxShadow = '0 0 15px black'; + div.style.WebkitBoxShadow = '0 0 15px black'; + div.style.overflow = 'hidden'; + div.style.opacity = 0; + div.style.zIndex = 99; + + document.body.appendChild(div); + div.appendChild(img); + + + /** + * This facilitates expanding the zoomed area once visible (the double click) + */ + div.ondblclick = function (event) + { + var event = RGraph.FixEventObject(event); + var img = event.target; + var div = event.target.parentNode; + + var current_width = div.offsetWidth + var current_height = div.offsetHeight + var target_width = img.offsetWidth; + var target_height = img.offsetHeight; + var diff_width = target_width - current_width; + var diff_height = target_height - current_height; + + var img_offset_left = parseInt(img.offsetLeft); + var img_offset_top = parseInt(img.offsetTop); + + // Global vars on purpose so the timers can access them + __zoomed_div_style__ = div.style; + __zoomed_img_style__ = img.style; + + setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 50); + setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 100); + setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 150); + setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 200); + setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 250); + + setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 50); + setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 100); + setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 150); + setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 200); + setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 250); + + setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 50); + setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 100); + setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 150); + setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 200); + setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 250); + + setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 50); + setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 100); + setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 150); + setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 200); + setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 250); + + // Move the image within the div + setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.8) + " + 'px'", 50); + setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.6) + " + 'px'", 100); + setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.4) + " + 'px'", 150); + setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.2) + " + 'px'", 200); + setTimeout("__zoomed_img_style__.left = 0", 250); + + setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.8) + " + 'px'", 50); + setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.6) + " + 'px'", 100); + setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.4) + " + 'px'", 150); + setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.2) + " + 'px'", 200); + setTimeout("__zoomed_img_style__.top = 0", 250); + + div.ondblclick = null; + } + /** + * Make the zoomed bit draggable + */ + div.onmousedown = function (e) + { + e = RGraph.FixEventObject(e); + + var div = e.target.parentNode; + var img = div.childNodes[0]; + + if (e.button == 0 || e.button == 1 ) { + + div.__offsetx__ = e.offsetX + img.offsetLeft; + div.__offsety__ = e.offsetY + img.offsetTop; + + RGraph.Registry.Set('chart.mousedown', div); + RGraph.Registry.Set('chart.button', 0); + + } else { + + img.__startx__ = e.pageX; + img.__starty__ = e.pageY; + + img.__originalx__ = img.offsetLeft; + img.__originaly__ = img.offsetTop; + + RGraph.Registry.Set('chart.mousedown', img); + RGraph.Registry.Set('chart.button', 2); + + /** + * Don't show a context menu when the zoomed area is right-clicked on + */ + window.oncontextmenu = function (e) + { + e = RGraph.FixEventObject(e); + + e.stopPropagation(); + + // [18th July 2010] Is this reallly necessary? + window.oncontextmenu = function (e) + { + return true; + } + + + return false; + } + } + + e.stopPropagation(); + + return false; + } + + window.onmouseup = function (e) + { + RGraph.Registry.Set('chart.mousedown', false); + } + + window.onmousemove = function (e) + { + if (RGraph.Registry.Get('chart.mousedown') && RGraph.Registry.Get('chart.button') == 0) { + + var div = RGraph.Registry.Get('chart.mousedown'); + + var x = e.pageX - div.__offsetx__; + var y = e.pageY - div.__offsety__; + + div.style.left = x + 'px'; + div.style.top = y + 'px'; + + } else if (RGraph.Registry.Get('chart.mousedown') && RGraph.Registry.Get('chart.button') == 2) { + + var img = RGraph.Registry.Get('chart.mousedown'); + + var x = img.__originalx__ + e.pageX - img.__startx__; + var y = img.__originaly__ + e.pageY - img.__starty__; + + img.style.left = x + 'px'; + img.style.top = y + 'px'; + } + } + // End dragging code + + + var divs = RGraph.Registry.Get('chart.zoomed.area.divs'); + if (typeof(divs) == 'object') { + divs.push(div); + } else { + RGraph.Registry.Set('chart.zoomed.area.divs', [div]) + } + + // Create the background + var bg = document.createElement('DIV'); + bg.style.position = 'fixed' + bg.style.zIndex = 98; + bg.style.top = 0; + bg.style.left = 0; + bg.style.backgroundColor = '#999'; + bg.style.opacity = 0; + bg.style.width = (screen.width + 100) + 'px'; + bg.style.height = (screen.height + 100) + 'px'; + document.body.appendChild(bg); + + bg.onclick = function (e) + { + div.style.display = 'none'; + bg.style.display = 'none'; + div = null; + bg = null; + } + + + setTimeout(function (){div.style.opacity = 0.2;}, 50); + setTimeout(function (){div.style.opacity = 0.4;}, 100); + setTimeout(function (){div.style.opacity = 0.6;}, 150); + setTimeout(function (){div.style.opacity = 0.8;}, 200); + setTimeout(function (){div.style.opacity = 1.0;}, 250); + + setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.1) + 'px'}, 50); + setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.2) + 'px'}, 100); + setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.3) + 'px'}, 150); + setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.4) + 'px'}, 200); + setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.5) + 'px'}, 250); + + setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.1) + 'px'}, 50); + setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.2) + 'px'}, 100); + setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.3) + 'px'}, 150); + setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.4) + 'px'}, 200); + setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.5) + 'px'}, 250); + + setTimeout(function () {div.style.width = width + (width * factor * 0.2) + 'px'}, 50); + setTimeout(function () {div.style.width = width + (width * factor * 0.4) + 'px'}, 100); + setTimeout(function () {div.style.width = width + (width * factor * 0.6) + 'px'}, 150); + setTimeout(function () {div.style.width = width + (width * factor * 0.8) + 'px'}, 200); + setTimeout(function () {div.style.width = width + (width * factor * 1.0) + 'px'}, 250); + + setTimeout(function () {div.style.height = height + (height * factor * 0.2) + 'px'}, 50); + setTimeout(function () {div.style.height = height + (height * factor * 0.4) + 'px'}, 100); + setTimeout(function () {div.style.height = height + (height * factor * 0.6) + 'px'}, 150); + setTimeout(function () {div.style.height = height + (height * factor * 0.8) + 'px'}, 200); + setTimeout(function () {div.style.height = height + (height * factor * 1.0) + 'px'}, 250); + + setTimeout(function (){bg.style.opacity = 0.1;}, 50); + setTimeout(function (){bg.style.opacity = 0.2;}, 100); + setTimeout(function (){bg.style.opacity = 0.3;}, 150); + setTimeout(function (){bg.style.opacity = 0.4;}, 200); + setTimeout(function (){bg.style.opacity = 0.5;}, 250); + + RGraph.Registry.Set('chart.zoomed.area.bg', bg); + RGraph.Registry.Set('chart.zoomed.area.img', img); + RGraph.Registry.Set('chart.zoomed.area.div', div); + RGraph.Registry.Set('chart.zoomed.area.mousedown', null); + } + + /** + * Fire the zoom event + */ + RGraph.FireCustomEvent(obj, 'onzoom'); + } + + canvas.onmouseout = function (e) + { + RGraph.Registry.Set('chart.zoomed.area.areadiv', null); + RGraph.Registry.Set('chart.zoomed.area.mousedown', null); + RGraph.Registry.Set('chart.zoomed.area.div', null); + } + } + } + + + /** + * This function sets up the zoom window if requested + * + * @param obj object The graph object + */ + RGraph.ShowZoomWindow = function (obj) + { + if (obj.Get('chart.zoom.mode') == 'thumbnail') { + RGraph.ZoomWindow(obj.canvas); + } + + if (obj.Get('chart.zoom.mode') == 'area') { + RGraph.ZoomArea(obj); + } + } + + + /** + * Installs the evnt handler for the zoom window/THUMBNAIL + */ + RGraph.ZoomWindow = function (canvas) + { + canvas.onmousemove = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var coords = RGraph.getMouseXY(e); + + /** + * Create the DIV + */ + if (!RGraph.Registry.Get('chart.zoomed.div')) { + + var div = document.createElement('div'); + div.className = 'RGraph_zoom_window'; + div.style.width = obj.Get('chart.zoom.thumbnail.width') + 'px'; + div.style.height = obj.Get('chart.zoom.thumbnail.height') + 'px'; + + // Added back in on the 17th December + div.style.border = '2px dashed gray'; + + div.style.position = 'absolute'; + div.style.overflow = 'hidden'; + div.style.backgroundColor = 'white'; + + // Initially the zoomed layer should be off-screen + div.style.left = '-1000px'; + div.style.top = '-1000px'; + + // Should these be 0? No. + div.style.borderRadius = '5px'; + div.style.MozBorderRadius = '5px'; + div.style.WebkitBorderRadius = '5px'; + + if (obj.Get('chart.zoom.shadow')) { + div.style.boxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px'; + div.style.MozBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px'; + div.style.WebkitBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px'; + } + + //div.style.opacity = 0.2; + div.__object__ = obj; + document.body.appendChild(div); + + /** + * Get the canvas as an image + */ + var img = document.createElement('img'); + img.width = obj.canvas.width * obj.Get('chart.zoom.factor'); + img.height = obj.canvas.height * obj.Get('chart.zoom.factor'); + img.style.position = 'relative'; + img.style.backgroundColor = 'white'; + img.__object__ = obj; + + div.appendChild(img); + + RGraph.Registry.Set('chart.zoomed.div', div); + RGraph.Registry.Set('chart.zoomed.img', img); + + // Fade the zoom in + setTimeout("RGraph.Registry.Get('chart.zoomed.div').__object__.canvas.onmouseover()", 5); + + } else { + + div = RGraph.Registry.Get('chart.zoomed.div'); + img = RGraph.Registry.Get('chart.zoomed.img'); + } + + // Make sure the image is up-to-date + img.src = canvas.toDataURL(); + + /** + * Ensure the div is visible + */ + if (div && div.style.opacity < 1) { + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400); + } + + /** + * Get the canvas x/y coords + */ + var c = RGraph.getCanvasXY(obj.canvas); + var x = c[0]; + var y = c[1]; + + /** + * Position the div and img + */ + var offset = 7; + + div.style.left = (e.pageX - obj.Get('chart.zoom.thumbnail.width') - offset) + 'px'; + div.style.top = (e.pageY - obj.Get('chart.zoom.thumbnail.height') - offset) + 'px'; + + var l = (obj.Get('chart.zoom.thumbnail.width') / 2) - (coords[0] * obj.Get('chart.zoom.factor')); + var t = (obj.Get('chart.zoom.thumbnail.height') / 2) - (coords[1] * obj.Get('chart.zoom.factor')); + + // More positioning + img.style.left = (l + ((obj.Get('chart.zoom.thumbnail.width') / 2) * obj.Get('chart.zoom.factor'))) + 'px'; + img.style.top = (t + ((obj.Get('chart.zoom.thumbnail.height') / 2) * obj.Get('chart.zoom.factor'))) + 'px'; + + /** + * Fire the onzoom event + */ + RGraph.FireCustomEvent(obj, 'onzoom'); + } + + /** + * The onmouseover event. Evidently. Fades the zoom window in + */ + canvas.onmouseover = function (e) + { + var div = RGraph.Registry.Get('chart.zoomed.div'); + + // ??? + if (!div) return; + + var obj = div.__object__; + + // Used for the enlargement animation + var targetWidth = obj.Get('chart.zoom.thumbnail.width'); + var targetHeight = obj.Get('chart.zoom.thumbnail.height'); + + div.style.width = 0; + div.style.height = 0; + + if (obj.Get('chart.zoom.fade.in')) { + + RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2; + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 100); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 200); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8", 300); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400); + + } else { + + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 1); + } + + // The enlargement animation frames + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (1/5) ) + "px'", 75); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (2/5) ) + "px'", 150); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (3/5) ) + "px'", 225); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (4/5) ) + "px'", 300); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (5/5) ) + "px'", 325); + + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (1/5) ) + "px'", 75); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (2/5) ) + "px'", 150); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (3/5) ) + "px'", 225); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (4/5) ) + "px'", 300); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (5/5) ) + "px'", 375); + } + + canvas.onmouseout = function (e) + { + if (RGraph.Registry.Get('chart.zoomed.div') && RGraph.Registry.Get('chart.zoomed.div').__object__.Get('chart.zoom.fade.out')) { + + RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8; + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 100); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 200); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2", 300); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0", 400); + + // Get rid of the zoom window + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 400); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 400); + + } else { + // Get rid of the zoom window + if (RGraph.Registry.Get('chart.zoomed.div')) { + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 1); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 41); + } } + } + } + + + /** + * A zoom in function + * + * @param e object The event object + */ + RGraph.Zoom = function (e) + { + e = RGraph.FixEventObject(e); + + /** + * Show the zoom window + */ + //if ((e.target.__canvas__ && e.target.__canvas__.__object__.Get('chart.zoom.mode') == 'thumbnail') || (e.target.parentNode.__canvas__ && e.target.parentNode.__canvas__.__object__.Get('chart.zoom.mode') == 'thumbnail') ) { + // return RGraph.ZoomWindow(e); + //} + if (e && e.target && e.target.__canvas__) { + var canvas = e.target.__canvas__; + + /******************************************************* + * This is here to facilitate zooming by just a single left click + *******************************************************/ + } else if (e && e.target && e.target.__object__) { + var canvas = e.target.__object__.canvas; + e.stopPropagation(); // Hmmmm + } + + // Fallback for MSIE9 + if (!canvas) { + var registry_canvas = RGraph.Registry.Get('chart.contextmenu').__canvas__; + if (registry_canvas) { + var canvas = registry_canvas; + } + } + + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var dataurl = canvas.toDataURL(); + var tmp = canvas; + var coords = RGraph.getCanvasXY(canvas); + var factor = obj.Get('chart.zoom.factor') - 1; + + var x = coords[0]; + var y = coords[1]; + + var img = document.createElement('img'); + img.className = 'RGraph_zoomed_canvas'; + img.style.border = '3px solid gray'; + img.style.width = canvas.width + 'px'; + img.style.height = canvas.height + 'px'; + img.style.position = 'absolute'; + img.style.left = x + 'px'; + img.style.top = y + 'px'; + img.style.backgroundColor = 'white'; + img.style.opacity = obj.Get('chart.zoom.fade.in') ? 0 : 1; + img.style.zIndex = 99; + img.src = dataurl; + document.body.appendChild(img); + + //RGraph.Registry.Set('chart.zoomedimage', img); + // Store the zoomed image in a global var - NOT the registry + __zoomedimage__ = img; + __zoomedimage__.obj = obj; + + // Image onclick should not hide the image + img.onclick = function (e) + { + e = RGraph.FixEventObject(e); + e.stopPropagation(); + return false; + } + + setTimeout(function (){window.onclick = RGraph.HideZoomedCanvas;}, 1); + + var width = parseInt(canvas.width); + var height = parseInt(canvas.height); + var frames = obj.Get('chart.zoom.frames'); + var delay = obj.Get('chart.zoom.delay'); + + // Increase the width over 10 frames - center + if (obj.Get('chart.zoom.hdir') == 'center') { + + for (var i=1; i<=frames; ++i) { + var newWidth = width * factor * (i/frames) + width; + var rightHandEdge = x + canvas.width; + var newLeft = (x + (canvas.width / 2)) - (newWidth / 2); + + setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay); + } + + // Left + } else if (obj.Get('chart.zoom.hdir') == 'left') { + for (var i=1; i<=frames; ++i) { + var newWidth = width * factor * (i/frames) + width; + var rightHandEdge = x + canvas.width; + var newLeft = rightHandEdge - newWidth; + + setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay); + } + + // Right (default) + } else { + for (var i=1; i<=frames; ++i) { + var newWidth = width * factor * (i/frames) + width; + setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'", i * delay); + } + } + + // Increase the height over 10 frames - up + if (obj.Get('chart.zoom.vdir') == 'up') { + for (var i=1; i<=frames; ++i) { + var newHeight = (height * factor * (i/frames)) + height; + var bottomEdge = y + canvas.height; + var newTop = bottomEdge - newHeight; + + setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay); + } + + // center + } else if (obj.Get('chart.zoom.vdir') == 'center') { + for (var i=1; i<=frames; ++i) { + var newHeight = (height * factor * (i/frames)) + height; + var bottomEdge = (y + (canvas.height / 2)) + (newHeight / 2); + var newTop = bottomEdge - newHeight; + + setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay); + } + + // Down (default + } else { + for (var i=1; i<=frames; ++i) { + setTimeout("__zoomedimage__.style.height = '" + String(height * factor * (i/frames) + height) + "px'", i * delay); + } + } + + // If enabled, increase the opactity over the requested number of frames + if (obj.Get('chart.zoom.fade.in')) { + for (var i=1; i<=frames; ++i) { + setTimeout("__zoomedimage__.style.opacity = " + Number(i / frames), i * (delay / 2)); + } + } + + // If stipulated, produce a shadow + if (obj.Get('chart.zoom.shadow')) { + for (var i=1; i<=frames; ++i) { + setTimeout("__zoomedimage__.style.boxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay); + setTimeout("__zoomedimage__.style.MozBoxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay); + setTimeout("__zoomedimage__.style.WebkitBoxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay); + } + } + + /** + * The onmouseout event. Hides the zoom window. Fades the zoom out + * + canvas.onmouseout = function (e) + { + if (RGraph.Registry.Get('chart.zoomed.div') && RGraph.Registry.Get('chart.zoomed.div').__object__.Get('chart.zoom.fade.out')) { + + RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8; + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 100); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 200); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2", 300); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0", 400); + + // Get rid of the zoom window + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 400); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 400); + + } else { + + // Get rid of the zoom window + if (RGraph.Registry.Get('chart.zoomed.div')) { + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 1); + setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 1); + } + } + } + */ + + // The background + if (obj.Get('chart.zoom.background')) { + + var div = document.createElement('DIV'); + div.style.backgroundColor = '#999'; + div.style.opacity = 0; + div.style.position = 'fixed'; + div.style.top = 0; + div.style.left = 0; + div.style.width = (screen.width + 100) + 'px'; + div.style.height = (screen.height + 100) + 'px'; + div.style.zIndex = 98; + + // Hides the zoomed caboodle + div.oncontextmenu = function (e) + { + return RGraph.HideZoomedCanvas(e); + } + + // 30th July 2010 - Is this necessary? + //for (var i=1; i<=frames; ++i) { + // setTimeout('__zoomedbackground__.style.opacity = ' + Number(0.04 * i), i * delay); + // + // // MSIE doesn't support zoom + // //setTimeout('__zoomedbackground__.style.filter = "progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135); Alpha(opacity=10)"', 50); + //} + + div.origHeight = div.style.height; + + document.body.appendChild(div); + + __zoomedbackground__ = div; + + // If the window is resized, hide the zoom + //window.onresize = RGraph.HideZoomedCanvas; + + for (var i=1; i<=frames; ++i) { + setTimeout("__zoomedbackground__.style.opacity = " + (Number(i / frames) * 0.5), i * (delay / 2)); + } + } + + /** + * Fire the onzoom event + */ + RGraph.FireCustomEvent(obj, 'onzoom'); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.fuel.js b/schall/static/RGraph/libraries/RGraph.fuel.js new file mode 100644 index 0000000..2bdef0f --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.fuel.js @@ -0,0 +1,364 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The Fuel widget constructor + * + * @param object canvas The canvas object + * @param int min The minimum value + * @param int max The maximum value + * @param int value The indicated value + */ + RGraph.Fuel = function (id, min, max, value) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'fuel'; + this.isRGraph = true; + this.min = min; + this.max = max; + this.value = value; + this.angles = {}; + this.currentValue = null; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Check for support + if (!this.canvas) { + alert('[FUEL] No canvas support'); + return; + } + + /** + * The funnel charts properties + */ + this.properties = + { + 'chart.colors': ['red'], + 'chart.needle.color': 'red', + 'chart.gutter.left': 5, + 'chart.gutter.right': 5, + 'chart.gutter.top': 5, + 'chart.gutter.bottom': 5, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.background': null, + 'chart.icon': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAfCAYAAAD0ma06AAAEGElEQVRIS7VXSyhtYRT+jnfe5FEMjAwUBiQGHikzRWIkkgy8YyDK+xnJK5JCeZSUGKBMiAyYkMxMJAMpSfJ+2/d8695/33NunSPnHqt2Z5+91/9/' + '/' + '/et9a/1b8Pn56dmMBhg/IWDgwNoNzc38PHxkXtN0+Tiexp9eH18fIDj1Bj63N/fw8vLS/wsmcHoqKmXT09PuL29RVFREU5OTvTJ6UIAgioQ+vLe09MTb29v8PX1RWBgICYnJ+XXIqDRWXN0dJT3nIDsWlpadP+lpSWZlD4KmL/8/' + '/7+Ls/S09N1/7y8PISHh+sK/QssDJWcHEyGCnB1dRUDAwPIzMzUx5GpAnZ1dcXy8jK2trbM5j06OsLc3JzISx8q4OzsLOOsAq6treHg4AAeHh4WJbq7u0Nzc7P+PiYmBnt7ezg9PcXExAQCAgLg5OSEx8dHuLu7Wwfc3t7G/v6+yEcjO8rIROGKaWdnZ+jr6zMDjI6OxvT0tDzr6uqS2KtksspwZ2cHjY2NuqSUhnHmilUCraysmElaWloKJpQCjI2NRX5+Pl5eXr6WlCv08/MTEMVOZDH+Zzw4CdlfX1/rDHt7ezE1NQXGkcYEKi4ulkVKYlpLGouBs/JiaGgIZL25uSlecXFxohAz/ccAz8/P4e/vj7q6Ojw8PMje5DNRy94MQ0JCUFtbK2wqKipE+sHBQbi4uPwMQ86ak5ODxMREVFdXIywsDCUlJRJDXnZlmJqaip6eHuTm5kqikGlycjIyMjL+ZrY9JSUgMzQiIgINDQ2ypaqqqkCZWXHsnjQEHB8fR0pKigAxabq7uyWOlJNxtLukTJDs7GxUVlZKDNl5oqKi8Pr6+jOAIyMjiI+Pl5JGQG4F1Qy+LN7f3fiUdGZmBsHBwRgbG8Pw8LD01ba2NmlX0rTtnTQLCwvSjEdHR3FxcSExLCwsRGRkpBR9vePzeMDyw3bT1NT0XXLiT4a7u7s4Pj4GGzd7K8GCgoKEsRR8I4Cm6hwHXV5eiv62GAE5npMTmFuBTCkzmzT7qs5Q9TlW/o6ODlvwhCHPM5SVPZIxYzNeXFxEa2srvL29YTC2GI3aMm3Zeq6urv4LMC0tDRsbG1K8k5KS9DgS0IwhKVFjSsJA22r9/f0oKCgQdvPz83JEmZ2dlcpD9maSshow0KZnlO8Csx9yK3BLKCMJPpf2xGMigdi9WXooaWdn53dxdP+amhrZh4eHh1hfX5cTW319vZyBnp+ffzNkBWBmhYaGysB/j322oCckJCArK0uGMlsJ5ubmBoPxRiMzFlomjr2MGdne3i5ANILRJEtJt6ysTG8h9gDl4am8vFwSUWron1O9LulXIOqk9pWftfdSS40yyj5Uh101wPRryuR7R1ZMX/U1pfy5IF40xcgUnGAc9wsGYxsFhy87kwAAAABJRU5ErkJggg==', + 'chart.labels.full': 'F', + 'chart.labels.empty': 'E' + } + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Fuel.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Fuel.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.Fuel.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Get the center X and Y of the chart. This is the center of the needle bulb + */ + this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft; + this.centery = this.canvas.height - 20 - this.gutterBottom + + /** + * Work out the radius of the chart + */ + this.radius = this.canvas.height - this.gutterTop - this.gutterBottom - 20; + + /** + * The start and end angles of the chart + */ + this.angles.start = 4.71 - 0.5; + this.angles.end = 4.71 + 0.5; + this.angles.needle = (((this.value - this.min) / (this.max - this.min)) * (this.angles.end - this.angles.start)) + this.angles.start; + + /** + * Draw the fuel guage + */ + this.DrawChart(); + + + /** + * Draw the labels on the chart + */ + this.DrawLabels(); + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * This function actually draws the chart + */ + RGraph.Fuel.prototype.DrawChart = function () + { + var context = this.context; + var canvas = this.canvas; + + /** + * Draw the "Scale" + */ + this.DrawScale(); + + /** + * Place the icon on the canvas + */ + if (!RGraph.isIE8()) { + this.DrawIcon(); + } + + /** + * Draw the needle on to the chart + */ + if (!this.Get('chart.icon') || RGraph.isIE8()) { + this.DrawNeedle(); + } + } + + + /** + * Draws the labels + */ + RGraph.Fuel.prototype.DrawLabels = function () + { + var radius = (this.radius - 20); + this.context.fillStyle = this.Get('chart.text.color'); + + // Draw the left label + var y = this.centery - Math.sin(this.angles.start - 3.14) * (this.radius - 25); + var x = this.centerx - Math.cos(this.angles.start - 3.14) * (this.radius - 25); + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, this.Get('chart.labels.empty'), 'center', 'center'); + + // Draw the right label + var y = this.centery - Math.sin(this.angles.start - 3.14) * (this.radius - 25); + var x = this.centerx + Math.cos(this.angles.start - 3.14) * (this.radius - 25); + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, this.Get('chart.labels.full'), 'center', 'center'); + } + + + /** + * Draws the needle + */ + RGraph.Fuel.prototype.DrawNeedle = function () + { + // Draw the actual needle + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.lineCap = 'round'; + this.context.strokeStyle = this.Get('chart.needle.color'); + + /** + * The angle for the needle + */ + var angle = this.angles.needle; + + this.context.arc(this.centerx, this.centery, this.radius - 30, angle, angle + 0.0001, false); + this.context.lineTo(this.centerx, this.centery); + this.context.stroke(); + + this.context.lineWidth = 1; + + // Create the gradient for the bulb + var cx = this.centerx + 10; + var cy = this.centery - 10 + + var grad = this.context.createRadialGradient(cx, cy, 35, cx, cy, 0); + grad.addColorStop(0, 'black'); + grad.addColorStop(1, '#eee'); + + if (navigator.userAgent.indexOf('Firefox/6.0') > 0) { + grad = this.context.createLinearGradient(cx + 10, cy - 10, cx - 10, cy + 10); + grad.addColorStop(1, '#666'); + grad.addColorStop(0.5, '#ccc'); + } + + // Draw the bulb + this.context.beginPath(); + this.context.fillStyle = grad; + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery, 20, 0, 6.28, 0); + this.context.fill(); + } + + + /** + * Draws the "scale" + */ + RGraph.Fuel.prototype.DrawScale = function () + { + //First draw the fill background + this.context.beginPath(); + this.context.strokeStyle = 'black'; + this.context.fillStyle = 'white'; + this.context.arc(this.centerx, this.centery, this.radius, this.angles.start, this.angles.end, false); + this.context.arc(this.centerx, this.centery, this.radius - 10, this.angles.end, this.angles.start, true); + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + + //First draw the fill itself + var start = this.angles.start; + var end = this.angles.needle; + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.colors')[0]; + this.context.arc(this.centerx, this.centery, this.radius, start, end, false); + this.context.arc(this.centerx, this.centery, this.radius - 10, end, start, true); + this.context.closePath(); + //this.context.stroke(); + this.context.fill(); + + // This draws the tickmarks + for (var a = this.angles.start; a<=this.angles.end+0.01; a+=((this.angles.end - this.angles.start) / 5)) { + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.radius - 10, a, a + 0.0001, false); + this.context.arc(this.centerx, this.centery, this.radius - 15, a + 0.0001, a, true); + this.context.stroke(); + } + } + + + /** + * Draws the icon + */ + RGraph.Fuel.prototype.DrawIcon = function () + { + var img = new Image(); + img.src = this.Get('chart.icon'); + img.__object__ = this; + + + img.onload = function (e) + { + var img = e.target; + var obj = img.__object__; + var context = obj.context; + + context.drawImage(this,obj.centerx - (this.width / 2),obj.gutterTop + 30); + + obj.DrawNeedle(); + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.funnel.js b/schall/static/RGraph/libraries/RGraph.funnel.js new file mode 100644 index 0000000..78a8b4e --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.funnel.js @@ -0,0 +1,679 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bar chart constructor + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.Funnel = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'funnel'; + this.coords = []; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Check for support + if (!this.canvas) { + alert('[FUNNEL] No canvas support'); + return; + } + + /** + * The funnel charts properties + */ + this.properties = { + 'chart.strokestyle': 'black', + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.labels': null, + 'chart.labels.sticks': false, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.colors': ['red', 'green', 'gray', 'blue', 'black', 'gray'], + 'chart.text.size': 10, + 'chart.text.boxed': true, + 'chart.text.halign': 'left', + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.blur': 3, + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.tooltips.highlight': true, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.taper': true} + + // Store the data + this.data = data; + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getSegment; + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Funnel.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Funnel.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.Funnel.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // This stops the coords array from growing + this.coords = []; + + RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + this.DrawFunnel(); + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * The tooltip handler + */ + if (this.Get('chart.tooltips')) { + + RGraph.Register(this); + + var canvas_onclick_func = function (e) + { + RGraph.Redraw(); + + var e = RGraph.FixEventObject(e); + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + + var mouseCoords = RGraph.getMouseXY(e); + var coords = obj.coords; + var x = mouseCoords[0]; + var y = mouseCoords[1]; + var segment = obj.getSegment(e); + + if (segment) { + + var idx = segment[2]; + + // Is there a tooltip defined? + if (!obj.Get('chart.tooltips')[idx] && typeof(obj.Get('chart.tooltips')) != 'function') { + return; + } + + context.beginPath(); + + RGraph.NoShadow(obj); + + context.strokeStyle = obj.Get('chart.highlight.stroke'); + context.fillStyle = obj.Get('chart.highlight.fill'); + + context.moveTo(coords[idx][0], coords[idx][1]); + context.lineTo(coords[idx][2], coords[idx][3]); + context.lineTo(coords[idx][4], coords[idx][5]); + context.lineTo(coords[idx][6], coords[idx][7]); + context.closePath(); + + context.stroke(); + context.fill(); + + /** + * Draw the key again for in-graph keys so they don't appear "under" the highlight + */ + if (obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') { + RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); + } + + /** + * Redraw the labels if necessary + */ + if (obj.Get('chart.labels')) { + obj.DrawLabels(); + } + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = obj.Get('chart.tooltips')(idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') { + var text = obj.Get('chart.tooltips')[idx](idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object') { + var text = obj.Get('chart.tooltips')[idx]; + + } else { + var text = ''; + } + + // Show the tooltip + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx); + + // Stop the event propagating + e.stopPropagation(); + } + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); + + /** + * Onmousemove event handler + */ + var canvas_onmousemove_func = function (e) + { + var e = RGraph.FixEventObject(e); + + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var overFunnel = false; + var coords = obj.coords; + + var mouseCoords = RGraph.getMouseXY(e); + var x = mouseCoords[0]; + var y = mouseCoords[1]; + var segment = obj.getSegment(e); + + if (segment) { + + var idx = segment[2]; + + // Is there a tooltip defined? + if (obj.Get('chart.tooltips')[idx] || typeof(obj.Get('chart.tooltips')) == 'function') { + + overFunnel = true; + canvas.style.cursor = 'pointer'; + + // Stop the event propagating + e.stopPropagation(); + + } + } + + if (!overFunnel) { + canvas.style.cursor = 'default'; + canvas.style.cursor = 'default'; + } + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + + /** + * Draw the labels on the chart + */ + this.DrawLabels(); + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * This function actually draws the chart + */ + RGraph.Funnel.prototype.DrawFunnel = function () + { + var context = this.context; + var canvas = this.canvas; + var width = this.canvas.width - this.gutterLeft - this.gutterRight; + var height = this.canvas.height - this.gutterTop - this.gutterBottom; + var total = RGraph.array_max(this.data); + var accheight = this.gutterTop; + + + /** + * Loop through each segment to draw + */ + + // Set a shadow if it's been requested + if (this.Get('chart.shadow')) { + context.shadowColor = this.Get('chart.shadow.color'); + context.shadowBlur = this.Get('chart.shadow.blur'); + context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + context.shadowOffsetY = this.Get('chart.shadow.offsety'); + } + + for (i=0; i<this.data.length; ++i) { + + i = Number(i); + + var firstvalue = this.data[0]; + var firstwidth = (firstvalue / total) * width; + var curvalue = this.data[i]; + var curwidth = (curvalue / total) * width; + var curheight = height / this.data.length; + var halfCurWidth = (curwidth / 2); + var nextvalue = this.data[i + 1] ? this.data[i + 1] : 0; + var nextwidth = this.data[i + 1] ? (nextvalue / total) * width : 0; + var halfNextWidth = (nextwidth / 2); + var center = this.gutterLeft + (firstwidth / 2); + + /** + * First segment + */ + if (i == 0) { + var x1 = center - halfCurWidth; + var y1 = this.gutterTop; + var x2 = center + halfCurWidth; + var y2 = this.gutterTop; + var x3 = center + halfNextWidth; + var y3 = accheight + curheight; + var x4 = center - halfNextWidth; + var y4 = accheight + curheight; + + /** + * Subsequent segments + */ + } else { + var x1 = center - halfCurWidth; + var y1 = accheight; + var x2 = center + halfCurWidth; + var y2 = accheight; + var x3 = center + halfNextWidth; + var y3 = accheight + curheight; + var x4 = center - halfNextWidth; + var y4 = accheight + curheight; + } + + /** + * Set the fill colour. If i is over 0 then don't use an offset + */ + if (document.all && this.Get('chart.shadow')) { + this.DrawIEShadow([x1, y1, x2, y2, x3, y3, x4, y4], i > 0 && this.Get('chart.shadow.offsety') < 0); + } + + context.strokeStyle = this.Get('chart.strokestyle'); + context.fillStyle = this.Get('chart.colors')[i]; + + if (halfNextWidth > 0 || this.Get('chart.taper')) { + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(x3, y3); + context.lineTo(x4, y4); + context.closePath(); + + /** + * Store the coordinates + */ + this.coords.push([x1, y1, x2, y2, x3, y3, x4, y4]); + } + + // The redrawing if the shadow is on will do the stroke + if (!this.Get('chart.shadow')) { + context.stroke(); + } + + context.fill(); + + accheight += curheight; + } + + /** + * If the shadow is enabled, redraw every segment, in order to allow for shadows going upwards + */ + if (this.Get('chart.shadow')) { + + RGraph.NoShadow(this); + + for (i=0; i<this.coords.length; ++i) { + + context.strokeStyle = this.Get('chart.strokestyle'); + context.fillStyle = this.Get('chart.colors')[i]; + + context.beginPath(); + context.moveTo(this.coords[i][0], this.coords[i][1]); + context.lineTo(this.coords[i][2], this.coords[i][3]); + context.lineTo(this.coords[i][4], this.coords[i][5]); + context.lineTo(this.coords[i][6], this.coords[i][7]); + context.closePath(); + + context.stroke(); + context.fill(); + } + } + + /** + * Lastly, draw the key if necessary + */ + if (this.Get('chart.key') && this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + } + + /** + * Draws the labels + */ + RGraph.Funnel.prototype.DrawLabels = function () + { + /** + * Draws the labels + */ + if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) { + + var context = this.context; + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + var color = this.Get('chart.text.color'); + var labels = this.Get('chart.labels'); + var halign = this.Get('chart.text.halign') == 'left' ? 'left' : 'center'; + var bgcolor = this.Get('chart.text.boxed') ? 'white' : null; + + if (typeof(this.Get('chart.labels.x')) == 'number') { + var x = this.Get('chart.labels.x'); + } else { + var x = halign == 'left' ? (this.gutterLeft - 15) : ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft; + } + + for (var j=0; j<this.coords.length; ++j) { // MUST be "j" + + context.beginPath(); + + // Set the color back to black + context.strokeStyle = 'black'; + context.fillStyle = color; + + // Turn off any shadow + RGraph.NoShadow(this); + + var label = labels[j]; + + RGraph.Text(context, + font, + size, + x, + this.coords[j][1], + label, + 'center', + halign, + this.Get('chart.text.boxed'), + null, + bgcolor); + + if (this.Get('chart.labels.sticks')) { + /** + * Measure the text + */ + this.context.font = size + 'pt ' + font; + var labelWidth = this.context.measureText(label).width; + + /** + * Draw the horizontal indicator line + */ + this.context.beginPath(); + this.context.strokeStyle = 'gray'; + + this.context.moveTo(x + labelWidth + 10, this.coords[j][1]); + this.context.lineTo(this.coords[j][0] - 10, this.coords[j][1]); + this.context.stroke(); + } + } + + + + /** + * This draws the last labels if defined + */ + var lastLabel = labels[j]; + + if (lastLabel) { + + RGraph.Text(context, font, size,x,this.coords[j - 1][5],lastLabel,'center',halign,this.Get('chart.text.boxed'),null,bgcolor); + + if (this.Get('chart.labels.sticks')) { + /** + * Measure the text + */ + this.context.font = size + 'pt ' + font; + var labelWidth = this.context.measureText(lastLabel).width; + + /** + * Draw the horizontal indicator line + */ + this.context.beginPath(); + this.context.strokeStyle = 'gray'; + this.context.moveTo(x + labelWidth + 10, this.coords[j - 1][7]); + this.context.lineTo(this.coords[j - 1][0] - 10, this.coords[j - 1][7]); + this.context.stroke(); + } + } + } + } + + + /** + * This function is used by MSIE only to manually draw the shadow + * + * @param array coords The coords for the bar + */ + RGraph.Funnel.prototype.DrawIEShadow = function (coords, noOffset) + { + var prevFillStyle = this.context.fillStyle; + var offsetx = this.Get('chart.shadow.offsetx'); + var offsety = this.Get('chart.shadow.offsety'); + var context = this.context; + + context.lineWidth = 1; + context.fillStyle = this.Get('chart.shadow.color'); + + // Draw the shadow + context.beginPath(); + context.moveTo(coords[0] + (noOffset ? 0 : offsetx), coords[1] + (noOffset ? 0 : offsety)); + context.lineTo(coords[2] + (noOffset ? 0 : offsetx), coords[3] + (noOffset ? 0 : offsety)); + context.lineTo(coords[4] + (noOffset ? 0 : offsetx), coords[5] + (noOffset ? 0 : offsety)); + context.lineTo(coords[6] + (noOffset ? 0 : offsetx), coords[7] + (noOffset ? 0 : offsety)); + context.closePath(); + + context.fill(); + + + + // Change the fillstyle back to what it was + this.context.fillStyle = prevFillStyle; + } + + + /** + * Gets the appropriate segment that has been highlighted + */ + RGraph.Funnel.prototype.getSegment = function (e) + { + var canvas = e.target; + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var coords = obj.coords; + var x = mouseCoords[0]; + var y = mouseCoords[1]; + + + + for (i=0; i<coords.length; ++i) { + if ( + x > coords[i][0] + && x < coords[i][2] + && y > coords[i][1] + && y < coords[i][5] + ) { + + /** + * Handle the right corner + */ + if (x > coords[i][4]) { + var w1 = coords[i][2] - coords[i][4]; + var h1 = coords[i][5] - coords[i][3];; + var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES + + var w2 = coords[i][2] - mouseCoords[0]; + var h2 = mouseCoords[1] - coords[i][1]; + var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES + + if (a2 > a1) { + continue; + } + + /** + * Handle the left corner + */ + } else if (x < coords[i][6]) { + var w1 = coords[i][6] - coords[i][0]; + var h1 = coords[i][7] - coords[i][1];; + var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES + + var w2 = mouseCoords[0] - coords[i][0]; + var h2 = mouseCoords[1] - coords[i][1]; + var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES + + if (a2 > a1) { + continue; + } + } + + return [obj, coords[i], i]; + } + } + + return null; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.gantt.js b/schall/static/RGraph/libraries/RGraph.gantt.js new file mode 100644 index 0000000..1e0ae49 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.gantt.js @@ -0,0 +1,514 @@ +7 /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The gantt chart constructor + * + * @param object canvas The cxanvas object + * @param array data The chart data + */ + RGraph.Gantt = function (id) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext("2d"); + this.canvas.__object__ = this; + this.type = 'gantt'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Set some defaults + this.properties = { + 'chart.background.barcolor1': 'white', + 'chart.background.barcolor2': 'white', + 'chart.background.grid': true, + 'chart.background.grid.width': 1, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.grid.hlines': true, + 'chart.background.grid.vlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':false, + 'chart.background.grid.autofit.numhlines': 7, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.background.vbars': [], + 'chart.text.size': 10, + 'chart.text.font': 'Verdana', + 'chart.text.color': 'black', + 'chart.gutter.left': 75, + 'chart.gutter.right': 25, + 'chart.gutter.top': 35, + 'chart.gutter.bottom': 25, + 'chart.labels': [], + 'chart.margin': 2, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.pos': null, + 'chart.title.yaxis.position': 'right', + 'chart.events': [], + 'chart.borders': true, + 'chart.defaultcolor': 'white', + 'chart.coords': [], + 'chart.tooltips': [], + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.xmin': 0, + 'chart.xmax': 0, + 'chart.contextmenu': null, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.adjustable': false + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A peudo setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Gantt.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A peudo getter + * + * @param name string The name of the property to get + */ + RGraph.Gantt.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * Draws the chart + */ + RGraph.Gantt.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Work out the graphArea + */ + this.graphArea = this.canvas.width - this.gutterLeft - this.gutterRight; + this.graphHeight = this.canvas.height - this.gutterTop - this.gutterBottom; + this.numEvents = this.Get('chart.events').length + this.barHeight = this.graphHeight / this.numEvents; + this.halfBarHeight = this.barHeight / 2; + + /** + * Draw the background + */ + RGraph.background.Draw(this); + + /** + * Draw a space for the left hand labels + */ + //this.context.beginPath(); + //this.context.lineWidth = 1; + //this.context.strokeStyle = this.Get('chart.background.grid.color'); + //this.context.fillStyle = 'white'; + //this.context.fillRect(0,gutter - 5,gutter * 3, RGraph.GetHeight(this) - (2 * gutter) + 10); + //this.context.moveTo(gutter * 3, gutter); + //this.context.lineTo(gutter * 3, RGraph.GetHeight(this) - gutter); + //this.context.stroke(); + //this.context.fill(); + + /** + * Draw the labels at the top + */ + this.DrawLabels(); + + /** + * Draw the events + */ + this.DrawEvents(); + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * This function enables adjusting + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the labels at the top and the left of the chart + */ + RGraph.Gantt.prototype.DrawLabels = function () + { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + + /** + * Draw the X labels at the top of the chart. + */ + var labelSpace = (this.graphArea) / this.Get('chart.labels').length; + var xPos = this.gutterLeft + (labelSpace / 2); + this.context.strokeStyle = 'black' + + for (i=0; i<this.Get('chart.labels').length; ++i) { + RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size'),xPos + (i * labelSpace),this.gutterTop - (this.Get('chart.text.size') / 2) - 5,String(this.Get('chart.labels')[i]),'center','center'); + } + + // Draw the vertical labels + for (var i=0; i<this.Get('chart.events').length; ++i) { + var ev = this.Get('chart.events')[i]; + var x = this.gutterLeft; + var y = this.gutterTop + this.halfBarHeight + (i * this.barHeight); + + RGraph.Text(this.context, + this.Get('chart.text.font'), + this.Get('chart.text.size'), + x - 5, y, + RGraph.is_array(ev[0]) ? String(ev[0][3]) : String(ev[3]), + 'center', + 'right'); + } + } + + /** + * Draws the events to the canvas + */ + RGraph.Gantt.prototype.DrawEvents = function () + { + var canvas = this.canvas; + var context = this.context; + var events = this.Get('chart.events'); + + /** + * Reset the coords array to prevent it growing + */ + this.coords = []; + + /** + * First draw the vertical bars that have been added + */ + if (this.Get('chart.vbars')) { + for (i=0; i<this.Get('chart.vbars').length; ++i) { + // Boundary checking + if (this.Get('chart.vbars')[i][0] + this.Get('chart.vbars')[i][1] > this.Get('chart.xmax')) { + this.Get('chart.vbars')[i][1] = 364 - this.Get('chart.vbars')[i][0]; + } + + var barX = this.gutterLeft + (( (this.Get('chart.vbars')[i][0] - this.Get('chart.xmin')) / (this.Get('chart.xmax') - this.Get('chart.xmin')) ) * this.graphArea); + + var barY = this.gutterTop; + var width = (this.graphArea / (this.Get('chart.xmax') - this.Get('chart.xmin')) ) * this.Get('chart.vbars')[i][1]; + var height = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + + // Right hand bounds checking + if ( (barX + width) > (RGraph.GetWidth(this) - this.gutterRight) ) { + width = RGraph.GetWidth(this) - this.gutterRight - barX; + } + + context.fillStyle = this.Get('chart.vbars')[i][2]; + context.fillRect(barX, barY, width, height); + } + } + + + /** + * Draw the events + */ + for (i=0; i<events.length; ++i) { + if (typeof(events[i][0]) == 'number') { + this.DrawSingleEvent(events[i]); + } else { + for (var j=0; j<events[i].length; ++j) { + this.DrawSingleEvent(events[i][j]); + } + } + + } + + + /** + * If tooltips are defined, handle them + */ + if (this.Get('chart.tooltips')) { + + // Register the object for redrawing + RGraph.Register(this); + + /** + * If the cursor is over a hotspot, change the cursor to a hand + */ + var canvas_onmousemove_func = function (eventObj) + { + eventObj = RGraph.FixEventObject(eventObj); + var canvas = eventObj.target; + var obj = canvas.__object__; + var len = obj.coords.length; + var mouseCoords = RGraph.getMouseXY(eventObj); + var bar = obj.getBar(eventObj); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + if (bar) { + canvas.style.cursor = 'pointer'; + return; + } + + canvas.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + + + var canvas_onclick_func = function (eventObj) + { + eventObj = RGraph.FixEventObject(eventObj); + + var canvas = eventObj.target; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(eventObj); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var bar = obj.getBar(eventObj); + + if (bar) { + + var idx = bar[5]; + + // Redraw the graph + RGraph.Redraw(); + + // Get the tooltip text + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx); + + if (String(text).length && text != '') { + + // SHOW THE CORRECT TOOLTIP + RGraph.Tooltip(canvas, text, eventObj.pageX, eventObj.pageY, idx); + + /** + * Draw a rectangle around the correct bar, in effect highlighting it + */ + + context.lineWidth = 1; + context.strokeStyle = obj.Get('chart.highlight.stroke'); + context.fillStyle = obj.Get('chart.highlight.fill'); + context.strokeRect(bar[1], bar[2], bar[3], bar[4]); + context.fillRect(bar[1], bar[2], bar[3], bar[4]); + + eventObj.stopPropagation(); + } + + return; + } + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); + } + } + + + /** + * Retrieves the bar (if any) that has been click on or is hovered over + * + * @param object e The event object + */ + RGraph.Gantt.prototype.getBar = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var coords = obj.coords; + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i<coords.length; i++) { + + var left = coords[i][0]; + var top = coords[i][1]; + var width = coords[i][2]; + var height = coords[i][3]; + + if ( mouseX >= left + && mouseX <= (left + width) + && mouseY >= top + && mouseY <= (top + height) + && (typeof(obj.Get('chart.tooltips')) == 'function' || obj.Get('chart.tooltips')[i]) ) { + + return [obj, left, top, width, height, i]; + } + } + } + + + /** + * Draws a single event + */ + RGraph.Gantt.prototype.DrawSingleEvent = function () + { + var min = this.Get('chart.xmin'); + var context = this.context; + var ev = RGraph.array_clone(arguments[0]); + + context.beginPath(); + context.strokeStyle = 'black'; + context.fillStyle = ev[4] ? ev[4] : this.Get('chart.defaultcolor'); + + var barStartX = this.gutterLeft + (((ev[0] - min) / (this.Get('chart.xmax') - min)) * this.graphArea); + //barStartX += this.margin; + var barStartY = this.gutterTop + (i * this.barHeight); + var barWidth = (ev[1] / (this.Get('chart.xmax') - min) ) * this.graphArea; + + /** + * If the width is greater than the graph atrea, curtail it + */ + if ( (barStartX + barWidth) > (RGraph.GetWidth(this) - this.gutterRight) ) { + barWidth = RGraph.GetWidth(this) - this.gutterRight - barStartX; + } + + /** + * Draw the actual bar storing store the coordinates + */ + this.coords.push([barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin'))]); + context.fillRect(barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin')) ); + + // Work out the completeage indicator + var complete = (ev[2] / 100) * barWidth; + + // Draw the % complete indicator. If it's greater than 0 + if (typeof(ev[2]) == 'number') { + context.beginPath(); + context.fillStyle = ev[5] ? ev[5] : '#0c0'; + context.fillRect(barStartX, + barStartY + this.Get('chart.margin'), + (ev[2] / 100) * barWidth, + this.barHeight - (2 * this.Get('chart.margin')) ); + + context.beginPath(); + context.fillStyle = this.Get('chart.text.color'); + RGraph.Text(context, this.Get('chart.text.font'), this.Get('chart.text.size'), barStartX + barWidth + 5, barStartY + this.halfBarHeight, String(ev[2]) + '%', 'center'); + } + + // draw the border around the bar + if (this.Get('chart.borders') || ev[6]) { + context.strokeStyle = typeof(ev[6]) == 'string' ? ev[6] : 'black'; + context.lineWidth = (typeof(ev[7]) == 'number' ? ev[7] : 1); + context.beginPath(); + context.strokeRect(barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin')) ); + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.gauge.js b/schall/static/RGraph/libraries/RGraph.gauge.js new file mode 100644 index 0000000..80a445e --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.gauge.js @@ -0,0 +1,464 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The line chart constructor + * + * @param object canvas The cxanvas object + * @param array data The chart data + * @param array ... Other lines to plot + */ + RGraph.Gauge = function (id, min, max, value) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'gauge'; + this.min = min; + this.max = max; + this.value = value; + this.isRGraph = true; + this.currentValue = null; + + /** + * Range checking + */ + if (this.value > this.max) { + this.value = max; + } + + if (this.value < this.min) { + this.value = min; + } + + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config type stuff + this.properties = { + 'chart.gutter.left': 5, + 'chart.gutter.right': 5, + 'chart.gutter.top': 5, + 'chart.gutter.bottom': 5, + 'chart.border.width': 10, + 'chart.title': '', + 'chart.title.font': 'Verdana', + 'chart.title.size': 14, + 'chart.title.color': '#333', + 'chart.title.bold': false, + 'chart.text.font': 'Verdana', + 'chart.text.color': '#666', + 'chart.text.size': 10, + 'chart.scale.decimals': 0, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.red.start': 0.9 * this.max, + 'chart.red.color': '#DC3912', + 'chart.yellow.color': '#FF9900', + 'chart.green.end': 0.7 * this.max, + 'chart.green.color': 'rgba(0,0,0,0)', + 'chart.needle.tail': false + } + } + + + + /** + * An all encompassing accessor + * + * @param string name The name of the property + * @param mixed value The value of the property + */ + RGraph.Gauge.prototype.Set = function (name, value) + { + this.properties[name] = value; + } + + + /** + * An all encompassing accessor + * + * @param string name The name of the property + */ + RGraph.Gauge.prototype.Get = function (name) + { + return this.properties[name]; + } + + + /** + * The function you call to draw the line chart + * + * @param bool An optional bool used internally to ditinguish whether the + * line chart is being called by the bar chart + */ + RGraph.Gauge.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + /** + * Store the value (for animation primarily + */ + this.currentValue = this.value; + + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft; + this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + this.radius = Math.min( + ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2), + ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + ); + this.startAngle = (1.57 / 3) + 1.57; + this.endAngle = 6.28 + 1.57 - (1.57 / 3); + + // This has to be in the constructor + this.centerpinRadius = (15/125) * this.radius; + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + + + // DRAW THE CHART HERE + this.DrawBackGround(); + this.DrawColorBands(); + this.DrawSmallTickmarks(); + this.DrawBigTickmarks(); + this.DrawLabels(); + this.DrawTitle(); + this.DrawNeedle(); + this.DrawCenterpin(); + + + + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail') { + RGraph.ShowZoomWindow(this); + } + + /** + * This function enables the zoom in area mode + */ + if (this.Get('chart.zoom.mode') == 'area') { + RGraph.ZoomArea(this); + } + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draw the background + */ + RGraph.Gauge.prototype.DrawBackGround = function () + { + var borderWidth = this.Get('chart.border.width'); + + this.context.beginPath(); + this.context.fillStyle = 'white'; + + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0); + this.context.fill(); + + /** + * Draw the gray circle + */ + this.context.beginPath(); + this.context.fillStyle = '#ccc';; + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0); + this.context.fill(); + + /** + * Draw the light gray inner border + */ + this.context.beginPath(); + this.context.fillStyle = '#f1f1f1'; + this.context.arc(this.centerx, this.centery, this.radius - borderWidth, 0, 6.28, 0); + this.context.fill(); + + // Draw the white circle inner border + this.context.beginPath(); + this.context.fillStyle = 'white'; + this.context.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, 6.28, 0); + this.context.fill(); + + this.context.beginPath(); + this.context.strokeStyle = 'black'; + //this.context.moveTo(this.centerx, this.centery) + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0); + this.context.stroke(); + } + + + /** + * This function draws the smaller tickmarks + */ + RGraph.Gauge.prototype.DrawSmallTickmarks = function () + { + var numTicks = 25; + + for (var i=0; i<=numTicks; ++i) { + this.context.beginPath(); + this.context.strokeStyle = 'black'; + var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle; + this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10, a, a + 0.00001, 0); + this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10 - 5, a, a + 0.00001, 0); + this.context.stroke(); + } + } + + + /** + * This function draws the large, bold tickmarks + */ + RGraph.Gauge.prototype.DrawBigTickmarks = function () + { + var numTicks = 5; + this.context.lineWidth = 3; + this.context.lineCap = 'round'; + + for (var i=0; i<=numTicks; ++i) { + this.context.beginPath(); + this.context.strokeStyle = 'black'; + var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle; + this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10, a, a + 0.00001, 0); + this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10 - 10, a, a + 0.00001, 0); + this.context.stroke(); + } + } + + + /** + * This function draws the centerpin + */ + RGraph.Gauge.prototype.DrawCenterpin = function () + { + var offset = 6; + + var grad = this.context.createRadialGradient(this.centerx + offset, this.centery - offset, 0, this.centerx + offset, this.centery - offset, 25); + grad.addColorStop(0, '#ddf'); + grad.addColorStop(1, 'blue'); + + this.context.beginPath(); + this.context.fillStyle = grad; + this.context.arc(this.centerx, this.centery, this.centerpinRadius, 0, 6.28, 0); + this.context.fill(); + } + + + /** + * This function draws the labels + */ + RGraph.Gauge.prototype.DrawLabels = function () + { + this.context.fillStyle = this.Get('chart.text.color'); + + this.context.beginPath(); + // First label + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery + Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')), this.Get('chart.units.pre') + String(this.min.toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'bottom', 'left'); + + // Second label + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - this.radius + 25 + this.Get('chart.border.width'), this.centery,this.Get('chart.units.pre') + String((((this.max - this.min) * 0.2) + this.min).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post')),'center', 'left'); + + // Third label + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery - Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String((((this.max - this.min) * 0.4) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'top', 'center'); + + // Fourth label + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery - Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String((((this.max - this.min) * 0.6) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'top', 'center'); + + // Fifth label + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + this.radius - 25 - this.Get('chart.border.width'), this.centery, this.Get('chart.units.pre') + String((((this.max - this.min) * 0.8) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'center', 'right'); + + // Sixth (last) label + RGraph.Text(this.context,this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery + Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String(this.max.toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'bottom', 'right'); + this.context.fill(); + } + + + /** + * This function draws the title + */ + RGraph.Gauge.prototype.DrawTitle = function () + { + var title = this.Get('chart.title'); + + if (title) { + + this.context.fillStyle = this.Get('chart.title.color'); + + this.context.beginPath(); + RGraph.Text(this.context, + this.Get('chart.title.font'), + this.Get('chart.title.size'), + this.centerx, + this.centery - 30, + String(this.Get('chart.title')), + 'center', + 'center', + null, + null, + null, + this.Get('chart.title.bold')); + this.context.fill(); + } + } + + + /** + * This function draws the Needle + */ + RGraph.Gauge.prototype.DrawNeedle = function () + { + this.context.lineWidth = 0.5; + this.context.strokeStyle = '#983724'; + this.context.fillStyle = '#D5604D'; + + var angle = (this.endAngle - this.startAngle) * ((this.value - this.min) / (this.max - this.min)); + angle += this.startAngle; + + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.radius - 25 - this.Get('chart.border.width'), angle, angle + 0.00001, false); + this.context.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle + 1.57, angle + 0.00001 + 1.57, false); + + if (this.Get('chart.needle.tail')) { + this.context.arc(this.centerx, this.centery, this.radius * 0.2 , angle + 3.14, angle + 0.00001 + 3.14, false); + } + + this.context.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle - 1.57, angle - 0.00001 - 1.57, false); + this.context.stroke(); + this.context.fill(); + + + //this.context.strokeStyle = '#D5604D'; + + //this.context.beginPath(); + // this.context.lineWidth = 5; + // this.context.arc(this.centerx, this.centery, 25, angle + 3.14, angle + 0.00001 + 3.14, false); + // this.context.lineTo(this.centerx, this.centery); + //this.context.stroke(); + //this.context.fill(); + + /** + * Store the angle in an object variable + */ + this.angle = angle; + } + + + /** + * This draws the green background to the tickmarks + */ + RGraph.Gauge.prototype.DrawColorBands = function () + { + /** + * Draw the GREEN region + */ + this.context.strokeStyle = this.Get('chart.green.color'); + this.context.fillStyle = this.Get('chart.green.color'); + + var greenStart = this.startAngle; + var greenEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.Get('chart.green.end') - this.min) / (this.max - this.min)) + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), greenStart, greenEnd, false); + this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), greenEnd, greenStart, true); + this.context.fill(); + + + /** + * Draw the YELLOW region + */ + this.context.strokeStyle = this.Get('chart.yellow.color'); + this.context.fillStyle = this.Get('chart.yellow.color'); + + var yellowStart = greenEnd; + var yellowEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.Get('chart.red.start') - this.min) / (this.max - this.min)) + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), yellowStart, yellowEnd, false); + this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), yellowEnd, yellowStart, true); + this.context.fill(); + + + /** + * Draw the RED region + */ + this.context.strokeStyle = this.Get('chart.red.color'); + this.context.fillStyle = this.Get('chart.red.color'); + + var redStart = yellowEnd; + var redEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.max - this.min) / (this.max - this.min)) + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), redStart, redEnd, false); + this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), redEnd, redStart, true); + this.context.fill(); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.hbar.js b/schall/static/RGraph/libraries/RGraph.hbar.js new file mode 100644 index 0000000..a8f39d6 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.hbar.js @@ -0,0 +1,966 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * 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. + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.HBar = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.data = data; + this.type = 'hbar'; + this.coords = []; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + this.max = 0; + this.stackedOrGrouped = false; + + // Default properties + this.properties = { + 'chart.gutter.left': 75, + 'chart.gutter.right': 25, + 'chart.gutter.top': 35, + 'chart.gutter.bottom': 25, + 'chart.background.grid': true, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 25, + 'chart.background.grid.vsize': 25, + 'chart.background.barcolor1': 'white', + 'chart.background.barcolor2': 'white', + 'chart.background.grid.hlines': true, + 'chart.background.grid.vlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':true, + 'chart.background.grid.autofit.numhlines': 14, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': 10, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.colors': ['red', 'blue', 'green', 'pink', 'yellow', 'cyan', 'navy', 'gray', 'black'], + 'chart.labels': [], + 'chart.labels.above': false, + 'chart.labels.above.decimals': 0, + 'chart.xlabels': true, + 'chart.contextmenu': null, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.units.ingraph': false, + 'chart.strokestyle': 'black', + 'chart.xmin': 0, + 'chart.xmax': 0, + 'chart.axis.color': 'black', + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.blur': 3, + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.vmargin': 3, + 'chart.grouping': 'grouped', + 'chart.tooltips': null, + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.highlight.stroke': 'black', + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.scale.decimals': null, + 'chart.noredraw': false + } + + // Check for support + if (!this.canvas) { + alert('[HBAR] No canvas support'); + return; + } + + for (i=0; i<this.data.length; ++i) { + if (typeof(this.data[i]) == 'object') { + this.stackedOrGrouped = true; + } + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.HBar.prototype.Set = function (name, value) + { + if (name == 'chart.labels.abovebar') { + name = 'chart.labels.above'; + } + + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.HBar.prototype.Get = function (name) + { + if (name == 'chart.labels.abovebar') { + name = 'chart.labels.above'; + } + + return this.properties[name]; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.HBar.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Stop the coords array from growing uncontrollably + */ + this.coords = []; + this.max = 0; + + /** + * Check for chart.xmin in stacked charts + */ + if (this.Get('chart.xmin') > 0 && this.Get('chart.grouping') == 'stacked') { + alert('[HBAR] Using chart.xmin is not supported with stacked charts, resetting chart.xmin to zero'); + this.Set('chart.xmin', 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.gutterLeft - this.gutterRight; + this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom; + this.halfgrapharea = this.grapharea / 2; + this.halfTextHeight = this.Get('chart.text.size') / 2; + + + // Progressively Draw the chart + RGraph.background.Draw(this); + + this.Drawbars(); + this.DrawAxes(); + this.DrawLabels(); + + + // Draw the key if necessary + if (this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + /** + * Install the event handlers for tooltips + */ + if (this.Get('chart.tooltips')) { + + // Need to register this object for redrawing + RGraph.Register(this); + + /** + * Install the window onclick handler + */ + window.onclick = function () + { + RGraph.Redraw(); + } + + + + /** + * If the cursor is over a hotspot, change the cursor to a hand + */ + //this.canvas.onmousemove = function (e) + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Get the mouse X/Y coordinates + */ + var mouseCoords = RGraph.getMouseXY(e); + + if (bar && (typeof(obj.Get('chart.tooltips')) == 'function' || obj.Get('chart.tooltips')[bar[4]]) ) { + + canvas.style.cursor = 'pointer'; + + var left = bar[0]; + var top = bar[1]; + var width = bar[2]; + var height = bar[3]; + var idx = bar[4]; + + /** + * Show the tooltip if the event is onmousemove + */ + if (obj.Get('chart.tooltips.event') == 'onmousemove') { + + var tooltipObj = RGraph.Registry.Get('chart.tooltip'); + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = obj.Get('chart.tooltips')(idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') { + var text = obj.Get('chart.tooltips')[idx](idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object') { + var text = obj.Get('chart.tooltips')[idx]; + + } else { + var text = null; + } + + if (text) { + if (!tooltipObj || tooltipObj.__index__ != idx) { + + RGraph.HideTooltip(); + RGraph.Redraw(); + + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(left, top, width, height); + obj.context.fillRect(left, top, width, height); + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx); + } + return; + } + } + } + + if (!bar) { + canvas.style.cursor = 'default'; + } + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + + + /** + * Install the onclick event handler for the tooltips + */ + //this.canvas.onclick = function (e) + var canvas_onclick_func = function (e) + { + e = RGraph.FixEventObject(e); + + //var canvas = document.getElementById(this.id); + var canvas = e.target; + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Redraw(); + + /** + * Get the mouse X/Y coordinates + */ + var mouseCoords = RGraph.getMouseXY(e); + + /******************************************************* + * Only do this if a bar is being hovered over + *******************************************************/ + if (bar) { + + var left = bar[0]; + var top = bar[1]; + var width = bar[2]; + var height = bar[3]; + var idx = bar[4]; + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = obj.Get('chart.tooltips')(idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') { + var text = obj.Get('chart.tooltips')[idx](idx); + + } else if (typeof(obj.Get('chart.tooltips')) == 'object') { + var text = obj.Get('chart.tooltips')[idx]; + + } else { + var text = null; + } + + /** + * Show a tooltip if it's defined + */ + if (String(text).length && text != null) { + + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(left, top, width, height); + obj.context.fillRect(left, top, width, height); + + obj.context.stroke(); + obj.context.fill(); + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx); + } + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id,'click', canvas_onclick_func); + + // This resets the bar graph + if (RGraph.Registry.Get('chart.tooltip')) { + RGraph.Registry.Get('chart.tooltip').style.display = 'none'; + RGraph.Registry.Set('chart.tooltip', null) + } + } + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + + /** + * Draw "in graph" labels + */ + RGraph.DrawInGraphLabels(this); + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + /** + * This draws the axes + */ + RGraph.HBar.prototype.DrawAxes = function () + { + var halfway = (this.graphwidth / 2) + this.gutterLeft; + + this.context.beginPath(); + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.axis.color'); + + // Draw the Y axis + if (this.Get('chart.yaxispos') == 'center') { + this.context.moveTo(halfway, this.gutterTop); + this.context.lineTo(halfway, this.canvas.height - this.gutterBottom); + } else { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + } + + // Draw the X axis + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom); + + // Draw the Y tickmarks + var yTickGap = (this.canvas.height - this.gutterTop - this.gutterBottom) / this.data.length; + + for (y=this.gutterTop; y<(this.canvas.height - this.gutterBottom - 1); y+=yTickGap) { + if (this.Get('chart.yaxispos') == 'center') { + this.context.moveTo(halfway + 3, y); + this.context.lineTo(halfway - 3, y); + } else { + this.context.moveTo(this.gutterLeft, y); + this.context.lineTo( this.gutterLeft - 3, y); + } + } + + + // Draw the X tickmarks + xTickGap = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight ) / 10; + yStart = RGraph.GetHeight(this) - this.gutterBottom; + yEnd = (RGraph.GetHeight(this) - this.gutterBottom) + 3; + + for (x=(RGraph.GetWidth(this) - this.gutterRight), i=0; this.Get('chart.yaxispos') == 'center' ? x>=this.gutterLeft : x>this.gutterLeft; x-=xTickGap) { + + if (this.Get('chart.yaxispos') != 'center' || i != 5) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + } + i++; + } + + this.context.stroke(); + } + + + /** + * This draws the labels for the graph + */ + RGraph.HBar.prototype.DrawLabels = function () + { + var context = this.context; + var canvas = this.canvas; + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var text_size = this.Get('chart.text.size'); + var font = this.Get('chart.text.font'); + + + /** + * Set the units to blank if they're to be used for ingraph labels only + */ + if (this.Get('chart.units.ingraph')) { + units_pre = ''; + units_post = ''; + } + + + /** + * Draw the X axis labels + */ + if (this.Get('chart.xlabels')) { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + + if (this.Get('chart.yaxispos') == 'center') { + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (10/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (9/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (8/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (7/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (6/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (4/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (3/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (2/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (1/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (0)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + + } else { + + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (5/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (4/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (3/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (2/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (1/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center'); + + if (this.Get('chart.xmin') > 0) { + RGraph.Text(context,font,text_size,this.gutterLeft,this.gutterTop + this.halfTextHeight + this.graphheight + 2,RGraph.number_format(this, this.Get('chart.xmin'), units_pre, units_post),'center','center'); + } + } + + this.context.fill(); + this.context.stroke(); + } + + /** + * The Y axis labels + */ + if (typeof(this.Get('chart.labels')) == 'object') { + + var xOffset = 5; + var font = this.Get('chart.text.font'); + + // Draw the X axis labels + this.context.fillStyle = this.Get('chart.text.color'); + + // How wide is each bar + var barHeight = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom ) / this.Get('chart.labels').length; + + // Reset the xTickGap + yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / this.Get('chart.labels').length + + // Draw the X tickmarks + var i=0; + for (y=this.gutterTop + (yTickGap / 2); y<=RGraph.GetHeight(this) - this.gutterBottom; y+=yTickGap) { + RGraph.Text(this.context, font,this.Get('chart.text.size'),this.gutterLeft - xOffset,y,String(this.Get('chart.labels')[i++]),'center','right'); + } + } + } + + + /** + * This function draws the actual bars + */ + RGraph.HBar.prototype.Drawbars = function () + { + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + var prevX = 0; + var prevY = 0; + + /** + * Work out the max value + */ + if (this.Get('chart.xmax')) { + this.scale = [ + (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.2) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')), + (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.4) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')), + (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.6) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')), + (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.8) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')), + (((this.Get('chart.xmax') - this.Get('chart.xmin')) + this.Get('chart.xmin'))).toFixed(this.Get('chart.scale.decimals')) + ]; + this.max = this.scale[4]; + + } else { + + var grouping = this.Get('chart.grouping'); + + for (i=0; i<this.data.length; ++i) { + if (typeof(this.data[i]) == 'object') { + var value = grouping == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ; + } else { + var value = Number(Math.abs(this.data[i])); + } + + this.max = Math.max(Math.abs(this.max), Math.abs(value)); + } + + this.scale = RGraph.getScale(this.max, this); + + /** + * Account for chart.xmin + */ + if (this.Get('chart.xmin') > 0) { + this.scale[0] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.2) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals'))); + this.scale[1] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.4) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals'))); + this.scale[2] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.6) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals'))); + this.scale[3] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.8) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals'))); + this.scale[4] = Number((((this.scale[4] - this.Get('chart.xmin')) * 1.0) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals'))); + } + + this.max = this.scale[4]; + } + + if (this.Get('chart.scale.decimals') == null && Number(this.max) == 1) { + this.Set('chart.scale.decimals', 1); + } + + /******************************************************* + * This is here to facilitate sequential colors + *******************************************************/ + var colorIdx = 0; + + /** + * The bars are drawn HERE + */ + var graphwidth = (this.canvas.width - this.gutterLeft - this.gutterRight); + var halfwidth = graphwidth / 2; + + for (i=0; i<this.data.length; ++i) { + + // Work out the width and height + var width = (this.data[i] / this.max) * graphwidth; + var height = this.graphheight / this.data.length; + + var orig_height = height; + + var x = this.gutterLeft; + var y = this.gutterTop + (i * height); + var vmargin = this.Get('chart.vmargin'); + + // 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 (this.Get('chart.shadow')) { + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + } + + /** + * Draw the bar + */ + this.context.beginPath(); + if (typeof(this.data[i]) == 'number') { + + var barHeight = height - (2 * vmargin); + var barWidth = ((this.data[i] - this.Get('chart.xmin')) / (this.max - this.Get('chart.xmin'))) * this.graphwidth; + var barX = this.gutterLeft; + + // Account for Y axis pos + if (this.Get('chart.yaxispos') == 'center') { + barWidth /= 2; + barX += halfwidth; + } + + // Set the fill color + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + + // Sequential colors + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = this.Get('chart.colors')[colorIdx++]; + } + + this.context.strokeRect(barX, this.gutterTop + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight); + this.context.fillRect(barX, this.gutterTop + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight); + + this.coords.push([barX, + y + vmargin, + barWidth, + height - (2 * vmargin), + this.context.fillStyle, + this.data[i], + true]); + + /** + * Stacked bar chart + */ + } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') { + + if (this.Get('chart.yaxispos') == 'center') { + alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped'); + } + + var barHeight = height - (2 * vmargin); + + for (j=0; j<this.data[i].length; ++j) { + + + // Set the fill/stroke colors + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[j]; + + + // Sequential colors + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = this.Get('chart.colors')[colorIdx++]; + } + + + var width = (((this.data[i][j]) / (this.max))) * this.graphwidth; + var totalWidth = (RGraph.array_sum(this.data[i]) / this.max) * this.graphwidth; + + this.context.strokeRect(x, this.gutterTop + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) ); + this.context.fillRect(x, this.gutterTop + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) ); + + /** + * Store the coords for tooltips + */ + + // The last property of this array is a boolean which tells you whether the value is the last or not + this.coords.push([x, + y + vmargin, + width, + height - (2 * vmargin), + this.context.fillStyle, + RGraph.array_sum(this.data[i]), + j == (this.data[i].length - 1) + ]); + + x += width; + } + + /** + * A grouped bar chart + */ + } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') { + + for (j=0; j<this.data[i].length; ++j) { + + /** + * Turn on the shadow if need be + */ + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + // Set the fill/stroke colors + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[j]; + + + // Sequential colors + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = this.Get('chart.colors')[colorIdx++]; + } + + var width = ((this.data[i][j] - this.Get('chart.xmin')) / (this.max - this.Get('chart.xmin'))) * (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight ); + var individualBarHeight = (height - (2 * vmargin)) / this.data[i].length; + + var startX = this.gutterLeft; + var startY = y + vmargin + (j * individualBarHeight); + + // Account for the Y axis being in the middle + if (this.Get('chart.yaxispos') == 'center') { + width /= 2; + startX += halfwidth; + } + + if (width < 0) { + startX += width; + width *= -1; + } + + this.context.strokeRect(startX, startY, width, individualBarHeight); + this.context.fillRect(startX, startY, width, individualBarHeight); + + this.coords.push([startX, + startY, + width, + individualBarHeight, + this.context.fillStyle, + this.data[i][j], + true]); + } + } + + this.context.closePath(); + } + + this.context.fill(); + this.context.stroke(); + + + + /** + * Now the bars are stroke()ed, turn off the shadow + */ + RGraph.NoShadow(this); + + this.RedrawBars(); + } + + + /** + * This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars + */ + RGraph.HBar.prototype.RedrawBars = function () + { + if (this.Get('chart.noredraw')) { + return; + } + + var coords = this.coords; + + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + var color = this.Get('chart.text.color'); + + RGraph.NoShadow(this); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + for (var i=0; i<coords.length; ++i) { + + if (this.Get('chart.shadow')) { + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = coords[i][4]; + this.context.lineWidth = 1; + this.context.strokeRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]); + this.context.fillRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]); + this.context.fill(); + this.context.stroke(); + } + + /** + * Draw labels "above" the bar + */ + if (this.Get('chart.labels.above') && coords[i][6]) { + + this.context.fillStyle = color; + this.context.strokeStyle = 'black'; + RGraph.NoShadow(this); + + var border = (coords[i][0] + coords[i][2] + 7 + this.context.measureText(this.Get('chart.units.pre') + this.coords[i][5] + this.Get('chart.units.post')).width) > RGraph.GetWidth(this) ? true : false; + + RGraph.Text(this.context, + font, + size, + coords[i][0] + coords[i][2] + (border ? -5 : 5), + coords[i][1] + (coords[i][3] / 2), + RGraph.number_format(this, (this.coords[i][5]).toFixed(this.Get('chart.labels.above.decimals')), this.Get('chart.units.pre'), this.Get('chart.units.post')), + 'center', + border ? 'right' : 'left', + border, + null, + border ? 'rgba(255,255,255,0.9)' : null); + } + } + } + + + /******************************************************* + * This function can be used to get the appropriate bar information (if any) + * + * @param e Event object + * @return Appriate bar information (if any) + *******************************************************/ + RGraph.HBar.prototype.getBar = function (e) + { + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0,len=obj.coords.length; i<len; i++) { + + var mouseX = mouseCoords[0]; // In relation to the canvas + var mouseY = mouseCoords[1]; // In relation to the canvas + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + var idx = i; + + if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) { + return [left, top, width, height, idx]; + } + } + } diff --git a/schall/static/RGraph/libraries/RGraph.hprogress.js b/schall/static/RGraph/libraries/RGraph.hprogress.js new file mode 100644 index 0000000..4e8fd59 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.hprogress.js @@ -0,0 +1,589 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The progress bar constructor + * + * @param int id The ID of the canvas tag + * @param int value The indicated value of the meter. + * @param int max The end value (the upper most) of the meter + */ + RGraph.HProgress = function (id, value, max) + { + this.id = id; + this.max = max; + this.value = value; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.canvas.__object__ = this; + this.type = 'hprogress'; + this.coords = []; + this.isRGraph = true; + this.currentValue = null; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + this.properties = { + 'chart.min': 0, + 'chart.colors': ['#0c0'], + 'chart.strokestyle': '#999', + 'chart.tickmarks': true, + 'chart.tickmarks.color': 'black', + 'chart.tickmarks.inner': false, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.numticks': 10, + 'chart.numticks.inner': 50, + 'chart.background.color': '#eee', + 'chart.shadow': false, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.shadow.blur': 3, + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.tooltips': [], + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.mode': 'canvas', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.background': true, + 'chart.zoom.thumbnail.width': 100, + 'chart.zoom.thumbnail.height': 100, + 'chart.arrows': false, + 'chart.margin': 0, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.label.inner': false, + 'chart.adjustable': false, + 'chart.scale.decimals': 0, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'gutter', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': false, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.key.color.shape': 'square', + 'chart.labels.position': 'bottom' + } + + // Check for support + if (!this.canvas) { + alert('[PROGRESS] No canvas support'); + return; + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A generic setter + * + * @param string name The name of the property to set + * @param string value The value of the poperty + */ + RGraph.HProgress.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A generic getter + * + * @param string name The name of the property to get + */ + RGraph.HProgress.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * Draws the progress bar + */ + RGraph.HProgress.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * This is new in May 2011 and facilitates individual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Figure out the width and height + this.width = this.canvas.width - this.gutterLeft - this.gutterRight; + this.height = this.canvas.height - this.gutterTop - this.gutterBottom; + this.coords = []; + + this.Drawbar(); + this.DrawTickMarks(); + this.DrawLabels(); + + this.context.stroke(); + this.context.fill(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Alternatively, show the tooltip if requested + */ + if (typeof(this.Get('chart.tooltips')) == 'function' || this.Get('chart.tooltips').length) { + + // Need to register this object for redrawing + RGraph.Register(this); + + /** + * Install the window onclick handler + */ + var window_onclick = function () {RGraph.Redraw();} + window.addEventListener('click', window_onclick, false); + RGraph.AddEventListener('window_' + this.id, 'click', window_onclick); + + + /** + * Install the onclick event handler for the tooltips + */ + var canvas_onclick_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Redraw(); + + /** + * Was the mouse over the bar + */ + if (bar) { + + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]); + + /** + * Show a tooltip if it's defined + */ + if (text) { + + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + + obj.context.strokeRect(bar[1], bar[2], bar[3], bar[4]); + obj.context.fillRect(bar[1], bar[2], bar[3], bar[4]); + + obj.context.stroke(); + obj.context.fill(); + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]); + } + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); + + /** + * If the cursor is over a hotspot, change the cursor to a hand + */ + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + // Is there a focused bar + if (bar){ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]); + + if (text) { + canvas.style.cursor = 'pointer'; + return; + } + } + + canvas.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + + // Draw the key if necessary + if (this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Instead of using RGraph.common.adjusting.js, handle them here + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + /** + * Draws the bar + */ + RGraph.HProgress.prototype.Drawbar = function () + { + // Set a shadow if requested + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + // Draw the shadow for MSIE + if (RGraph.isIE8() && this.Get('chart.shadow')) { + this.context.fillStyle = this.Get('chart.shadow.color'); + this.context.fillRect(this.gutterLeft + this.Get('chart.shadow.offsetx'), this.gutterTop + this.Get('chart.shadow.offsety'), this.width, this.height); + } + + // Draw the outline + this.context.fillStyle = this.Get('chart.background.color'); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height); + this.context.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height); + + // Turn off any shadow + RGraph.NoShadow(this); + + this.context.fillStyle = this.Get('chart.color'); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + var margin = this.Get('chart.margin'); + + // Draw the actual bar itself + var barWidth = Math.min(this.width, ((RGraph.array_sum(this.value) - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * this.width); + + if (this.Get('chart.tickmarks.inner')) { + + var spacing = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.Get('chart.numticks.inner'); + + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.strokestyle'); + + this.context.beginPath(); + for (var x = this.gutterLeft; x<RGraph.GetWidth(this) - this.gutterRight; x+=spacing) { + this.context.moveTo(x, this.gutterTop); + this.context.lineTo(x, this.gutterTop + 2); + + this.context.moveTo(x, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(x, RGraph.GetHeight(this) - this.gutterBottom - 2); + } + this.context.stroke(); + } + + /** + * This bit draws the actual progress bar + */ + if (typeof(this.value) == 'number') { + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + this.context.strokeRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin); + this.context.fillRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin); + + // Store the coords + this.coords.push([this.gutterLeft, + this.gutterTop + margin, + barWidth, + this.height - margin - margin]); + + } else if (typeof(this.value) == 'object') { + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + var startPoint = this.gutterLeft; + + for (var i=0; i<this.value.length; ++i) { + + var segmentLength = (this.value[i] / RGraph.array_sum(this.value)) * barWidth; + this.context.fillStyle = this.Get('chart.colors')[i]; + + this.context.strokeRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin); + this.context.fillRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin); + + + // Store the coords + this.coords.push([startPoint, + this.gutterTop + margin, + segmentLength, + this.height - margin - margin]); + + startPoint += segmentLength; + } + } + + /** + * Draw the arrows indicating the level if requested + */ + if (this.Get('chart.arrows')) { + var x = this.gutterLeft + barWidth; + var y = this.gutterTop; + + this.context.lineWidth = 1; + this.context.fillStyle = 'black'; + this.context.strokeStyle = 'black'; + + this.context.beginPath(); + this.context.moveTo(x, y - 3); + this.context.lineTo(x + 2, y - 7); + this.context.lineTo(x - 2, y - 7); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + this.context.beginPath(); + this.context.moveTo(x, y + this.height + 4); + this.context.lineTo(x + 2, y + this.height + 9); + this.context.lineTo(x - 2, y + this.height + 9); + this.context.closePath(); + + this.context.stroke(); + this.context.fill() + + + /** + * Draw the "in-bar" label + */ + if (this.Get('chart.label.inner')) { + this.context.beginPath(); + this.context.fillStyle = 'black'; + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') + 2, this.gutterLeft + barWidth + 5, RGraph.GetHeight(this) / 2, String(this.Get('chart.units.pre') + this.value + this.Get('chart.units.post')), 'center', 'left'); + this.context.fill(); + } + } + + } + + /** + * The function that draws the tick marks. Apt name... + */ + RGraph.HProgress.prototype.DrawTickMarks = function () + { + var context = this.context; + + context.strokeStyle = this.Get('chart.tickmarks.color'); + + if (this.Get('chart.tickmarks')) { + + this.context.beginPath(); + + // This is used by the label function below + this.tickInterval = this.width / this.Get('chart.numticks'); + + var start = this.Get('chart.tickmarks.zerostart') ? 0 : this.tickInterval; + + if (this.Get('chart.labels.position') == 'top') { + for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) { + context.moveTo(i, this.gutterTop); + context.lineTo(i, this.gutterTop - 4); + } + + } else { + + for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) { + context.moveTo(i, this.gutterTop + this.height); + context.lineTo(i, this.gutterTop + this.height + 4); + } + } + + this.context.stroke(); + } + } + + + /** + * The function that draws the labels + */ + RGraph.HProgress.prototype.DrawLabels = function () + { + var context = this.context; + this.context.fillStyle = this.Get('chart.text.color'); + + var xPoints = []; + var yPoints = []; + + for (i=0; i<this.Get('chart.numticks'); i++) { + + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + + if (this.Get('chart.labels.position') == 'top') { + var x = this.width * (i/this.Get('chart.numticks')) + this.gutterLeft + (this.width / this.Get('chart.numticks')); + var y = this.gutterTop - 6; + var valign = 'bottom'; + } else { + var x = this.width * (i/this.Get('chart.numticks')) + this.gutterLeft + (this.width / this.Get('chart.numticks')); + var y = this.height + this.gutterTop + 4; + var valign = 'top'; + } + + RGraph.Text(this.context,font,size,x,y,this.Get('chart.units.pre') + String((((this.max - this.Get('chart.min')) / this.Get('chart.numticks') ) * (i + 1) + this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),valign,'center'); + } + + if (this.Get('chart.tickmarks.zerostart')) { + if (this.Get('chart.labels.position') == 'top') { + RGraph.Text(this.context,font,size,this.gutterLeft,this.gutterTop - 6,this.Get('chart.units.pre') + Number(this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post'),'bottom','center'); + } else { + RGraph.Text(this.context,font,size,this.gutterLeft,this.canvas.height - this.gutterBottom + 5,this.Get('chart.units.pre') + Number(this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post'),'top','center'); + } + } + + + + // Draw the title text + if (this.Get('chart.title')) { + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + this.gutterTop + this.Get('chart.text.size'), + 0, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + } + } + + + /** + * Returns the focused bar + * + * @param event e The event object + */ + RGraph.HProgress.prototype.getBar = function (e) + { + var obj = e.target.__object__; + var mouseCoords = RGraph.getMouseXY(e) + + for (var i=0; i<obj.coords.length; i++) { + + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + var idx = i; + + if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) { + return [obj, left, top, width, height, idx]; + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.led.js b/schall/static/RGraph/libraries/RGraph.led.js new file mode 100644 index 0000000..8e7d2b7 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.led.js @@ -0,0 +1,232 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The LED lights constructor + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.LED = function (id, text) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'led'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + /** + * Set the string that is to be displayed + */ + this.text = text; + + /** + * The letters and numbers + */ + this.lights = { + 'a': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]], + 'b': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0]], + 'c': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,1],[0,1,1,0]], + 'd': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,0]], + 'e': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]], + 'f': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], + 'g': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,0,1,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + 'h': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]], + 'i': [[0,1,1,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,1,1]], + 'j': [[0,1,1,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,0,0]], + 'k': [[1,0,0,1],[1,0,0,1],[1,0,1,0],[1,1,0,0],[1,0,1,0],[1,0,0,1],[1,0,0,1]], + 'l': [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]], + 'm': [[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]], + 'n': [[1,0,0,1],[1,1,0,1],[1,0,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]], + 'o': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + 'p': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], + 'q': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,1,1],[0,1,1,1]], + 'r': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,1,0],[1,0,0,1],[1,0,0,1]], + 's': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[0,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]], + 't': [[1,1,1,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], + 'u': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + 'v': [[1,0,1],[1,0,1],[1,0,1],[1,0,1],[1,0,1],[0,1,0],[0,1,0]], + 'w': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1],[0,1,1,0]], + 'x': [[0,1,0,1],[0,1,0,1],[0,1,0,1],[0,0,1,0],[0,1,0,1],[0,1,0,1],[0,1,0,1]], + 'y': [[0,1,0,1],[0,1,0,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]], + 'z': [[1,1,1,1],[0,0,0,1],[0,0,1,0],[0,0,1,0],[0,1,0,0],[1,0,0,0],[1,1,1,1]], + ' ': [[],[],[],[],[], [], []], + '0': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + '1': [[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,1,1]], + '2': [[0,1,1,0],[1,0,0,1],[0,0,0,1],[0,0,1,0],[0,1,,0],[1,0,0,0],[1,1,1,1]], + '3': [[0,1,1,0],[1,0,0,1],[0,0,0,1],[0,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]], + '4': [[1,0,0,0],[1,0,0,0],[1,0,1,0],[1,0,1,0],[1,1,1,1],[0,0,1,0],[0,0,1,0]], + '5': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]], + '6': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + '7': [[1,1,1,1],[0,0,0,1],[0,0,0,1],[0,0,1,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], + '8': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0]], + '9': [[0,1,1,1],[1,0,0,1],[1,0,0,1],[0,1,1,1],[0,0,0,1],[0,0,0,1],[0,0,0,1]] + } + + // Various config type stuff + this.properties = { + 'chart.background': 'white', + 'chart.dark': '#eee', + 'chart.light': '#f66', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.width': null, + 'chart.height': null + } + + // Check for support + if (!this.canvas) { + alert('[LED] No canvas support'); + return; + } + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.LED.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.LED.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * This draws the LEDs + */ + RGraph.LED.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + // First clear the canvas, using the background colour + RGraph.Clear(this.canvas, this.Get('chart.background')); + + for (var l=0; l<this.text.length; l++) { + this.DrawLetter(this.text.charAt(l), l); + } + + /** + * Set the title attribute on the canvas + */ + this.canvas.title = RGraph.rtrim(this.text); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws a single letter + * + * @param string lights The lights to draw to draw + * @param int index The position of the letter + */ + RGraph.LED.prototype.DrawLetter = function (letter, index) + { + var light = this.Get('chart.light'); + var dark = this.Get('chart.dark'); + var lights = (this.lights[letter] ? this.lights[letter] : [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]); + var lwidth = RGraph.GetWidth(this) / this.text.length; + var diameter = lwidth / 5; + var radius = diameter / 2; + + var lheight = diameter * 7; + if (lheight > RGraph.GetHeight(this)) { + lheight = RGraph.GetHeight(this); + diameter = (lheight / 7); + radius = (diameter / 2); + lwidth = diameter * 5; + } + + for (var i=0; i<7; i++) { + for (var j=0; j<5; j++) { + + var x = (j * diameter) + (index * lwidth) + radius; + var y = ((i * diameter)) + radius; + + // Draw a circle + this.context.fillStyle = (lights[i][j] ? light : dark); + this.context.strokeStyle = (lights[i][j] ? '#ccc' : 'rgba(0,0,0,0)'); + this.context.beginPath(); + this.context.arc(x, y, radius, 0, 6.28, 0); + + this.context.stroke(); + this.context.fill(); + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.line.js b/schall/static/RGraph/libraries/RGraph.line.js new file mode 100644 index 0000000..e641f87 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.line.js @@ -0,0 +1,2217 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The line chart constructor + * + * @param object canvas The cxanvas object + * @param array data The chart data + * @param array ... Other lines to plot + */ + RGraph.Line = function (id) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'line'; + this.max = 0; + this.coords = []; + this.coords2 = []; + this.coordsKey = []; + this.hasnegativevalues = false; + this.isRGraph = true; + + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config type stuff + this.properties = { + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': 1, + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 25, + 'chart.background.grid.vsize': 25, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit': true, + 'chart.background.grid.autofit.align': false, + 'chart.background.grid.autofit.numhlines': 5, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.background.hbars': null, + 'chart.background.image': null, + 'chart.labels': null, + 'chart.labels.ingraph': null, + 'chart.labels.above': false, + 'chart.labels.above.size': 8, + 'chart.xtickgap': 20, + 'chart.smallxticks': 3, + 'chart.largexticks': 5, + 'chart.ytickgap': 20, + 'chart.smallyticks': 3, + 'chart.largeyticks': 5, + 'chart.numyticks': 10, + 'chart.linewidth': 1.01, + 'chart.colors': ['red', '#0f0', '#00f', '#f0f', '#ff0', '#0ff'], + 'chart.hmargin': 0, + 'chart.tickmarks.dot.color': 'white', + 'chart.tickmarks': null, + 'chart.tickmarks.linewidth': null, + 'chart.ticksize': 3, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.tickdirection': -1, + 'chart.yaxispoints': 5, + 'chart.fillstyle': null, + 'chart.xaxispos': 'bottom', + 'chart.yaxispos': 'left', + 'chart.xticks': null, + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.ymin': null, + 'chart.ymax': null, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': 0.5, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.shadow': false, + 'chart.shadow.offsetx': 2, + 'chart.shadow.offsety': 2, + 'chart.shadow.blur': 3, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.tooltips': null, + 'chart.tooltips.hotspot.xonly': false, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': '#999', + 'chart.highlight.fill': 'white', + 'chart.stepped': false, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': null, + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.key.interactive': false, + 'chart.contextmenu': null, + 'chart.ylabels': true, + 'chart.ylabels.count': 5, + 'chart.ylabels.inside': false, + 'chart.ylabels.invert': false, + 'chart.xlabels.inside': false, + 'chart.xlabels.inside.color': 'rgba(255,255,255,0.5)', + 'chart.noaxes': false, + 'chart.noyaxis': false, + 'chart.noxaxis': false, + 'chart.noendxtick': false, + 'chart.units.post': '', + 'chart.units.pre': '', + 'chart.scale.decimals': null, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.axesontop': false, + 'chart.filled': false, + 'chart.filled.range': false, + 'chart.filled.accumulative': true, + 'chart.variant': null, + 'chart.axis.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.backdrop': false, + 'chart.backdrop.size': 30, + 'chart.backdrop.alpha': 0.2, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.noredraw': false, + 'chart.outofbounds': false, + 'chart.chromefix': true, + 'chart.animation.factor': 1, + 'chart.animation.unfold.x': false, + 'chart.animation.unfold.y': true, + 'chart.animation.unfold.initial': 2, + 'chart.curvy': false, + 'chart.curvy.factor': 0.25, + 'chart.line.visible': true + } + + /** + * Change null arguments to empty arrays + */ + for (var i=1; i<arguments.length; ++i) { + if (typeof(arguments[i]) == 'null' || !arguments[i]) { + arguments[i] = []; + } + } + + + /** + * Store the original data. Thiss also allows for giving arguments as one big array. + */ + this.original_data = []; + + for (var i=1; i<arguments.length; ++i) { + if (arguments[1] && typeof(arguments[1]) == 'object' && arguments[1][0] && typeof(arguments[1][0]) == 'object' && arguments[1][0].length) { + + var tmp = []; + + for (var i=0; i<arguments[1].length; ++i) { + tmp[i] = RGraph.array_clone(arguments[1][i]); + } + + for (var j=0; j<tmp.length; ++j) { + this.original_data[j] = RGraph.array_clone(tmp[j]); + } + + } else { + this.original_data[i - 1] = RGraph.array_clone(arguments[i]); + } + } + + // Check for support + if (!this.canvas) { + alert('[LINE] Fatal error: no canvas support'); + return; + } + + /** + * Store the data here as one big array + */ + this.data_arr = []; + + for (var i=1; i<arguments.length; ++i) { + for (var j=0; j<arguments[i].length; ++j) { + this.data_arr.push(arguments[i][j]); + } + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getPoint; + } + + + /** + * An all encompassing accessor + * + * @param string name The name of the property + * @param mixed value The value of the property + */ + RGraph.Line.prototype.Set = function (name, value) + { + // Consolidate the tooltips + if (name == 'chart.tooltips') { + + var tooltips = []; + + for (var i=1; i<arguments.length; i++) { + if (typeof(arguments[i]) == 'object' && arguments[i][0]) { + for (var j=0; j<arguments[i].length; j++) { + tooltips.push(arguments[i][j]); + } + + } else if (typeof(arguments[i]) == 'function') { + tooltips = arguments[i]; + + } else { + tooltips.push(arguments[i]); + } + } + + // Because "value" is used further down at the end of this function, set it to the expanded array os tooltips + value = tooltips; + } + + /** + * Reverse the tickmarks to make them correspond to the right line + */ + if (name == 'chart.tickmarks' && typeof(value) == 'object' && value) { + value = RGraph.array_reverse(value); + } + + /** + * Inverted Y axis should show the bottom end of the scale + */ + if (name == 'chart.ylabels.invert' && value && this.Get('chart.ymin') == null) { + this.Set('chart.ymin', 0); + } + + /** + * If (buggy) Chrome and the linewidth is 1, change it to 1.01 + */ + if (name == 'chart.linewidth' && navigator.userAgent.match(/Chrome/)) { + if (value == 1) { + value = 1.01; + + } else if (RGraph.is_array(value)) { + for (var i=0; i<value.length; ++i) { + if (typeof(value[i]) == 'number' && value[i] == 1) { + value[i] = 1.01; + } + } + } + } + + /** + * Check for xaxispos + */ + if (name == 'chart.xaxispos' ) { + if (value != 'bottom' && value != 'center' && value != 'top') { + alert('[LINE] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center'); + value = 'center'; + } + } + + /** + * Check chart.curvy.factor - should be 0 - 0.5 + */ + if (name == 'chart.curvy.factor' && (value < 0 || value > 0.5)) { + alert('[LINE] chart.curvy.factor should be between 0 and 0.5 - setting it to 0.25'); + value = 0.25; + } + + this.properties[name] = value; + } + + + /** + * An all encompassing accessor + * + * @param string name The name of the property + */ + RGraph.Line.prototype.Get = function (name) + { + return this.properties[name]; + } + + + /** + * The function you call to draw the line chart + * + * @param bool An optional bool used internally to ditinguish whether the + * line chart is being called by the bar chart + */ + RGraph.Line.prototype.Draw = function () + { + // MUST be the FIRST thing done - Hand over drawing to the bar chart if it's a combo back to the Bar chart + if (this.__bar__ && !arguments[0]) { + this.__bar__.Draw(); + return; + } + + // MUST be the SECOND thing done! + if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) { + RGraph.DrawBackgroundImage(this); + return; + } + + if (this.__bar__ && this.Get('chart.tooltips.highlight')) { + //this.Set('chart.tooltips.highlight', false); + } + + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + + /** + * Check for Chrome 6 and shadow + * + * TODO Remove once it's been fixed (for a while) + * SEARCH TAGS: CHROME FIX SHADOW BUG + */ + if ( this.Get('chart.shadow') + && navigator.userAgent.match(/Chrome/) + && this.Get('chart.linewidth') <= 1 + && this.Get('chart.chromefix') + && this.Get('chart.shadow.blur') > 0) { + alert('[RGRAPH WARNING] Chrome has a shadow bug, meaning you should increase the linewidth to at least 1.01'); + } + + + // Reset the data back to that which was initially supplied + this.data = RGraph.array_clone(this.original_data); + + + // Reset the max value + this.max = 0; + + /** + * Reverse the datasets so that the data and the labels tally + * COMMENTED OUT 15TH AUGUST 2011 + */ + //this.data = RGraph.array_reverse(this.data); + + if (this.Get('chart.filled') && !this.Get('chart.filled.range') && this.data.length > 1 && this.Get('chart.filled.accumulative')) { + + var accumulation = []; + + for (var set=0; set<this.data.length; ++set) { + for (var point=0; point<this.data[set].length; ++point) { + this.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) + this.data[set][point]; + accumulation[point] = this.data[set][point]; + } + } + } + + /** + * Get the maximum Y scale value + */ + if (this.Get('chart.ymax')) { + + this.max = this.Get('chart.ymax'); + this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + + this.scale = [ + ( ((this.max - this.min) * (1/5)) + this.min).toFixed(this.Get('chart.scale.decimals')), + ( ((this.max - this.min) * (2/5)) + this.min).toFixed(this.Get('chart.scale.decimals')), + ( ((this.max - this.min) * (3/5)) + this.min).toFixed(this.Get('chart.scale.decimals')), + ( ((this.max - this.min) * (4/5)) + this.min).toFixed(this.Get('chart.scale.decimals')), + this.max.toFixed(this.Get('chart.scale.decimals')) + ]; + + // Check for negative values + if (!this.Get('chart.outofbounds')) { + for (dataset=0; dataset<this.data.length; ++dataset) { + for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) { + + // Check for negative values + this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues; + } + } + } + + } else { + + this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + + // Work out the max Y value + for (dataset=0; dataset<this.data.length; ++dataset) { + for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) { + + this.max = Math.max(this.max, this.data[dataset][datapoint] ? Math.abs(parseFloat(this.data[dataset][datapoint])) : 0); + + // Check for negative values + if (!this.Get('chart.outofbounds')) { + this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues; + } + } + } + + // 20th April 2009 - moved out of the above loop + this.scale = RGraph.getScale(Math.abs(parseFloat(this.max)), this); + this.max = this.scale[4] ? this.scale[4] : 0; + + if (this.Get('chart.ymin')) { + this.scale[0] = ((this.max - this.Get('chart.ymin')) * (1/5)) + this.Get('chart.ymin'); + this.scale[1] = ((this.max - this.Get('chart.ymin')) * (2/5)) + this.Get('chart.ymin'); + this.scale[2] = ((this.max - this.Get('chart.ymin')) * (3/5)) + this.Get('chart.ymin'); + this.scale[3] = ((this.max - this.Get('chart.ymin')) * (4/5)) + this.Get('chart.ymin'); + this.scale[4] = ((this.max - this.Get('chart.ymin')) * (5/5)) + this.Get('chart.ymin'); + } + + if (typeof(this.Get('chart.scale.decimals')) == 'number') { + this.scale[0] = Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')); + this.scale[1] = Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')); + this.scale[2] = Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')); + this.scale[3] = Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')); + this.scale[4] = Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')); + } + } + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Reset the coords array otherwise it will keep growing + */ + this.coords = []; + + /** + * 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.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + this.halfgrapharea = this.grapharea / 2; + this.halfTextHeight = this.Get('chart.text.size') / 2; + + // Check the combination of the X axis position and if there any negative values + // + // 19th Dec 2010 - removed for Opera since it can be reported incorrectly whn there + // are multiple graphs on the page + if (this.Get('chart.xaxispos') == 'bottom' && this.hasnegativevalues && navigator.userAgent.indexOf('Opera') == -1) { + alert('[LINE] You have negative values and the X axis is at the bottom. This is not good...'); + } + + if (this.Get('chart.variant') == '3d') { + RGraph.Draw3DAxes(this); + } + + // Progressively Draw the chart + RGraph.background.Draw(this); + + /** + * Draw any horizontal bars that have been defined + */ + if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) { + RGraph.DrawBars(this); + } + + if (this.Get('chart.axesontop') == false) { + this.DrawAxes(); + } + + /** + * Handle the appropriate shadow color. This now facilitates an array of differing + * shadow colors + */ + var shadowColor = this.Get('chart.shadow.color'); + + //if (typeof(shadowColor) == 'object') { + // shadowColor = RGraph.array_reverse(RGraph.array_clone(this.Get('chart.shadow.color'))); + //} + + + for (var i=0, j=0; i<this.data.length; i++, j++) { + + this.context.beginPath(); + + /** + * Turn on the shadow if required + */ + if (this.Get('chart.shadow') && !this.Get('chart.filled')) { + + /** + * Accommodate an array of shadow colors as well as a single string + */ + if (typeof(shadowColor) == 'object' && shadowColor[i - 1]) { + this.context.shadowColor = shadowColor[i]; + } else if (typeof(shadowColor) == 'object') { + this.context.shadowColor = shadowColor[0]; + } else if (typeof(shadowColor) == 'string') { + this.context.shadowColor = shadowColor; + } + + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + + } else if (this.Get('chart.filled') && this.Get('chart.shadow')) { + alert('[LINE] Shadows are not permitted when the line is filled'); + } + + /** + * Draw the line + */ + + if (this.Get('chart.fillstyle')) { + if (typeof(this.Get('chart.fillstyle')) == 'object' && this.Get('chart.fillstyle')[j]) { + var fill = this.Get('chart.fillstyle')[j]; + + } else if (typeof(this.Get('chart.fillstyle')) == 'string') { + var fill = this.Get('chart.fillstyle'); + + } else { + alert('[LINE] Warning: chart.fillstyle must be either a string or an array with the same number of elements as you have sets of data'); + } + } else if (this.Get('chart.filled')) { + var fill = this.Get('chart.colors')[j]; + + } else { + var fill = null; + } + + /** + * Figure out the tickmark to use + */ + if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'object') { + var tickmarks = this.Get('chart.tickmarks')[i]; + } else if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'string') { + var tickmarks = this.Get('chart.tickmarks'); + } else if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'function') { + var tickmarks = this.Get('chart.tickmarks'); + } else { + var tickmarks = null; + } + + + this.DrawLine(this.data[i], + this.Get('chart.colors')[j], + fill, + this.GetLineWidth(j), + tickmarks, + i); + + this.context.stroke(); + } + + + + + + + + + + + + + + /** + * If tooltips are defined, handle them + */ + if (this.Get('chart.tooltips') && (this.Get('chart.tooltips').length || typeof(this.Get('chart.tooltips')) == 'function')) { + + // Need to register this object for redrawing + if (this.Get('chart.tooltips.highlight')) { + RGraph.Register(this); + } + + RGraph.InstallLineTooltipEventListeners(this); + + /** + * If there's a bar, install the event listeners + */ + if (this.__bar__) { + RGraph.InstallBarTooltipEventListeners(this.__bar__); + } + } + + + + + + + + + + + + + + + /** + * If the axes have been requested to be on top, do that + */ + if (this.Get('chart.axesontop')) { + this.DrawAxes(); + } + + /** + * Draw the labels + */ + this.DrawLabels(); + + /** + * Draw the range if necessary + */ + this.DrawRange(); + + // Draw a key if necessary + if (this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + /** + * Draw " above" labels if enabled + */ + if (this.Get('chart.labels.above')) { + this.DrawAboveLabels(); + } + + /** + * Draw the "in graph" labels + */ + RGraph.DrawInGraphLabels(this); + + /** + * Draw crosschairs + */ + RGraph.DrawCrosshairs(this); + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * Redraw the lines if a filled range is on the cards + */ + if (this.Get('chart.filled') && this.Get('chart.filled.range') && this.data.length == 2) { + + this.context.beginPath(); + var len = this.coords.length / 2; + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.strokeStyle = this.Get('chart.colors')[0]; + + for (var i=0; i<len; ++i) { + if (i == 0) { + this.context.moveTo(this.coords[i][0], this.coords[i][1]); + } else { + this.context.lineTo(this.coords[i][0], this.coords[i][1]); + } + } + + this.context.stroke(); + + + this.context.beginPath(); + + if (this.Get('chart.colors')[1]) { + this.context.strokeStyle = this.Get('chart.colors')[1]; + } + + for (var i=this.coords.length - 1; i>=len; --i) { + if (i == (this.coords.length - 1) ) { + this.context.moveTo(this.coords[i][0], this.coords[i][1]); + } else { + this.context.lineTo(this.coords[i][0], this.coords[i][1]); + } + } + + this.context.stroke(); + } else if (this.Get('chart.filled') && this.Get('chart.filled.range')) { + alert('[LINE] You must have only two sets of data for a filled range chart'); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail') { + RGraph.ShowZoomWindow(this); + } + + /** + * This function enables the zoom in area mode + */ + if (this.Get('chart.zoom.mode') == 'area') { + RGraph.ZoomArea(this); + } + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * This function enables adjustments + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the axes + */ + RGraph.Line.prototype.DrawAxes = function () + { + // Don't draw the axes? + if (this.Get('chart.noaxes')) { + return; + } + + // Turn any shadow off + RGraph.NoShadow(this); + + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.axis.color'); + this.context.beginPath(); + + // Draw the X axis + if (this.Get('chart.noxaxis') == false) { + if (this.Get('chart.xaxispos') == 'center') { + this.context.moveTo(this.gutterLeft, (this.grapharea / 2) + this.gutterTop); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, (this.grapharea / 2) + this.gutterTop); + } else if (this.Get('chart.xaxispos') == 'top') { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop); + } else { + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + } + } + + // Draw the Y axis + if (this.Get('chart.noyaxis') == false) { + if (this.Get('chart.yaxispos') == 'left') { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom ); + } else { + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + } + } + + /** + * Draw the X tickmarks + */ + if (this.Get('chart.noxaxis') == false) { + + if (this.data[0].length > 0) { + var xTickInterval = (this.canvas.width - this.gutterLeft - this.gutterRight) / (this.Get('chart.xticks') ? this.Get('chart.xticks') : (this.data[0].length - 1)); + } + + if (!xTickInterval || xTickInterval <= 0) { + xTickInterval = (this.canvas.width - this.gutterLeft - this.gutterRight) / (this.Get('chart.labels') && this.Get('chart.labels').length ? this.Get('chart.labels').length - 1 : 10); + } + + for (x=this.gutterLeft + (this.Get('chart.yaxispos') == 'left' ? xTickInterval : 0); x<=(this.canvas.width - this.gutterRight + 1 ); x+=xTickInterval) { + + if (this.Get('chart.yaxispos') == 'right' && x >= (this.canvas.width - this.gutterRight - 1) ) { + break; + } + + // If the last tick is not desired... + if (this.Get('chart.noendxtick')) { + if (this.Get('chart.yaxispos') == 'left' && x >= (this.canvas.width - this.gutterRight)) { + break; + } else if (this.Get('chart.yaxispos') == 'right' && x == this.gutterLeft) { + continue; + } + } + + var yStart = this.Get('chart.xaxispos') == 'center' ? (this.gutterTop + (this.grapharea / 2)) - 3 : RGraph.GetHeight(this) - this.gutterBottom; + var yEnd = this.Get('chart.xaxispos') == 'center' ? yStart + 6 : RGraph.GetHeight(this) - this.gutterBottom - (x % 60 == 0 ? this.Get('chart.largexticks') * this.Get('chart.tickdirection') : this.Get('chart.smallxticks') * this.Get('chart.tickdirection')); + + if (this.Get('chart.xaxispos') == 'top') { + yStart = this.gutterTop - 3; + yEnd = this.gutterTop; + } + + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + } + + // Draw an extra tickmark if there is no X axis, but there IS a Y axis + } else if (this.Get('chart.noyaxis') == false) { + if (this.Get('chart.yaxispos') == 'left') { + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft - this.Get('chart.smallyticks'), RGraph.GetHeight(this) - this.gutterBottom); + } else { + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks'), RGraph.GetHeight(this) - this.gutterBottom); + } + } + + /** + * Draw the Y tickmarks + */ + var numyticks = this.Get('chart.numyticks'); + + if (this.Get('chart.noyaxis') == false) { + var counter = 0; + var adjustment = 0; + + if (this.Get('chart.yaxispos') == 'right') { + adjustment = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight); + } + + // X axis at the center + if (this.Get('chart.xaxispos') == 'center') { + var interval = (this.grapharea / numyticks); + var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks')); + + // Draw the upper halves Y tick marks + for (y=this.gutterTop; y < (this.grapharea / 2) + this.gutterTop; y+=interval) { + this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y); + this.context.lineTo(lineto, y); + } + + // Draw the lower halves Y tick marks + for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) { + this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y); + this.context.lineTo(lineto, y); + } + + // X axis at the top + } else if (this.Get('chart.xaxispos') == 'top') { + var interval = (this.grapharea / numyticks); + var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks')); + + // Draw the Y tick marks + for (y=this.gutterTop + interval; y <=this.grapharea + this.gutterTop; y+=interval) { + this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y); + this.context.lineTo(lineto, y); + } + + // If there's no X axis draw an extra tick + if (this.Get('chart.noxaxis')) { + this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), this.gutterTop); + this.context.lineTo(lineto, this.gutterTop); + } + + // X axis at the bottom + } else { + var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : this.canvas.width - this.gutterRight + this.Get('chart.smallyticks')); + + for (y=this.gutterTop; y < (this.canvas.height - this.gutterBottom) && counter < numyticks; y+=( (this.canvas.height - this.gutterTop - this.gutterBottom) / numyticks) ) { + + this.context.moveTo(this.gutterLeft + adjustment, y); + this.context.lineTo(lineto, y); + + var counter = counter +1; + } + } + + // Draw an extra X tickmark + } else if (this.Get('chart.noxaxis') == false) { + + if (this.Get('chart.yaxispos') == 'left') { + this.context.moveTo(this.gutterLeft, this.Get('chart.xaxispos') == 'top' ? this.gutterTop : RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft, this.Get('chart.xaxispos') == 'top' ? this.gutterTop - this.Get('chart.smallxticks') : RGraph.GetHeight(this) - this.gutterBottom + this.Get('chart.smallxticks')); + } else { + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom + this.Get('chart.smallxticks')); + } + } + + this.context.stroke(); + } + + + /** + * Draw the text labels for the axes + */ + RGraph.Line.prototype.DrawLabels = function () + { + this.context.strokeStyle = 'black'; + this.context.fillStyle = this.Get('chart.text.color'); + this.context.lineWidth = 1; + + // Turn off any shadow + RGraph.NoShadow(this); + + // This needs to be here + var font = this.Get('chart.text.font'); + var text_size = this.Get('chart.text.size'); + var context = this.context; + var canvas = this.canvas; + + // Draw the Y axis labels + if (this.Get('chart.ylabels') && this.Get('chart.ylabels.specific') == null) { + + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5; + var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; + + var numYLabels = this.Get('chart.ylabels.count'); + var bounding = false; + var bgcolor = this.Get('chart.ylabels.inside') ? this.Get('chart.ylabels.inside.color') : null; + + + /** + * If the Y labels are inside the Y axis, invert the alignment + */ + if (this.Get('chart.ylabels.inside') == true && align == 'left') { + xpos -= 10; + align = 'right'; + bounding = true; + + + } else if (this.Get('chart.ylabels.inside') == true && align == 'right') { + xpos += 10; + align = 'left'; + bounding = true; + } + + + + if (this.Get('chart.xaxispos') == 'center') { + var half = this.grapharea / 2; + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + // Draw the upper halves labels + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (0/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, bounding, null, bgcolor); + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (1/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (3/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (2/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (4/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + // Draw the lower halves labels + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (6/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (8/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (7/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (9/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (10/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, (this.scale[4] == '1.0' ? '1.0' : this.scale[4]), units_pre, units_post), null, align, bounding, null, bgcolor); + + } else if (numYLabels == 10) { + + // 10 Y labels + var interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + // This draws the upper halves labels + RGraph.Text(context,font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((i/20) * (this.grapharea) ), RGraph.number_format(this, ((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))),units_pre, units_post), null, align, bounding, null, bgcolor); + + // And this draws the lower halves labels + RGraph.Text(context, font, text_size, xpos, + + this.gutterTop + this.halfTextHeight + ((i/20) * this.grapharea) + (this.grapharea / 2) + (this.grapharea / 20), + + '-' + RGraph.number_format(this, (this.scale[4] - ((this.scale[4] / numYLabels) * (numYLabels - i - 1))).toFixed((this.Get('chart.scale.decimals'))),units_pre, units_post), null, align, bounding, null, bgcolor); + } + + } else { + alert('[LINE SCALE] The number of Y labels must be 1/3/5/10'); + } + + // Draw the lower limit if chart.ymin is specified + if (typeof(this.Get('chart.ymin')) == 'number') { + RGraph.Text(context, font, text_size, xpos, RGraph.GetHeight(this) / 2, RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', align, bounding, null, bgcolor); + } + + // No X axis - so draw 0 + if (this.Get('chart.noxaxis') == true) { + RGraph.Text(context,font,text_size,xpos,this.gutterTop + ( (5/5) * half ) + this.halfTextHeight,this.Get('chart.units.pre') + '0' + this.Get('chart.units.post'),null, align, bounding, null, bgcolor); + } + + // X axis at the top + } else if (this.Get('chart.xaxispos') == 'top') { + + var scale = RGraph.array_reverse(this.scale); + + /** + * Accommodate reversing the Y labels + */ + if (this.Get('chart.ylabels.invert')) { + + scale = RGraph.array_reverse(scale); + + this.context.translate(0, this.grapharea * -0.2); + if (typeof(this.Get('chart.ymin')) == null) { + this.Set('chart.ymin', 0); + } + } + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((1/5) * (this.grapharea ) ), '-' + RGraph.number_format(this, scale[4], units_pre, units_post), null, align, bounding, null, bgcolor); + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((4/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[1], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((2/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[3], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((3/5) * (this.grapharea ) ), '-' + RGraph.number_format(this, scale[2], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((5/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[0], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + } else if (numYLabels == 10) { + + // 10 Y labels + var interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + + RGraph.Text(context,font,text_size,xpos,(2 * interval) + this.gutterTop + this.halfTextHeight + ((i/10) * (this.grapharea) ),'-' + RGraph.number_format(this,(scale[0] - (((scale[0] - this.min) / numYLabels) * (numYLabels - i - 1))).toFixed((this.Get('chart.scale.decimals'))),units_pre,units_post),null,align,bounding,null,bgcolor); + } + + } else { + alert('[LINE SCALE] The number of Y labels must be 1/3/5/10'); + } + + + /** + * Accommodate translating back after reversing the labels + */ + if (this.Get('chart.ylabels.invert')) { + this.context.translate(0, 0 - (this.grapharea * -0.2)); + } + + // Draw the lower limit if chart.ymin is specified + if (typeof(this.Get('chart.ymin')) == 'number') { + RGraph.Text(context,font,text_size,xpos,this.Get('chart.ylabels.invert') ? this.canvas.height - this.gutterBottom : this.gutterTop,'-' + RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center',align,bounding,null,bgcolor); + } + + } else { + + /** + * Accommodate reversing the Y labels + */ + if (this.Get('chart.ylabels.invert')) { + this.scale = RGraph.array_reverse(this.scale); + this.context.translate(0, this.grapharea * 0.2); + if (typeof(this.Get('chart.ymin')) == null) { + this.Set('chart.ymin', 0); + } + } + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((0/5) * (this.grapharea ) ), RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, bounding, null, bgcolor); + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((3/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((1/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((2/5) * (this.grapharea ) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor); + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((4/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor); + } + + } else if (numYLabels == 10) { + + // 10 Y labels + var interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context,font,text_size,xpos,this.gutterTop + this.halfTextHeight + ((i/10) * (this.grapharea) ),RGraph.number_format(this,((((this.scale[4] - this.min) / numYLabels) * (numYLabels - i)) + this.min).toFixed((this.Get('chart.scale.decimals'))),units_pre,units_post),null,align,bounding,null,bgcolor); + } + + } else { + alert('[LINE SCALE] The number of Y labels must be 1/3/5/10'); + } + + + /** + * Accommodate translating back after reversing the labels + */ + if (this.Get('chart.ylabels.invert')) { + this.context.translate(0, 0 - (this.grapharea * 0.2)); + } + + // Draw the lower limit if chart.ymin is specified + if (typeof(this.Get('chart.ymin')) == 'number') { + RGraph.Text(context,font,text_size,xpos,this.Get('chart.ylabels.invert') ? this.gutterTop : RGraph.GetHeight(this) - this.gutterBottom,RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center',align,bounding,null,bgcolor); + } + } + + // No X axis - so draw 0 - but not if the X axis is in the center + if ( this.Get('chart.noxaxis') == true + && this.Get('chart.ymin') == null + && this.Get('chart.xaxispos') != 'center' + ) { + + RGraph.Text(context,font,text_size,xpos,this.Get('chart.xaxispos') == 'top' ? this.gutterTop + this.halfTextHeight: (this.canvas.height - this.gutterBottom + this.halfTextHeight),this.Get('chart.units.pre') + '0' + this.Get('chart.units.post'),null, align, bounding, null, bgcolor); + } + + } else if (this.Get('chart.ylabels') && typeof(this.Get('chart.ylabels.specific')) == 'object') { + + // A few things + var gap = this.grapharea / this.Get('chart.ylabels.specific').length; + var halign = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left'; + var bounding = false; + var bgcolor = null; + var ymin = this.Get('chart.ymin') != null && this.Get('chart.ymin'); + + // Figure out the X coord based on the position of the axis + if (this.Get('chart.yaxispos') == 'left') { + var x = this.gutterLeft - 5; + + if (this.Get('chart.ylabels.inside')) { + x += 10; + halign = 'left'; + bounding = true; + bgcolor = 'rgba(255,255,255,0.5)'; + } + + } else if (this.Get('chart.yaxispos') == 'right') { + var x = this.canvas.width - this.gutterRight + 5; + + if (this.Get('chart.ylabels.inside')) { + x -= 10; + halign = 'right'; + bounding = true; + bgcolor = 'rgba(255,255,255,0.5)'; + } + } + + + // Draw the labels + if (this.Get('chart.xaxispos') == 'center') { + + // Draw the top halfs labels + for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) { + var y = this.gutterTop + (this.grapharea / ((this.Get('chart.ylabels.specific').length ) * 2) * i); + + if (ymin && ymin > 0) { + var y = ((this.grapharea / 2) / (this.Get('chart.ylabels.specific').length - (ymin ? 1 : 0)) ) * i; + y += this.gutterTop; + } + + RGraph.Text(context, font, text_size,x,y,String(this.Get('chart.ylabels.specific')[i]), 'center', halign, bounding, 0, bgcolor); + } + + // Now reverse the labels and draw the bottom half + var reversed_labels = RGraph.array_reverse(this.Get('chart.ylabels.specific')); + + // Draw the bottom halfs labels + for (var i=0; i<reversed_labels.length; ++i) { + var y = (this.grapharea / 2) + this.gutterTop + ((this.grapharea / (reversed_labels.length * 2) ) * (i + 1)); + + if (ymin && ymin > 0) { + var y = ((this.grapharea / 2) / (reversed_labels.length - (ymin ? 1 : 0)) ) * i; + y += this.gutterTop; + y += (this.grapharea / 2); + } + + RGraph.Text(context, font, text_size,x,y,String(reversed_labels[i]), 'center', halign, bounding, 0, bgcolor); + } + + } else if (this.Get('chart.xaxispos') == 'top') { + + // Reverse the labels and draw + var reversed_labels = RGraph.array_reverse(this.Get('chart.ylabels.specific')); + + // Draw the bottom halfs labels + for (var i=0; i<reversed_labels.length; ++i) { + + var y = ((this.grapharea / (reversed_labels.length - (ymin ? 1 : 0)) ) * (i + (ymin ? 0 : 1))); + y = y + this.gutterTop; + + RGraph.Text(context, font, text_size,x,y,String(reversed_labels[i]), 'center', halign, bounding, 0, bgcolor); + } + + } else { + for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) { + var y = this.gutterTop + ((this.grapharea / (this.Get('chart.ylabels.specific').length - (ymin ? 1 : 0) )) * i); + RGraph.Text(context, font, text_size,x,y,String(this.Get('chart.ylabels.specific')[i]), 'center', halign, bounding, 0, bgcolor); + } + } + } + + // Draw the X axis labels + if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) { + + + var yOffset = 13; + var bordered = false; + var bgcolor = null; + + if (this.Get('chart.xlabels.inside')) { + yOffset = -5; + bordered = true; + bgcolor = this.Get('chart.xlabels.inside.color'); + } + + /** + * Text angle + */ + var angle = 0; + var valign = null; + var halign = 'center'; + + if (typeof(this.Get('chart.text.angle')) == 'number' && this.Get('chart.text.angle') > 0) { + angle = -1 * this.Get('chart.text.angle'); + valign = 'center'; + halign = 'right'; + yOffset = 10; + + if (this.Get('chart.xaxispos') == 'top') { + yOffset = 10; + } + } + + this.context.fillStyle = this.Get('chart.text.color'); + var numLabels = this.Get('chart.labels').length; + + for (i=0; i<numLabels; ++i) { + + // Changed 8th Nov 2010 to be not reliant on the coords + //if (this.Get('chart.labels')[i] && this.coords && this.coords[i] && this.coords[i][0]) { + if (this.Get('chart.labels')[i]) { + + var labelX = ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - (2 * this.Get('chart.hmargin'))) / (numLabels - 1) ) * i; + labelX += this.gutterLeft + this.Get('chart.hmargin'); + + /** + * Account for an unrelated number of labels + */ + if (this.Get('chart.labels').length != this.data[0].length) { + labelX = this.gutterLeft + this.Get('chart.hmargin') + ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - (2 * this.Get('chart.hmargin'))) * (i / (this.Get('chart.labels').length - 1))); + } + + // This accounts for there only being one point on the chart + if (!labelX) { + labelX = this.gutterLeft + this.Get('chart.hmargin'); + } + + if (this.Get('chart.xaxispos') == 'top' && this.Get('chart.text.angle') > 0) { + halign = 'left'; + } + + RGraph.Text(context, + font, + text_size, + labelX, + (this.Get('chart.xaxispos') == 'top') ? this.gutterTop - yOffset - (this.Get('chart.xlabels.inside') ? -22 : 0) : (RGraph.GetHeight(this) - this.gutterBottom) + yOffset, + String(this.Get('chart.labels')[i]), + valign, + halign, + bordered, + angle, + bgcolor); + } + } + + } + + this.context.stroke(); + this.context.fill(); + } + + + /** + * Draws the line + */ + RGraph.Line.prototype.DrawLine = function (lineData, color, fill, linewidth, tickmarks, index) + { + // This facilitates the Rise animation (the Y value only) + if (this.Get('chart.animation.unfold.y') && this.Get('chart.animation.factor') != 1) { + for (var i=0; i<lineData.length; ++i) { + lineData[i] *= this.Get('chart.animation.factor'); + } + } + + var penUp = false; + var yPos = null; + var xPos = 0; + this.context.lineWidth = 1; + var lineCoords = []; + + /** + * Get the previous line data + */ + if (index > 0) { + var prevLineCoords = this.coords2[index - 1]; + } + + // Work out the X interval + var xInterval = (this.canvas.width - (2 * this.Get('chart.hmargin')) - this.gutterLeft - this.gutterRight) / (lineData.length - 1); + + // Loop thru each value given, plotting the line + // (FORMERLY FIRST) + for (i=0; i<lineData.length; i++) { + + var data_point = lineData[i]; + + yPos = this.canvas.height - (((data_point - (data_point > 0 ? this.Get('chart.ymin') : (-1 * this.Get('chart.ymin')))) / (this.max - this.min) ) * this.grapharea); + yPos = (this.grapharea / (this.max - this.min)) * (data_point - this.min); + yPos = this.canvas.height - yPos; + + /** + * This skirts an annoying JS rounding error + * SEARCH TAGS: JS ROUNDING ERROR DECIMALS + */ + if (data_point == this.max) { + yPos = Math.round(yPos); + } + + if (this.Get('chart.ylabels.invert')) { + yPos -= this.gutterBottom; + yPos -= this.gutterTop; + yPos = this.canvas.height - yPos; + } + + // Make adjustments depending on the X axis position + if (this.Get('chart.xaxispos') == 'center') { + yPos = (yPos - this.gutterBottom - this.gutterTop) / 2; + yPos = yPos + this.gutterTop; + + // TODO Check this + } else if (this.Get('chart.xaxispos') == 'top') { + + yPos = (this.grapharea / (this.max - this.min)) * (Math.abs(data_point) - this.min); + yPos += this.gutterTop; + + if (this.Get('chart.ylabels.invert')) { + yPos -= this.gutterTop; + yPos = this.grapharea - yPos; + yPos += this.gutterTop; + } + + } else if (this.Get('chart.xaxispos') == 'bottom') { + // TODO + yPos -= this.gutterBottom; // Without this the line is out of place due to the gutter + } + + + + // Null data points, and a special case for this bug:http://dev.rgraph.net/tests/ymin.html + if ( lineData[i] == null + || (this.Get('chart.xaxispos') == 'bottom' && lineData[i] < this.min && !this.Get('chart.outofbounds')) + || (this.Get('chart.xaxispos') == 'center' && lineData[i] < (-1 * this.max) && !this.Get('chart.outofbounds'))) { + + yPos = null; + } + + // Not always very noticeable, but it does have an effect + // with thick lines + this.context.lineCap = 'round'; + this.context.lineJoin = 'round'; + + // Plot the line if we're at least on the second iteration + if (i > 0) { + xPos = xPos + xInterval; + } else { + xPos = this.Get('chart.hmargin') + this.gutterLeft; + } + + if (this.Get('chart.animation.unfold.x')) { + xPos *= this.Get('chart.animation.factor'); + + if (xPos < this.Get('chart.gutter.left')) { + xPos = this.Get('chart.gutter.left'); + } + } + + /** + * Add the coords to an array + */ + this.coords.push([xPos, yPos]); + lineCoords.push([xPos, yPos]); + } + + this.context.stroke(); + + // Store the coords in another format, indexed by line number + this.coords2[index] = lineCoords; + + /** + * For IE only: Draw the shadow ourselves as ExCanvas doesn't produce shadows + */ + if (RGraph.isIE8() && this.Get('chart.shadow')) { + this.DrawIEShadow(lineCoords, this.context.shadowColor); + } + + /** + * Now draw the actual line [FORMERLY SECOND] + */ + this.context.beginPath(); + // Transparent now as of 11/19/2011 + this.context.strokeStyle = 'rgba(0,0,0,0)'; + //this.context.strokeStyle = fill; + if (fill) { + this.context.fillStyle = fill; + } + + var isStepped = this.Get('chart.stepped'); + var isFilled = this.Get('chart.filled'); + + + for (var i=0; i<lineCoords.length; ++i) { + + xPos = lineCoords[i][0]; + yPos = lineCoords[i][1]; + var set = index; + + var prevY = (lineCoords[i - 1] ? lineCoords[i - 1][1] : null); + var isLast = (i + 1) == lineCoords.length; + + /** + * This nullifys values which are out-of-range + */ + if (prevY < this.gutterTop || prevY > (RGraph.GetHeight(this) - this.gutterBottom) ) { + penUp = true; + } + + if (i == 0 || penUp || !yPos || !prevY || prevY < this.gutterTop) { + + if (this.Get('chart.filled') && !this.Get('chart.filled.range')) { + + this.context.moveTo(xPos + 1, this.canvas.height - this.gutterBottom - (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0) -1); + + // This facilitates the X axis being at the top + // NOTE: Also done below + if (this.Get('chart.xaxispos') == 'top') { + this.context.moveTo(xPos + 1, this.Get('chart.gutter.top')); + } + + this.context.lineTo(xPos + 1, yPos); + + } else { + + if (RGraph.isIE8() && yPos == null) { + // Nada + } else { + this.context.moveTo(xPos, yPos); + } + } + + if (yPos == null) { + penUp = true; + + } else { + penUp = false; + } + + } else { + + // Draw the stepped part of stepped lines + if (isStepped) { + this.context.lineTo(xPos, lineCoords[i - 1][1]); + } + + if ((yPos >= this.gutterTop && yPos <= (this.canvas.height - this.gutterBottom)) || this.Get('chart.outofbounds') ) { + + if (isLast && this.Get('chart.filled') && !this.Get('chart.filled.range') && this.Get('chart.yaxispos') == 'right') { + xPos -= 1; + } + + + // Added 8th September 2009 + if (!isStepped || !isLast) { + this.context.lineTo(xPos, yPos); + + if (isFilled && lineCoords[i+1] && lineCoords[i+1][1] == null) { + this.context.lineTo(xPos, RGraph.GetHeight(this) - this.gutterBottom); + } + + // Added August 2010 + } else if (isStepped && isLast) { + this.context.lineTo(xPos,yPos); + } + + + penUp = false; + } else { + penUp = true; + } + } + } + + if (this.Get('chart.filled') && !this.Get('chart.filled.range')) { + + // Is this needed ?? + var fillStyle = this.Get('chart.fillstyle'); + + /** + * Draw the bottom edge of the filled bit using either the X axis or the prevlinedata, + * depending on the index of the line. The first line uses the X axis, and subsequent + * lines use the prevLineCoords array + */ + if (index > 0 && this.Get('chart.filled.accumulative')) { + this.context.lineTo(xPos, prevLineCoords ? prevLineCoords[i - 1][1] : (this.canvas.height - this.gutterBottom - 1 + (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0))); + + for (var k=(i - 1); k>=0; --k) { + this.context.lineTo(prevLineCoords[k][0], prevLineCoords[k][1]); + } + } else { + // Draw a line down to the X axis + if (this.Get('chart.xaxispos') == 'top') { + this.context.lineTo(xPos, this.Get('chart.gutter.top') + 1); + this.context.lineTo(lineCoords[0][0],this.Get('chart.gutter.top') + 1); + } else { + this.context.lineTo(xPos,this.canvas.height - this.gutterBottom - 1 - + (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0)); + this.context.lineTo(lineCoords[0][0],this.canvas.height - this.Get('chart.gutter.bottom') - (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0)); + } + } + + this.context.fillStyle = fill; + + this.context.fill(); + this.context.beginPath(); + } + + /** + * FIXME this may need removing when Chrome is fixed + * SEARCH TAGS: CHROME SHADOW BUG + */ + if (navigator.userAgent.match(/Chrome/) && this.Get('chart.shadow') && this.Get('chart.chromefix') && this.Get('chart.shadow.blur') > 0) { + + for (var i=lineCoords.length - 1; i>=0; --i) { + if ( + typeof(lineCoords[i][1]) != 'number' + || (typeof(lineCoords[i+1]) == 'object' && typeof(lineCoords[i+1][1]) != 'number') + ) { + this.context.moveTo(lineCoords[i][0],lineCoords[i][1]); + } else { + this.context.lineTo(lineCoords[i][0],lineCoords[i][1]); + } + } + } + + this.context.stroke(); + + + if (this.Get('chart.backdrop')) { + this.DrawBackdrop(lineCoords, color); + } + + // Now redraw the lines with the correct line width + this.RedrawLine(lineCoords, color, linewidth); + + this.context.stroke(); + + // Draw the tickmarks + for (var i=0; i<lineCoords.length; ++i) { + + i = Number(i); + + if (isStepped && i == (lineCoords.length - 1)) { + this.context.beginPath(); + //continue; + } + + if ( + ( + tickmarks != 'endcircle' + && tickmarks != 'endsquare' + && tickmarks != 'filledendsquare' + && tickmarks != 'endtick' + && tickmarks != 'endtriangle' + && tickmarks != 'arrow' + && tickmarks != 'filledarrow' + ) + || (i == 0 && tickmarks != 'arrow' && tickmarks != 'filledarrow') + || i == (lineCoords.length - 1) + ) { + + var prevX = (i <= 0 ? null : lineCoords[i - 1][0]); + var prevY = (i <= 0 ? null : lineCoords[i - 1][1]); + + this.DrawTick(lineData, lineCoords[i][0], lineCoords[i][1], color, false, prevX, prevY, tickmarks, i); + + // Draws tickmarks on the stepped bits of stepped charts. Takend out 14th July 2010 + // + //if (this.Get('chart.stepped') && lineCoords[i + 1] && this.Get('chart.tickmarks') != 'endsquare' && this.Get('chart.tickmarks') != 'endcircle' && this.Get('chart.tickmarks') != 'endtick') { + // this.DrawTick(lineCoords[i + 1][0], lineCoords[i][1], color); + //} + } + } + + // Draw something off canvas to skirt an annoying bug + this.context.beginPath(); + this.context.arc(RGraph.GetWidth(this) + 50000, RGraph.GetHeight(this) + 50000, 2, 0, 6.38, 1); + } + + + /** + * This functions draws a tick mark on the line + * + * @param xPos int The x position of the tickmark + * @param yPos int The y position of the tickmark + * @param color str The color of the tickmark + * @param bool Whether the tick is a shadow. If it is, it gets offset by the shadow offset + */ + RGraph.Line.prototype.DrawTick = function (lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index) + { + // If the yPos is null - no tick + if ((yPos == null || yPos > (this.canvas.height - this.gutterBottom) || yPos < this.gutterTop) && !this.Get('chart.outofbounds') || !this.Get('chart.line.visible')) { + return; + } + + this.context.beginPath(); + + var offset = 0; + + // Reset the stroke and lineWidth back to the same as what they were when the line was drawm + // UPDATE 28th July 2011 - the line width is now set to 1 + this.context.lineWidth = this.Get('chart.tickmarks.linewidth') ? this.Get('chart.tickmarks.linewidth') : this.Get('chart.linewidth'); + this.context.strokeStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle; + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle; + + // Cicular tick marks + if ( tickmarks == 'circle' + || tickmarks == 'filledcircle' + || tickmarks == 'endcircle') { + + if (tickmarks == 'circle'|| tickmarks == 'filledcircle' || (tickmarks == 'endcircle') ) { + this.context.beginPath(); + this.context.arc(xPos + offset, yPos + offset, this.Get('chart.ticksize'), 0, 360 / (180 / Math.PI), false); + + if (tickmarks == 'filledcircle') { + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle; + } else { + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : 'white'; + } + + this.context.stroke(); + this.context.fill(); + } + + // Halfheight "Line" style tick marks + } else if (tickmarks == 'halftick') { + this.context.beginPath(); + this.context.moveTo(xPos, yPos); + this.context.lineTo(xPos, yPos + this.Get('chart.ticksize')); + + this.context.stroke(); + + // Tick style tickmarks + } else if (tickmarks == 'tick') { + this.context.beginPath(); + this.context.moveTo(xPos, yPos - this.Get('chart.ticksize')); + this.context.lineTo(xPos, yPos + this.Get('chart.ticksize')); + + this.context.stroke(); + + // Endtick style tickmarks + } else if (tickmarks == 'endtick') { + this.context.beginPath(); + this.context.moveTo(xPos, yPos - this.Get('chart.ticksize')); + this.context.lineTo(xPos, yPos + this.Get('chart.ticksize')); + + this.context.stroke(); + + // "Cross" style tick marks + } else if (tickmarks == 'cross') { + this.context.beginPath(); + this.context.moveTo(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize')); + this.context.lineTo(xPos + this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize')); + this.context.moveTo(xPos + this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize')); + this.context.lineTo(xPos - this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize')); + + this.context.stroke(); + + + // Triangle style tick marks + } else if (tickmarks == 'triangle' || tickmarks == 'filledtriangle' || tickmarks == 'endtriangle') { + this.context.beginPath(); + + if (tickmarks == 'filledtriangle') { + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle; + } else { + this.context.fillStyle = 'white'; + } + + this.context.moveTo(xPos - this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize')); + this.context.lineTo(xPos, yPos - this.Get('chart.ticksize')); + this.context.lineTo(xPos + this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize')); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + + // A white bordered circle + } else if (tickmarks == 'borderedcircle' || tickmarks == 'dot') { + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.tickmarks.dot.color'); + this.context.fillStyle = this.Get('chart.tickmarks.dot.color'); + + // The outer white circle + this.context.beginPath(); + this.context.arc(xPos, yPos, this.Get('chart.ticksize'), 0, 360 / (180 / Math.PI), false); + this.context.closePath(); + + + this.context.fill(); + this.context.stroke(); + + // Now do the inners + this.context.beginPath(); + this.context.fillStyle = color; + this.context.strokeStyle = color; + this.context.arc(xPos, yPos, this.Get('chart.ticksize') - 2, 0, 360 / (180 / Math.PI), false); + + this.context.closePath(); + + this.context.fill(); + this.context.stroke(); + + } else if ( tickmarks == 'square' + || tickmarks == 'filledsquare' + || (tickmarks == 'endsquare') + || (tickmarks == 'filledendsquare') ) { + + this.context.fillStyle = 'white'; + this.context.strokeStyle = this.context.strokeStyle; // FIXME Is this correct? + + this.context.beginPath(); + this.context.strokeRect(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'), this.Get('chart.ticksize') * 2, this.Get('chart.ticksize') * 2); + + // Fillrect + if (tickmarks == 'filledsquare' || tickmarks == 'filledendsquare') { + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle; + this.context.fillRect(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'), this.Get('chart.ticksize') * 2, this.Get('chart.ticksize') * 2); + + } else if (tickmarks == 'square' || tickmarks == 'endsquare') { + this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : 'white'; + this.context.fillRect((xPos - this.Get('chart.ticksize')) + 1, (yPos - this.Get('chart.ticksize')) + 1, (this.Get('chart.ticksize') * 2) - 2, (this.Get('chart.ticksize') * 2) - 2); + } + + this.context.stroke(); + this.context.fill(); + + /** + * FILLED arrowhead + */ + } else if (tickmarks == 'filledarrow') { + + var x = Math.abs(xPos - prevX); + var y = Math.abs(yPos - prevY); + + if (yPos < prevY) { + var a = Math.atan(x / y) + 1.57; + } else { + var a = Math.atan(y / x) + 3.14; + } + + this.context.beginPath(); + this.context.moveTo(xPos, yPos); + this.context.arc(xPos, yPos, 7, a - 0.5, a + 0.5, false); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + /** + * Arrow head, NOT filled + */ + } else if (tickmarks == 'arrow') { + + var x = Math.abs(xPos - prevX); + var y = Math.abs(yPos - prevY); + + if (yPos < prevY) { + var a = Math.atan(x / y) + 1.57; + } else { + var a = Math.atan(y / x) + 3.14; + } + + this.context.beginPath(); + this.context.moveTo(xPos, yPos); + this.context.arc(xPos, yPos, 7, a - 0.5 - (document.all ? 0.1 : 0.01), a - 0.4, false); + + this.context.moveTo(xPos, yPos); + this.context.arc(xPos, yPos, 7, a + 0.5 + (document.all ? 0.1 : 0.01), a + 0.5, true); + + + this.context.stroke(); + + /** + * Custom tick drawing function + */ + } else if (typeof(tickmarks) == 'function') { + tickmarks(this, lineData, lineData[index], index, xPos, yPos, color, prevX, prevY); + } + } + + + /** + * Draws a filled range if necessary + */ + RGraph.Line.prototype.DrawRange = function () + { + /** + * Fill the range if necessary + */ + if (this.Get('chart.filled.range') && this.Get('chart.filled')) { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.fillstyle'); + this.context.strokeStyle = this.Get('chart.fillstyle'); + this.context.lineWidth = 1; + var len = (this.coords.length / 2); + + for (var i=0; i<len; ++i) { + if (i == 0) { + this.context.moveTo(this.coords[i][0], this.coords[i][1]) + } else { + this.context.lineTo(this.coords[i][0], this.coords[i][1]) + } + } + + for (var i=this.coords.length - 1; i>=len; --i) { + this.context.lineTo(this.coords[i][0], this.coords[i][1]) + } + this.context.stroke(); + this.context.fill(); + } + } + + + /** + * Redraws the line with the correct line width etc + * + * @param array coords The coordinates of the line + */ + RGraph.Line.prototype.RedrawLine = function (coords, color, linewidth) + { + if (this.Get('chart.noredraw')) { + return; + } + + this.context.strokeStyle = (typeof(color) == 'object' && color ? color[0] : color); + this.context.lineWidth = linewidth; + + if (!this.Get('chart.line.visible')) { + this.context.strokeStyle = 'rgba(0,0,0,0)'; + } + + if (this.Get('chart.curvy')) { + this.DrawCurvyLine(coords, !this.Get('chart.line.visible') ? 'rgba(0,0,0,0)' : color, linewidth); + return + } + + this.context.beginPath(); + + var len = coords.length; + var width = this.canvas.width + var height = this.canvas.height; + var penUp = false; + + for (var i=0; i<len; ++i) { + + var xPos = coords[i][0]; + var yPos = coords[i][1]; + + if (i > 0) { + var prevX = coords[i - 1][0]; + var prevY = coords[i - 1][1]; + } + + + if (( + (i == 0 && coords[i]) + || (yPos < this.gutterTop) + || (prevY < this.gutterTop) + || (yPos > (height - this.gutterBottom)) + || (i > 0 && prevX > (width - this.gutterRight)) + || (i > 0 && prevY > (height - this.gutterBottom)) + || prevY == null + || penUp == true + ) && (!this.Get('chart.outofbounds') || yPos == null || prevY == null) ) { + + if (RGraph.isIE8() && yPos == null) { + // ...? + } else { + this.context.moveTo(coords[i][0], coords[i][1]); + } + + penUp = false; + + } else { + + if (this.Get('chart.stepped') && i > 0) { + this.context.lineTo(coords[i][0], coords[i - 1][1]); + } + + // Don't draw the last bit of a stepped chart. Now DO + //if (!this.Get('chart.stepped') || i < (coords.length - 1)) { + this.context.lineTo(coords[i][0], coords[i][1]); + //} + penUp = false; + } + } + + /** + * If two colors are specified instead of one, go over the up bits + */ + if (this.Get('chart.colors.alternate') && typeof(color) == 'object' && color[0] && color[1]) { + for (var i=1; i<len; ++i) { + + var prevX = coords[i - 1][0]; + var prevY = coords[i - 1][1]; + + this.context.beginPath(); + this.context.strokeStyle = color[coords[i][1] < prevY ? 0 : 1]; + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.moveTo(prevX, prevY); + this.context.lineTo(coords[i][0], coords[i][1]); + this.context.stroke(); + } + } + } + + + /** + * This function is used by MSIE only to manually draw the shadow + * + * @param array coords The coords for the line + */ + RGraph.Line.prototype.DrawIEShadow = function (coords, color) + { + var offsetx = this.Get('chart.shadow.offsetx'); + var offsety = this.Get('chart.shadow.offsety'); + + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.strokeStyle = color; + this.context.beginPath(); + + for (var i=0; i<coords.length; ++i) { + if (i == 0) { + this.context.moveTo(coords[i][0] + offsetx, coords[i][1] + offsety); + } else { + this.context.lineTo(coords[i][0] + offsetx, coords[i][1] + offsety); + } + } + + this.context.stroke(); + } + + + /** + * Draw the backdrop + */ + RGraph.Line.prototype.DrawBackdrop = function (coords, color) + { + var size = this.Get('chart.backdrop.size'); + this.context.lineWidth = size; + this.context.globalAlpha = this.Get('chart.backdrop.alpha'); + this.context.strokeStyle = color; + this.context.lineJoin = 'miter'; + + this.context.beginPath(); + this.context.moveTo(coords[0][0], coords[0][1]); + for (var j=1; j<coords.length; ++j) { + this.context.lineTo(coords[j][0], coords[j][1]); + } + + this.context.stroke(); + + // Reset the alpha value + this.context.globalAlpha = 1; + this.context.lineJoin = 'round'; + RGraph.NoShadow(this); + } + + + /** + * Returns the linewidth + */ + RGraph.Line.prototype.GetLineWidth = function (i) + { + var linewidth = this.Get('chart.linewidth'); + + if (typeof(linewidth) == 'number') { + return linewidth; + + } else if (typeof(linewidth) == 'object') { + if (linewidth[i]) { + return linewidth[i]; + } else { + return linewidth[0]; + } + + alert('[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers'); + } + } + + + /** + * The getPoint() method - used to get the point the mouse is currently over, if any + * + * @param object e The event object + * @param object OPTIONAL You can pass in the bar object instead of the + * function getting it from the canvas + */ + RGraph.Line.prototype.getPoint = function (e) + { + var canvas = e.target; + var obj = canvas.__object__; + var context = obj.context; + var mouseXY = RGraph.getMouseXY(e); + var mouseX = mouseXY[0]; + var mouseY = mouseXY[1]; + + // This facilitates you being able to pass in the bar object as a parameter instead of + // the function getting it from the object + if (arguments[1]) { + obj = arguments[1]; + } + + for (var i=0; i<obj.coords.length; ++i) { + + var xCoord = obj.coords[i][0]; + var yCoord = obj.coords[i][1]; + + // Do this if the hotspot is triggered by the X coord AND the Y coord + if ( obj.Get('chart.tooltips.hotspot.xonly') == false + && mouseX <= (xCoord + 5) + && mouseX >= (xCoord - 5) + && mouseY <= (yCoord + 5) + && mouseY >= (yCoord - 5) + ) { + + return [obj, xCoord, yCoord, i]; + + } else if ( obj.Get('chart.tooltips.hotspot.xonly') == true + && mouseX <= (xCoord + 5) + && mouseX >= (xCoord - 5)) { + + return [obj, xCoord, yCoord, i]; + } + } + } + + + /** + * Draws the above line labels + */ + RGraph.Line.prototype.DrawAboveLabels = function () + { + var context = this.context; + var size = this.Get('chart.labels.above.size'); + var font = this.Get('chart.text.font'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + + context.beginPath(); + + // Don't need to check that chart.labels.above is enabled here, it's been done already + for (var i=0; i<this.coords.length; ++i) { + var coords = this.coords[i]; + + RGraph.Text(context, font, size, coords[0], coords[1] - 5 - size, RGraph.number_format(this, this.data_arr[i], units_pre, units_post), 'center', 'center', true, null, 'rgba(255, 255, 255, 0.7)'); + } + + context.fill(); + } + + + /** + * Draw a curvy line. This isn't 100% accurate but may be more to your tastes + */ + RGraph.Line.prototype.DrawCurvyLine = function (coords, color, linewidth) + { + var co = this.context; + + // Now calculate the halfway point + co.beginPath(); + + co.strokeStyle = color; + co.lineWidth = linewidth; + + for (var i=0; i<coords.length; ++i) { + if (coords[i + 1]) { + + var factor = this.Get('chart.curvy.factor'); + var coordX = coords[i][0]; + var coordY = coords[i][1]; + var nextX = coords[i + 1][0]; + var nextY = coords[i + 1][1]; + var offsetX = (coords[i + 1][0] - coords[i][0]) * factor; + var offsetY = (coords[i + 1][1] - coords[i][1]) * factor; + + if (i == 0) { + co.moveTo(coordX, coordY); + co.lineTo(nextX - offsetX, nextY - offsetY); + + } else if (nextY == null) { + co.lineTo(coordX, coordY); + + } else if (coordY == null) { + co.moveTo(nextX, nextY); + + + } else { + + co.quadraticCurveTo(coordX, coordY, coordX + offsetX, coordY + offsetY); + + if (nextY) { + co.lineTo(nextX - offsetX, nextY - offsetY); + } else { + co.lineTo(coordX, coordY); + } + } + + // Draw the last bit + } else { + co.lineTo(coords[i][0], coords[i][1]); + } + } + + co.stroke(); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.meter.js b/schall/static/RGraph/libraries/RGraph.meter.js new file mode 100644 index 0000000..a246cbf --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.meter.js @@ -0,0 +1,573 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bar chart constructor + * + * @param string canvas The canvas ID + * @param min integer The minimum value + * @param max integer The maximum value + * @param value integer The indicated value + */ + RGraph.Meter = function (id, min, max, value) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'meter'; + this.min = min; + this.max = max; + this.value = value; + this.centerx = null; + this.centery = null; + this.radius = null; + this.isRGraph = true; + this.currentValue = null; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config type stuff + this.properties = { + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.linewidth': 1, + 'chart.linewidth.segments': 1, + 'chart.strokestyle': null, + 'chart.border': true, + 'chart.border.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.value.label': false, + 'chart.value.text.decimals': 0, + 'chart.value.text.units.pre': '', + 'chart.value.text.units.post': '', + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.color': 'black', + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.green.start': ((this.max - this.min) * 0.35) + this.min, + 'chart.green.end': this.max, + 'chart.green.color': '#207A20', + 'chart.yellow.start': ((this.max - this.min) * 0.1) + this.min, + 'chart.yellow.end': ((this.max - this.min) * 0.35) + this.min, + 'chart.yellow.color': '#D0AC41', + 'chart.red.start': this.min, + 'chart.red.end': ((this.max - this.min) * 0.1) + this.min, + 'chart.red.color': '#9E1E1E', + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.contextmenu': null, + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.shadow': false, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.shadow.blur': 3, + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.tickmarks.small.num': 100, + 'chart.tickmarks.big.num': 10, + 'chart.tickmarks.small.color': '#bbb', + 'chart.tickmarks.big.color': 'black', + 'chart.scale.decimals': 0, + 'chart.radius': null, + 'chart.centerx': null, + 'chart.centery': null, + 'chart.labels': true, + 'chart.segment.radius.start': null, + 'chart.needle.radius': null, + 'chart.needle.tail': false + } + + + // Check for support + if (!this.canvas) { + alert('[METER] No canvas support'); + return; + } + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Meter.prototype.Set = function (name, value) + { + if (name == 'chart.value') { + this.value = value; + return; + } + + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Meter.prototype.Get = function (name) + { + if (name == 'chart.value') { + return this.value; + } + + return this.properties[name]; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.Meter.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Constrain the value to be within the min and max + */ + if (this.value > this.max) this.value = this.max; + if (this.value < this.min) this.value = this.min; + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + this.centerx = this.canvas.width / 2; + this.centery = this.canvas.height - this.gutterBottom; + this.radius = Math.min( + this.canvas.width - this.gutterLeft - this.gutterRight, + this.canvas.height - this.gutterTop - this.gutterBottom + ); + + /** + * Custom centerx, centery and radius + */ + if (typeof(this.Get('chart.centerx')) == 'number') this.centerx = this.Get('chart.centerx'); + if (typeof(this.Get('chart.centery')) == 'number') this.centery = this.Get('chart.centery'); + if (typeof(this.Get('chart.radius')) == 'number') this.radius = this.Get('chart.radius'); + + this.DrawBackground(); + this.DrawNeedle(); + this.DrawLabels(); + this.DrawReadout(); + + /** + * Draw the title + */ + RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * For MSIE only, to cover the spurious lower ends of the circle + */ + if (RGraph.isIE8()) { + // Cover the left tail + this.context.beginPath(); + this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + this.context.fillStyle = 'white'; + this.context.fillRect(this.centerx - this.radius - 5, RGraph.GetHeight(this) - this.gutterBottom + 1, 10, this.gutterBottom); + this.context.fill(); + + // Cover the right tail + this.context.beginPath(); + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + this.context.fillStyle = 'white'; + this.context.fillRect(this.centerx + this.radius - 5, RGraph.GetHeight(this) - this.gutterBottom + 1, 10, this.gutterBottom); + this.context.fill(); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the background of the chart + */ + RGraph.Meter.prototype.DrawBackground = function () + { + // Draw the shadow + if (this.Get('chart.shadow')) { + this.context.beginPath(); + this.context.fillStyle = 'white'; + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + + this.context.arc(this.centerx, this.centery, this.radius, 3.14, 6.28, false); + //this.context.arc(this.centerx, this.centery, , 0, 6.28, false); + this.context.fill(); + + + this.context.beginPath(); + var r = (this.radius * 0.06) > 40 ? 40 : (this.radius * 0.06); + this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0); + this.context.fill(); + + RGraph.NoShadow(this); + } + + // First, draw the grey tickmarks + if (this.Get('chart.tickmarks.small.num')) { + for (var i=0; i<3.14; i+=(3.14 / this.Get('chart.tickmarks.small.num'))) { + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.tickmarks.small.color'); + this.context.arc(this.centerx, this.centery, this.radius, 3.14 + i, 3.1415 + i, 0); + this.context.arc(this.centerx, this.centery, this.radius - 5, 3.14 + i, 3.1415 + i, 0); + this.context.stroke(); + } + + // Draw the white semi-circle that makes the tickmarks + this.context.beginPath(); + this.context.fillStyle = 'white' + this.context.arc(this.centerx, this.centery, this.radius - 4, 3.14, 6.28, false); + this.context.closePath(); + this.context.fill(); + } + + + + // Second, draw the darker tickmarks. First run draws them in white to get rid of the existing tickmark, + // then the second run draws them in the requested color + if (this.Get('chart.tickmarks.big.num')) { + var colors = ['white','white',this.Get('chart.tickmarks.big.color')]; + for (var j=0; j<colors.length; ++j) { + for (var i=0; i<3.14; i+=(3.1415927 / this.Get('chart.tickmarks.big.num'))) { + this.context.beginPath(); + this.context.strokeStyle = colors[j]; + this.context.arc(this.centerx, this.centery, this.radius, 3.14 + i, 3.1415 + i, 0); + this.context.arc(this.centerx, this.centery, this.radius - 5, 3.14 + i, 3.1415 + i, 0); + //this.context.lineTo(this.centerx, this.centery); + this.context.stroke(); + } + } + } + + // Draw the white circle that makes the tickmarks + this.context.beginPath(); + this.context.fillStyle = 'white'; + this.context.arc(this.centerx, this.centery, this.radius - 7, 3.1415927, 6.28, false); + this.context.closePath(); + this.context.fill(); + + /** + * Color ranges - either green/yellow/red or an arbitrary number of ranges + */ + var ranges = this.Get('chart.colors.ranges'); + + if (RGraph.is_array(this.Get('chart.colors.ranges'))) { + + var ranges = this.Get('chart.colors.ranges'); + + for (var i=0; i<ranges.length; ++i) { + + this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : ranges[i][2]; + this.context.fillStyle = ranges[i][2]; + this.context.lineWidth = this.Get('chart.linewidth.segments'); + + this.context.beginPath(); + this.context.moveTo(this.centerx,this.centery); + this.context.arc(this.centerx, + this.centery, + this.radius * 0.85, + (((ranges[i][0] - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927, + (((ranges[i][1] - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927, + false); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + } + + // Stops the last line from being changed to a big linewidth. So it seems. + this.context.beginPath(); + + } else { + // Draw the green area + this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.green.color'); + this.context.fillStyle = this.Get('chart.green.color'); + this.context.lineWidth = this.Get('chart.linewidth.segments'); + this.context.beginPath(); + this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.green.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.green.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false); + + if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) { + this.context.arc(this.centerx,this.centery,this.Get('chart.segment.radius.start'),(((this.Get('chart.green.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.green.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true); + } else { + this.context.lineTo(this.centerx, this.centery); + } + + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + + // Draw the yellow area + this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.yellow.color'); + this.context.fillStyle = this.Get('chart.yellow.color'); + this.context.lineWidth = this.Get('chart.linewidth.segments'); + this.context.beginPath(); + this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.yellow.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.yellow.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false); + + if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) { + this.context.arc(this.centerx,this.centery,this.Get('chart.segment.radius.start'),(((this.Get('chart.yellow.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927, (((this.Get('chart.yellow.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true); + } else { + this.context.lineTo(this.centerx, this.centery); + } + + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + + // Draw the red area + this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.red.color'); + this.context.fillStyle = this.Get('chart.red.color'); + this.context.lineWidth = this.Get('chart.linewidth.segments'); + this.context.beginPath(); + this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.red.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.red.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false); + + if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) { + this.context.arc(this.centerx,this.centery, this.Get('chart.segment.radius.start'),(((this.Get('chart.red.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.red.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true); + } else { + this.context.lineTo(this.centerx, this.centery); + } + + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + + // Revert the linewidth + this.context.lineWidth = 1; + } + + // Draw the outline + if (this.Get('chart.border')) { + this.context.strokeStyle = this.Get('chart.border.color'); + this.context.lineWidth = this.Get('chart.linewidth'); + + this.context.beginPath(); + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery, this.radius, 3.1415927, 6.2831854, false); + this.context.closePath(); + } + + this.context.stroke(); + + // Reset the linewidth back to 1 + this.context.lineWidth = 1; + } + + + /** + * Draws the pointer + */ + RGraph.Meter.prototype.DrawNeedle = function () + { + // Allow customising the needle radius + var needleRadius = typeof(this.Get('chart.needle.radius')) == 'number' ? this.Get('chart.needle.radius') : this.radius * 0.7; + + // First draw the circle at the bottom + this.context.fillStyle = 'black'; + this.context.lineWidth = this.radius >= 200 ? 7 : 3; + this.context.lineCap = 'round'; + + // Now, draw the arm of the needle + this.context.beginPath(); + this.context.strokeStyle = 'black'; + if (typeof(this.Get('chart.needle.linewidth')) == 'number') this.context.lineWidth = this.Get('chart.needle.linewidth'); + var a = (((this.value - this.min) / (this.max - this.min)) * 3.14) + 3.14; + this.context.arc(this.centerx, this.centery, needleRadius, a, a + 0.001, false); + this.context.lineTo(this.centerx, this.centery); + this.context.stroke(); + + // Draw the triangular needle head + this.context.beginPath(); + this.context.lineWidth = 1; + //this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery, needleRadius + 15, a, a + 0.001, 0); + this.context.arc(this.centerx, this.centery, needleRadius - 15, a + 0.087, a + 0.087999, 0); + this.context.arc(this.centerx, this.centery, needleRadius - 15, a - 0.087, a - 0.087999, 1); + this.context.fill(); + + // Draw the tail if requested + if (this.Get('chart.needle.tail')) { + this.context.beginPath(); + this.context.strokeStyle = 'black'; + if (typeof(this.Get('chart.needle.linewidth')) == 'number') this.context.lineWidth = this.Get('chart.needle.linewidth'); + var a = (((this.value - this.min) / (this.max - this.min)) * 3.14) + 6.28; + this.context.arc(this.centerx, this.centery, 25, a, a + 0.001, false); + this.context.lineTo(this.centerx, this.centery); + this.context.stroke(); + } + + // Draw the center circle + var r = (this.radius * 0.06) > 40 ? 40 : (this.radius * 0.06); + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0); + this.context.fill(); + + // Draw the centre bit of the circle + this.context.fillStyle = 'white'; + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, r - 2, 0, 6.28, 0); + this.context.fill(); + } + + + /** + * Draws the labels + */ + RGraph.Meter.prototype.DrawLabels = function () + { + if (!this.Get('chart.labels')) { + return; + } + + var context = this.context; + var radius = this.radius; + var text_size = this.Get('chart.text.size'); + var text_font = this.Get('chart.text.font'); + var units_post = this.Get('chart.units.post'); + var units_pre = this.Get('chart.units.pre'); + var centerx = this.centerx; + var centery = this.centery; + var min = this.min; + var max = this.max; + var decimals = this.Get('chart.scale.decimals'); + + context.fillStyle = this.Get('chart.text.color'); + context.lineWidth = 1; + + context.beginPath(); + + + RGraph.Text(context,text_font,text_size,centerx - radius + (0.075 * radius),centery - (this.Get('chart.border') ? 10 : -5),units_pre + (min).toFixed(decimals) + units_post,'center', 'left', false, 270); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(0.62819 / 2) * (radius - (0.075 * radius)) ),centery - (Math.sin(0.682819 / 2) * (radius - (0.1587 * radius)) ),units_pre + (((max - min) * (1/10)) + min).toFixed(decimals) + units_post,'center','center', false, 288); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(0.62819) * (radius - (0.07 * radius)) ),centery - (Math.sin(0.682819) * (radius - (0.15 * radius)) ),units_pre + (((max - min) * (2/10)) + min).toFixed(decimals) + units_post,'center','center', false, 306); + RGraph.Text(context, text_font, text_size,centerx - (Math.cos(0.95) * (radius - (0.085 * radius)) ),centery - (Math.sin(0.95) * (radius - (0.0785 * radius)) ),units_pre + (((max - min) * (3/10)) + min).toFixed(decimals) + units_post,'center', 'center', false, 320); + RGraph.Text(context, text_font, text_size,centerx - (Math.cos(1.2566) * (radius - (0.085 * radius)) ),centery - (Math.sin(1.2566) * (radius - (0.0785 * radius)) ),units_pre + (((max - min) * (4/10)) + min).toFixed(decimals) + units_post,'center', 'center', false, 342); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(1.57) * (radius - (0.075 * radius)) ),centery - (Math.sin(1.57) * (radius - (0.075 * radius)) ),units_pre + (((max - min) * (5/10)) + min).toFixed(decimals) + units_post,'center','center', false, 0); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(1.88495562) * (radius - (0.075 * radius)) ),centery - (Math.sin(1.88495562) * (radius - (0.075 * radius)) ),units_pre + (((max - min)* (6/10)) + min).toFixed(decimals) + units_post,'center','center', false, 18); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.1989) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.1989) * (radius - (0.075 * radius)) ),units_pre + (((max - min)* (7/10)) + min).toFixed(decimals) + units_post,'center','center', false, 36); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.51327416) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.51327416) * (radius - (0.075 * radius)) ), units_pre + (((max - min) * (8/10)) + min).toFixed(decimals) + units_post,'center','center', false, 54); + RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.82764832) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.82764832) * (radius - (0.075 * radius)) ),units_pre + (((max - min) * (9/10)) + min).toFixed(decimals) + units_post,'center','center', false, 72); + RGraph.Text(context,text_font,text_size,centerx + radius - (0.075 * radius),centery - (!this.Get('chart.border') ? -5 : 10),units_pre + (max).toFixed(decimals) + units_post,'center', 'right', false, 90); + + context.fill(); + context.stroke(); + } + + + /** + * This function draws the text readout if specified + */ + RGraph.Meter.prototype.DrawReadout = function () + { + if (this.Get('chart.value.text')) { + this.context.beginPath(); + RGraph.Text(this.context, + this.Get('chart.text.font'), + this.Get('chart.text.size'), + this.centerx, + this.centery - this.Get('chart.text.size') - 15, + this.Get('chart.value.text.units.pre') + (this.value).toFixed(this.Get('chart.value.text.decimals')) + this.Get('chart.value.text.units.post'), + 'center', + 'center', + true, + null, + 'white'); + + this.context.stroke(); + this.context.fill(); + } + } diff --git a/schall/static/RGraph/libraries/RGraph.modaldialog.js b/schall/static/RGraph/libraries/RGraph.modaldialog.js new file mode 100644 index 0000000..2d4cc88 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.modaldialog.js @@ -0,0 +1,244 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + ModalDialog = {} + ModalDialog.dialog = null; + ModalDialog.background = null; + ModalDialog.offset = 50; + ModalDialog.events = []; + + /** + * Shows the dialog with the supplied DIV acting as the contents + * + * @param string id The ID of the DIV to use as the dialogs contents + * @param int width The width of the dialog + */ + ModalDialog.Show = function (id, width) + { + ModalDialog.id = id; + ModalDialog.width = width; + + ModalDialog.ShowBackground(); + ModalDialog.ShowDialog(); + + // Install the event handlers + window.onresize = ModalDialog.Resize; + + + // Call them initially + ModalDialog.Resize(); + + ModalDialog.FireCustomEvent('onmodaldialog'); + } + + + /** + * Shows the background semi-transparent darkened DIV + */ + ModalDialog.ShowBackground = function () + { + // Create the background if neccessary + ModalDialog.background = document.createElement('DIV'); + ModalDialog.background.className = 'ModalDialog_background'; + ModalDialog.background.style.position = 'fixed'; + ModalDialog.background.style.top = 0; + ModalDialog.background.style.left = 0; + ModalDialog.background.style.width = (screen.width + 100) + 'px'; + ModalDialog.background.style.height = (screen.height + 100) + 'px'; + ModalDialog.background.style.backgroundColor = 'rgb(204,204,204)'; + ModalDialog.background.style.opacity = 0; + ModalDialog.background.style.zIndex = 3276; + ModalDialog.background.style.filter = "Alpha(opacity=50)"; + + document.body.appendChild(ModalDialog.background); + + ModalDialog.background.style.visibility = 'visible'; + } + + + /** + * Shows the dialog itself + */ + ModalDialog.ShowDialog = function () + { + // Create the DIV if necessary + if (!ModalDialog.dialog) { + ModalDialog.dialog = document.createElement('DIV'); + + ModalDialog.dialog.id = 'ModalDialog_dialog'; + ModalDialog.dialog.className = 'ModalDialog_dialog'; + + var borderRadius = '15px'; + + ModalDialog.dialog.style.borderRadius = borderRadius; + ModalDialog.dialog.style.MozBorderRadius = borderRadius; + ModalDialog.dialog.style.WebkitBorderRadius = borderRadius; + + ModalDialog.dialog.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + ModalDialog.dialog.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)'; + ModalDialog.dialog.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; + + ModalDialog.dialog.style.position = 'fixed'; + ModalDialog.dialog.style.backgroundColor = 'white'; + ModalDialog.dialog.style.width = parseInt(ModalDialog.width) + 'px'; + ModalDialog.dialog.style.border = '2px solid #999'; + ModalDialog.dialog.style.zIndex = 32767; + ModalDialog.dialog.style.padding = '5px'; + ModalDialog.dialog.style.paddingTop = '25px'; + ModalDialog.dialog.style.opacity = 0; + + if (document.all) { + ModalDialog.dialog.style.zIndex = 32767; + } + + + + // Accomodate various browsers + if (navigator.userAgent.indexOf('Opera') != -1) { + ModalDialog.dialog.style.paddingTop = '25px'; + + } else if (navigator.userAgent.indexOf('MSIE') != -1) { + ModalDialog.dialog.style.paddingTop = '25px'; + + } else if (navigator.userAgent.indexOf('Safari') != -1) { + ModalDialog.dialog.style.paddingTop = '25px'; + } + + document.body.appendChild(ModalDialog.dialog); + + + // Now create the grey bar at the top of the dialog + var bar = document.createElement('DIV'); + bar.className = 'ModalDialog_topbar'; + bar.style.top = 0; + bar.style.left = 0; + bar.style.width = '100%';//(ModalDialog.dialog.offsetWidth - 4) + 'px'; + bar.style.height = '20px'; + bar.style.backgroundColor = '#bbb'; + bar.style.borderBottom = '2px solid #999'; + //bar.style.zIndex = 50000; + bar.style.position = 'absolute'; + var borderRadius = '11px'; + bar.style.WebkitBorderTopLeftRadius = borderRadius; + bar.style.WebkitBorderTopRightRadius = borderRadius; + bar.style.MozBorderRadiusTopleft = borderRadius; + bar.style.MozBorderRadiusTopright = borderRadius; + bar.style.borderTopRightRadius = borderRadius; + bar.style.borderTopLeftRadius = borderRadius; + ModalDialog.dialog.appendChild(bar); + + // Add the content div + var content = document.createElement('DIV'); + //content.style.paddingTop = '20px'; + content.style.width = '100%'; + content.style.height = '100%'; + ModalDialog.dialog.appendChild(content); + + content.innerHTML = document.getElementById(ModalDialog.id).innerHTML; + + // Now reposition the dialog in the center + ModalDialog.dialog.style.left = (document.body.offsetWidth / 2) - (ModalDialog.dialog.offsetWidth / 2) + 'px'; + ModalDialog.dialog.style.top = '30%'; + } + + // Show the dialog + ModalDialog.dialog.style.visibility = 'visible'; + + // A simple fade-in effect + setTimeout('ModalDialog.dialog.style.opacity = 0.2', 50); + setTimeout('ModalDialog.dialog.style.opacity = 0.4', 100); + setTimeout('ModalDialog.dialog.style.opacity = 0.6', 150); + setTimeout('ModalDialog.dialog.style.opacity = 0.8', 200); + setTimeout('ModalDialog.dialog.style.opacity = 1', 250); + + setTimeout('ModalDialog.background.style.opacity = 0.1', 50); + setTimeout('ModalDialog.background.style.opacity = 0.2', 100); + setTimeout('ModalDialog.background.style.opacity = 0.3', 150); + setTimeout('ModalDialog.background.style.opacity = 0.4', 200); + setTimeout('ModalDialog.background.style.opacity = 0.5', 250); + } + + + /** + * Hides everything + */ + ModalDialog.Close = function () + { + if (ModalDialog.dialog) { + ModalDialog.dialog.style.visibility = 'hidden'; + ModalDialog.dialog.style.opacity = 0; + } + + if (ModalDialog.background) { + ModalDialog.background.style.visibility = 'hidden'; + ModalDialog.background.style.opacity = 0; + } + } + + // An alias + ModalDialog.Hide = ModalDialog.Close + + + /** + * Accommodate the window being resized + */ + ModalDialog.Resize = function () + { + if (ModalDialog.dialog) { + ModalDialog.dialog.style.left = (document.body.offsetWidth / 2) - (ModalDialog.dialog.offsetWidth / 2) + 'px'; + } + + ModalDialog.background.style.width = '2500px'; + ModalDialog.background.style.height = '2500px'; + } + + + /** + * Returns the page height + * + * @return int The page height + */ + ModalDialog.AddCustomEventListener = function (name, func) + { + if (typeof(ModalDialog.events) == 'undefined') { + ModalDialog.events = []; + } + + ModalDialog.events.push([name, func]); + } + + + /** + * Used to fire the ModalDialog custom event + * + * @param object obj The graph object that fires the event + * @param string event The name of the event to fire + */ + ModalDialog.FireCustomEvent = function (name) + { + for (var i=0; i<ModalDialog.events.length; ++i) { + if (typeof(ModalDialog.events[i][0]) == 'string' && ModalDialog.events[i][0] == name && typeof(ModalDialog.events[i][1]) == 'function') { + ModalDialog.events[i][1](); + } + } + } + + + /** + * Returns true if the browser is IE8 + */ + ModalDialog.isIE8 = function () + { + return document.all && (navigatot.userAgent.indexOf('MSIE 8') > 0); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.odo.js b/schall/static/RGraph/libraries/RGraph.odo.js new file mode 100644 index 0000000..2da7fcd --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.odo.js @@ -0,0 +1,815 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The odometer constructor. Pass it the ID of the canvas tag, the start value of the odo, + * the end value, and the value that the pointer should point to. + * + * @param string id The ID of the canvas tag + * @param int start The start value of the Odo + * @param int end The end value of the odo + * @param int value The indicated value (what the needle points to) + */ + RGraph.Odometer = function (id, start, end, value) + { + this.id = id + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.canvas.__object__ = this; + this.type = 'odo'; + this.isRGraph = true; + this.start = start; + this.end = end; + this.value = value; + this.currentValue = null; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + this.properties = { + 'chart.radius': null, + 'chart.value.text': false, + 'chart.value.text.decimals': 0, + 'chart.needle.color': 'black', + 'chart.needle.width': 2, + 'chart.needle.head': true, + 'chart.needle.tail': true, + 'chart.needle.type': 'pointer', + 'chart.needle.extra': [], + 'chart.needle.triangle.border': '#aaa', + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.green.max': end * 0.75, + 'chart.red.min': end * 0.9, + 'chart.green.color': 'green', + 'chart.yellow.color': 'yellow', + 'chart.red.color': 'red', + 'chart.green.solid': false, + 'chart.yellow.solid': false, + 'chart.red.solid': false, + 'chart.label.area': 35, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.font': null, + 'chart.title.bold': true, + 'chart.contextmenu': null, + 'chart.linewidth': 1, + 'chart.shadow.inner': false, + 'chart.shadow.inner.color': 'black', + 'chart.shadow.inner.offsetx': 3, + 'chart.shadow.inner.offsety': 3, + 'chart.shadow.inner.blur': 6, + 'chart.shadow.outer': false, + 'chart.shadow.outer.color': '#666', + 'chart.shadow.outer.offsetx': 0, + 'chart.shadow.outer.offsety': 0, + 'chart.shadow.outer.blur': 15, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.scale.decimals': 0, + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.border': false, + 'chart.border.color1': '#BEBCB0', + 'chart.border.color2': '#F0EFEA', + 'chart.border.color3': '#BEBCB0', + 'chart.tickmarks.highlighted': false, + 'chart.zerostart': false, + 'chart.labels': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.value.units.pre': '', + 'chart.value.units.post': '', + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.halign': 'right', + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.text.size': 10, + } + } + + + /** + * A peudo setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Odometer.prototype.Set = function (name, value) + { + if (name == 'chart.needle.style') { + alert('[RGRAPH] The RGraph property chart.needle.style has changed to chart.needle.color'); + } + + if (name == 'chart.needle.thickness') { + name = 'chart.needle.width'; + } + + if (name == 'chart.value') { + this.value = value; + return; + } + + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Odometer.prototype.Get = function (name) + { + if (name == 'chart.value') { + return this.value; + } + + return this.properties[name.toLowerCase()]; + } + + + /** + * Draws the odometer + */ + RGraph.Odometer.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * No longer allow values outside the range of the Odo + */ + if (this.value > this.end) { + this.value = this.end; + } + + if (this.value < this.start) { + this.value = this.start; + } + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Work out a few things + this.radius = Math.min((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / 2, (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2) - (this.Get('chart.border') ? 25 : 0); + this.diameter = 2 * this.radius; + this.centerx = this.canvas.width / 2; + this.centery = this.canvas.height / 2; + this.range = this.end - this.start; + + + if (typeof(this.Get('chart.radius')) == 'number') { + this.radius = this.Get('chart.radius'); + } + + /** + * Move the centerx if the key is defined + */ + if (this.Get('chart.key').length > 0 && this.canvas.width > this.canvas.height) { + this.centerx = 5 + this.radius; + } + + this.context.lineWidth = this.Get('chart.linewidth'); + + // Draw the background + this.DrawBackground(); + + // And lastly, draw the labels + this.DrawLabels(); + + // Draw the needle + this.DrawNeedle(this.value, this.Get('chart.needle.color')); + + /** + * Draw any extra needles + */ + if (this.Get('chart.needle.extra').length > 0) { + for (var i=0; i<this.Get('chart.needle.extra').length; ++i) { + var needle = this.Get('chart.needle.extra')[i]; + this.DrawNeedle(needle[0], needle[1], needle[2]); + } + } + + /** + * Draw the key if requested + */ + if (this.Get('chart.key').length > 0) { + // Build a colors array out of the needle colors + var colors = [this.Get('chart.needle.color')]; + + if (this.Get('chart.needle.extra').length > 0) { + for (var i=0; i<this.Get('chart.needle.extra').length; ++i) { + var needle = this.Get('chart.needle.extra')[i]; + colors.push(needle[1]); + } + } + + RGraph.DrawKey(this, this.Get('chart.key'), colors); + } + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + /** + * Draws the background + */ + RGraph.Odometer.prototype.DrawBackground = function () + { + this.context.beginPath(); + + /** + * Turn on the shadow if need be + */ + if (this.Get('chart.shadow.outer')) { + RGraph.SetShadow(this, this.Get('chart.shadow.outer.color'), this.Get('chart.shadow.outer.offsetx'), this.Get('chart.shadow.outer.offsety'), this.Get('chart.shadow.outer.blur')); + } + + var backgroundColor = '#eee'; + + // Draw the grey border + this.context.fillStyle = backgroundColor; + this.context.arc(this.centerx, this.centery, this.radius, 0.0001, 6.28, false); + this.context.fill(); + + /** + * Turn off the shadow + */ + RGraph.NoShadow(this); + + + // Draw a circle + this.context.strokeStyle = '#666'; + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, false); + + // Now draw a big white circle to make the lines appear as tick marks + // This is solely for Chrome + this.context.fillStyle = backgroundColor; + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, false); + this.context.fill(); + + /** + * Draw more tickmarks + */ + this.context.beginPath(); + this.context.strokeStyle = '#bbb'; + + for (var i=0; i<=360; i+=3) { + this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.degrees2Radians(i), false); + this.context.lineTo(this.centerx, this.centery); + } + this.context.stroke(); + + this.context.beginPath(); + this.context.lineWidth = 1; + this.context.strokeStyle = 'black'; + + // Now draw a big white circle to make the lines appear as tick marks + this.context.fillStyle = backgroundColor; + this.context.strokeStyle = backgroundColor; + this.context.arc(this.centerx, this.centery, this.radius - 5, 0, 6.28, false); + this.context.fill(); + this.context.stroke(); + + // Gray lines at 18 degree intervals + this.context.beginPath(); + this.context.strokeStyle = '#ddd'; + for (var i=0; i<360; i+=18) { + this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.degrees2Radians(i), false); + this.context.lineTo(this.centerx, this.centery); + } + this.context.stroke(); + + // Redraw the outer circle + this.context.beginPath(); + this.context.strokeStyle = 'black'; + this.context.arc(this.centerx, this.centery, this.radius, 0, 6.2830, false); + this.context.stroke(); + + /** + * Now draw the center bits shadow if need be + */ + if (this.Get('chart.shadow.inner')) { + this.context.beginPath(); + RGraph.SetShadow(this, this.Get('chart.shadow.inner.color'), this.Get('chart.shadow.inner.offsetx'), this.Get('chart.shadow.inner.offsety'), this.Get('chart.shadow.inner.blur')); + this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.label.area'), 0, 6.28, 0); + this.context.fill(); + this.context.stroke(); + + /** + * Turn off the shadow + */ + RGraph.NoShadow(this); + } + + /******************************************************* + * Draw the green area + *******************************************************/ + if (this.Get('chart.green.solid')) { + var greengrad = this.Get('chart.green.color'); + + } else { + var greengrad = this.context.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius); + greengrad.addColorStop(0, 'white'); + greengrad.addColorStop(1, this.Get('chart.green.color')); + } + + // Draw the "tick highlight" + if (this.Get('chart.tickmarks.highlighted')) { + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.strokeStyle = greengrad; + this.context.arc(this.centerx, this.centery, this.radius - 2.5, + + -1.57, + (((this.Get('chart.green.max') - this.start)/ (this.end - this.start)) * 6.2830) - 1.57, + 0); + + this.context.stroke(); + + this.context.lineWidth = 1; + } + + this.context.beginPath(); + this.context.fillStyle = greengrad; + this.context.arc( + this.centerx, + this.centery, + this.radius - this.Get('chart.label.area'), + -1.57, + (((this.Get('chart.green.max') - this.start)/ (this.end - this.start)) * 6.2830) - 1.57, + false + ); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); + this.context.fill(); + + + /******************************************************* + * Draw the yellow area + *******************************************************/ + if (this.Get('chart.yellow.solid')) { + var yellowgrad = this.Get('chart.yellow.color'); + + } else { + var yellowgrad = this.context.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius); + yellowgrad.addColorStop(0, 'white'); + yellowgrad.addColorStop(1, this.Get('chart.yellow.color')); + } + + // Draw the "tick highlight" + if (this.Get('chart.tickmarks.highlighted')) { + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.strokeStyle = yellowgrad; + this.context.arc(this.centerx, this.centery, this.radius - 2.5, ( + + ((this.Get('chart.green.max') - this.start) / (this.end - this.start)) * 6.2830) - 1.57, + (((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57, + 0); + + this.context.stroke(); + + this.context.lineWidth = 1; + } + + this.context.beginPath(); + this.context.fillStyle = yellowgrad; + this.context.arc( + this.centerx, + this.centery, + this.radius - this.Get('chart.label.area'), + ( ((this.Get('chart.green.max') -this.start) / (this.end - this.start)) * 6.2830) - 1.57, + ( ((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57, + false + ); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); + this.context.fill(); + + /******************************************************* + * Draw the red area + *******************************************************/ + if (this.Get('chart.red.solid')) { + var redgrad = this.Get('chart.red.color'); + + } else { + var redgrad = this.context.createRadialGradient(this.centerx, + this.centery, + 0, + this.centerx, + this.centery, + this.radius); + redgrad.addColorStop(0, 'white'); + redgrad.addColorStop(1, this.Get('chart.red.color')); + } + + + // Draw the "tick highlight" + if (this.Get('chart.tickmarks.highlighted')) { + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.strokeStyle = redgrad; + this.context.arc(this.centerx, this.centery, this.radius - 2.5,(((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,(2 * Math.PI) - (0.5 * Math.PI),0); + this.context.stroke(); + + this.context.lineWidth = 1; + } + + this.context.beginPath(); + this.context.fillStyle = redgrad; + this.context.strokeStyle = redgrad; + this.context.arc( + this.centerx, + this.centery, + this.radius - this.Get('chart.label.area'), + (((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57, + 6.2830 - (0.25 * 6.2830), + false + ); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); + this.context.fill(); + + + /** + * Draw the thick border + */ + + if (this.Get('chart.border')) { + + + var grad = this.context.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius + 20); + grad.addColorStop(0, this.Get('chart.border.color1')); + grad.addColorStop(0.5, this.Get('chart.border.color2')); + grad.addColorStop(1, this.Get('chart.border.color3')); + + + this.context.beginPath(); + this.context.fillStyle = grad; + this.context.strokeStyle = 'rgba(0,0,0,0)' + this.context.lineWidth = 0.001; + this.context.arc(this.centerx, this.centery, this.radius + 20, 0, 6.2830, 0); + this.context.arc(this.centerx, this.centery, this.radius - 2, 6.2830, 0, 1); + this.context.fill(); + } + + // Put the linewidth back to what it was + this.context.lineWidth = this.Get('chart.linewidth'); + + + /** + * Draw the title if specified + */ + if (this.Get('chart.title')) { + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + this.centery - this.radius, + null, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + } + + + // Draw the big tick marks + if (!this.Get('chart.tickmarks.highlighted')) { + for (var i=18; i<=360; i+=36) { + this.context.beginPath(); + this.context.strokeStyle = '#999'; + this.context.lineWidth = 2; + this.context.arc(this.centerx, this.centery, this.radius - 1, RGraph.degrees2Radians(i), RGraph.degrees2Radians(i+0.01), false); + this.context.arc(this.centerx, this.centery, this.radius - 7, RGraph.degrees2Radians(i), RGraph.degrees2Radians(i+0.01), false); + this.context.stroke(); + } + } + } + + + /** + * Draws the needle of the odometer + * + * @param number value The value to represent + * @param string color The color of the needle + * @param number The OPTIONAL length of the needle + */ + RGraph.Odometer.prototype.DrawNeedle = function (value, color) + { + // The optional length of the needle + var length = arguments[2] ? arguments[2] : this.radius - this.Get('chart.label.area'); + + // ===== First draw a grey background circle ===== + + this.context.fillStyle = '#999'; + + this.context.beginPath(); + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery, 10, 0, 6.28, false); + this.context.fill(); + this.context.closePath(); + + this.context.fill(); + + // =============================================== + + this.context.fillStyle = color + this.context.strokeStyle = '#666'; + + // Draw the centre bit + this.context.beginPath(); + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery, 8, 0, 6.28, false); + this.context.fill(); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + if (this.Get('chart.needle.type') == 'pointer') { + + this.context.strokeStyle = color; + this.context.lineWidth = this.Get('chart.needle.width'); + this.context.lineCap = 'round'; + this.context.lineJoin = 'round'; + + // Draw the needle + this.context.beginPath(); + // The trailing bit on the opposite side of the dial + this.context.beginPath(); + this.context.moveTo(this.centerx, this.centery); + + if (this.Get('chart.needle.tail')) { + + this.context.arc(this.centerx, + this.centery, + 20, + (((value / this.range) * 360) + 90) / 57.3, + (((value / this.range) * 360) + 90 + 0.01) / 57.3, // The 0.01 avoids a bug in ExCanvas and Chrome 6 + false + ); + } + + // Draw the long bit on the opposite side + this.context.arc(this.centerx, + this.centery, + length - 10, + (((value / this.range) * 360) - 90) / 57.3, + (((value / this.range) * 360) - 90 + 0.1 ) / 57.3, // The 0.1 avoids a bug in ExCanvas and Chrome 6 + false + ); + this.context.closePath(); + + //this.context.stroke(); + //this.context.fill(); + + + } else if (this.Get('chart.needle.type') == 'triangle') { + + this.context.lineWidth = 0.01; + this.context.lineEnd = 'square'; + this.context.lineJoin = 'miter'; + + /** + * This draws the version of the pointer that becomes the border + */ + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.needle.triangle.border'); + this.context.arc(this.centerx, this.centery, 11, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0); + this.context.arc(this.centerx, this.centery, 11, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0); + this.context.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0); + this.context.closePath(); + this.context.fill(); + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, 15, 0, 6.28, 0); + this.context.closePath(); + this.context.fill(); + + // This draws the pointer + this.context.beginPath(); + this.context.strokeStyle = 'black'; + this.context.fillStyle = color; + this.context.arc(this.centerx, this.centery, 7, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0); + this.context.arc(this.centerx, this.centery, 7, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0); + this.context.arc(this.centerx, this.centery, length - 13, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0); + this.context.closePath(); + this.context.stroke(); + this.context.fill(); + + /** + * This is here to accomodate the MSIE/ExCanvas combo + */ + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, 7, 0, 6.28, 0); + this.context.closePath(); + this.context.fill(); + } + + + this.context.stroke(); + this.context.fill(); + + // Draw the mini center circle + this.context.beginPath(); + this.context.fillStyle = color; + this.context.arc(this.centerx, this.centery, this.Get('chart.needle.type') == 'pointer' ? 7 : 12, 0.01, 6.2830, false); + this.context.fill(); + + // This draws the arrow at the end of the line + if (this.Get('chart.needle.head') && this.Get('chart.needle.type') == 'pointer') { + this.context.lineWidth = 1; + this.context.fillStyle = color; + + // round, bevel, miter + this.context.lineJoin = 'miter'; + this.context.lineCap = 'butt'; + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, (((value / this.range) * 360) - 90 + 0.1) / 57.3, false); + + this.context.arc(this.centerx, + this.centery, + length - 20, + RGraph.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 80 : 85) ), + RGraph.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 100 : 95) ), + 1); + this.context.closePath(); + + this.context.fill(); + //this.context.stroke(); + } + + /** + * Draw a white circle at the centre + */ + this.context.beginPath(); + this.context.fillStyle = 'gray'; + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx,this.centery,2,0,6.2795,false); + this.context.closePath(); + + this.context.fill(); + } + + /** + * Draws the labels for the Odo + */ + RGraph.Odometer.prototype.DrawLabels = function () + { + var context = this.context; + var size = this.Get('chart.text.size'); + var font = this.Get('chart.text.font'); + var centerx = this.centerx; + var centery = this.centery; + var r = this.radius - (this.Get('chart.label.area') / 2); + var start = this.start; + var end = this.end; + var decimals = this.Get('chart.scale.decimals'); + var labels = this.Get('chart.labels'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + + context.beginPath(); + context.fillStyle = this.Get('chart.text.color'); + + /** + * If label are specified, use those + */ + if (labels) { + for (var i=0; i<labels.length; ++i) { + + RGraph.Text(context, + font, + size, + centerx + (Math.cos(((i / labels.length) * 6.28) - 1.57) * (this.radius - (this.Get('chart.label.area') / 2) ) ), // Sin A = Opp / Hyp + centery + (Math.sin(((i / labels.length) * 6.28) - 1.57) * (this.radius - (this.Get('chart.label.area') / 2) ) ), // Cos A = Adj / Hyp + String(units_pre + labels[i] + units_post), + 'center', + 'center'); + } + + /** + * If not, use the maximum value + */ + } else { + RGraph.Text(context, font, size, centerx + (0.588 * r ), centery - (0.809 * r ), String(units_pre + (((end - start) * (1/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 36); + RGraph.Text(context, font, size, centerx + (0.951 * r ), centery - (0.309 * r), String(units_pre + (((end - start) * (2/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 72); + RGraph.Text(context, font, size, centerx + (0.949 * r), centery + (0.287 * r), String(units_pre + (((end - start) * (3/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 108); + RGraph.Text(context, font, size, centerx + (0.588 * r ), centery + (0.809 * r ), String(units_pre + (((end - start) * (4/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 144); + RGraph.Text(context, font, size, centerx, centery + r, String(units_pre + (((end - start) * (5/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 180); + RGraph.Text(context, font, size, centerx - (0.588 * r ), centery + (0.809 * r ), String(units_pre + (((end - start) * (6/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 216); + RGraph.Text(context, font, size, centerx - (0.949 * r), centery + (0.300 * r), a = String(units_pre + (((end - start) * (7/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 252); + RGraph.Text(context, font, size, centerx - (0.951 * r), centery - (0.309 * r), String(units_pre + (((end - start) * (8/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 288); + RGraph.Text(context, font, size, centerx - (0.588 * r ), centery - (0.809 * r ), String(units_pre + (((end - start) * (9/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 324); + RGraph.Text(context, font, size, centerx, centery - r, this.Get('chart.zerostart') ? units_pre + String(this.start) + units_post : String(units_pre + (((end - start) * (10/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 360); + } + + this.context.fill(); + + /** + * Draw the text label below the center point + */ + if (this.Get('chart.value.text')) { + context.strokeStyle = 'black'; + RGraph.Text(context, font, size + 2, centerx, centery + size + 2 + 10, String(this.Get('chart.value.units.pre') + this.value.toFixed(this.Get('chart.value.text.decimals')) + this.Get('chart.value.units.post')), 'center', 'center', true, null, 'white'); + } + } diff --git a/schall/static/RGraph/libraries/RGraph.pie.js b/schall/static/RGraph/libraries/RGraph.pie.js new file mode 100644 index 0000000..26f688e --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.pie.js @@ -0,0 +1,1042 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The pie chart constructor + * + * @param data array The data to be represented on the pie chart + */ + RGraph.Pie = function (id, data) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext("2d"); + this.canvas.__object__ = this; + this.total = 0; + this.subTotal = 0; + this.angles = []; + this.data = data; + this.properties = []; + this.type = 'pie'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + this.properties = { + 'chart.colors': ['rgb(255,0,0)', '#ddd', 'rgb(0,255,0)', 'rgb(0,0,255)', 'pink', 'yellow', '#000'], + 'chart.strokestyle': '#999', + 'chart.linewidth': 1, + 'chart.labels': [], + 'chart.labels.sticks': false, + 'chart.labels.sticks.length': 7, + 'chart.labels.sticks.color': '#aaa', + 'chart.segments': [], + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': 0.5, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.shadow': false, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.tooltips': [], + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.style': '3d', + 'chart.highlight.style.2d.fill': 'rgba(255,255,255,0.5)', + 'chart.highlight.style.2d.stroke': 'rgba(255,255,255,0)', + 'chart.centerx': null, + 'chart.centery': null, + 'chart.radius': null, + 'chart.border': false, + 'chart.border.color': 'rgba(255,255,255,0.5)', + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.align': 'center', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.variant': 'pie', + 'chart.variant.donut.color': 'white', + 'chart.exploded': [], + 'chart.effect.roundrobin.multiplier': 1 + } + + /** + * Calculate the total + */ + for (var i=0,len=data.length; i<len; i++) { + this.total += data[i]; + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getSegment; + } + + + /** + * A generic setter + */ + RGraph.Pie.prototype.Set = function (name, value) + { + if (name == 'chart.highlight.style.2d.color') { + name = 'chart.highlight.style.2d.fill'; + } + + this.properties[name] = value; + } + + + /** + * A generic getter + */ + RGraph.Pie.prototype.Get = function (name) + { + if (name == 'chart.highlight.style.2d.color') { + name = 'chart.highlight.style.2d.fill'; + } + + return this.properties[name]; + } + + + /** + * This draws the pie chart + */ + RGraph.Pie.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Reset this to an empty array + */ + this.Set('chart.segments', []); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + this.radius = this.Get('chart.radius') ? this.Get('chart.radius') : this.getRadius(); + // this.centerx now defined below + this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + this.subTotal = 0; + this.angles = []; + + /** + * Allow the specification of a custom centery + */ + if (typeof(this.Get('chart.centery')) == 'number') { + this.centery = this.Get('chart.centery'); + } + + /** + * Alignment (Pie is center aligned by default) Only if centerx is not defined - donut defines the centerx + */ + if (this.Get('chart.align') == 'left') { + this.centerx = this.radius + this.gutterLeft; + + } else if (this.Get('chart.align') == 'right') { + this.centerx = this.canvas.width - this.radius - this.gutterRight; + + } else { + this.centerx = this.canvas.width / 2; + } + + /** + * Allow the specification of a custom centerx + */ + if (typeof(this.Get('chart.centerx')) == 'number') { + this.centerx = this.Get('chart.centerx'); + } + + /** + * Draw the shadow if required + */ + if (this.Get('chart.shadow') && 0) { + + var offsetx = document.all ? this.Get('chart.shadow.offsetx') : 0; + var offsety = document.all ? this.Get('chart.shadow.offsety') : 0; + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.shadow.color'); + + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + + this.context.arc(this.centerx + offsetx, this.centery + offsety, this.radius, 0, 6.28, 0); + + this.context.fill(); + + // Now turn off the shadow + RGraph.NoShadow(this); + } + + /** + * The total of the array of values + */ + this.total = RGraph.array_sum(this.data); + + for (var i=0,len=this.data.length; i<len; i++) { + + var angle = ((this.data[i] / this.total) * 360); + + // Draw the segment + this.DrawSegment(angle,this.Get('chart.colors')[i],i == (this.data.length - 1), i); + } + + RGraph.NoShadow(this); + + + /** + * Redraw the seperating lines + */ + this.DrawBorders(); + + /** + * Now draw the segments again with shadow turned off. This is always performed, + * not just if the shadow is on. + */ + + for (var i=0; i<this.angles.length; i++) { + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[i]; + this.context.moveTo(this.angles[i][2], this.angles[i][3]); + this.context.arc(this.angles[i][2], + this.angles[i][3], + this.radius, + (this.angles[i][0] / 57.3), + (this.angles[i][1] / 57.3), + false); + this.context.lineTo(this.angles[i][2], this.angles[i][3]); + this.context.closePath(); + this.context.fill(); + this.context.stroke(); + } + + + + /** + * Draw label sticks + */ + if (this.Get('chart.labels.sticks')) { + + this.DrawSticks(); + + // Redraw the border going around the Pie chart if the stroke style is NOT white + var strokeStyle = this.Get('chart.strokestyle'); + var isWhite = strokeStyle == 'white' || strokeStyle == '#fff' || strokeStyle == '#fffffff' || strokeStyle == 'rgb(255,255,255)' || strokeStyle == 'rgba(255,255,255,0)'; + + if (!isWhite || (isWhite && this.Get('chart.shadow'))) { + // Again (?) + this.DrawBorders(); + } + } + + /** + * Draw the labels + */ + this.DrawLabels(); + + /** + * Draw the title + */ + if (this.Get('chart.align') == 'left') { + var centerx = this.radius + this.Get('chart.gutter.left'); + + } else if (this.Get('chart.align') == 'right') { + var centerx = RGraph.GetWidth(this) - (this.radius + this.gutterRight); + + } else { + var centerx = null; + } + + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + (this.canvas.height / 2) - this.radius - 5, + centerx, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Tooltips + */ + if (this.Get('chart.tooltips').length) { + + /** + * Register this object for redrawing + */ + RGraph.Register(this); + + /** + * The onclick event + */ + //this.canvas.onclick = function (e) + var canvas_onclick_func = function (e) + { + RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + var mouseCoords = RGraph.getMouseXY(e); + + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = e.target.__object__; + + + + /** + * If it's actually a donut make sure the hyp is bigger + * than the size of the hole in the middle + */ + if (obj.Get('chart.variant') == 'donut' && Math.abs(hyp) < (obj.radius / 2)) { + return; + } + + /** + * The angles for each segment are stored in "angles", + * so go through that checking if the mouse position corresponds + */ + var isDonut = obj.Get('chart.variant') == 'donut'; + var hStyle = obj.Get('chart.highlight.style'); + var segment = obj.getSegment(e); + + if (segment) { + + var x = mouseCoords[0] - segment[0]; + var y = mouseCoords[1] - segment[1]; + var theta = Math.atan(y / x); // RADIANS + var hyp = y / Math.sin(theta); + + + if ( RGraph.Registry.Get('chart.tooltip') + && segment[5] == RGraph.Registry.Get('chart.tooltip').__index__ + && RGraph.Registry.Get('chart.tooltip').__canvas__.__object__.id == obj.id) { + + return; + + } else { + RGraph.Redraw(); + } + + + if (isDonut || hStyle == '2d') { + + context.beginPath(); + + context.strokeStyle = obj.Get('chart.highlight.style.2d.stroke'); + context.fillStyle = obj.Get('chart.highlight.style.2d.fill'); + + //context.moveTo(obj.centerx, obj.centery); + + context.moveTo(segment[0], segment[1]); + context.arc(segment[0], segment[1], segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); + context.lineTo(segment[0], segment[1]); + context.closePath(); + + context.stroke(); + context.fill(); + + //Removed 7th December 2010 + //context.stroke(); + + } else if (hStyle == 'explode') { + + var exploded = []; + + exploded[segment[5]] = 0; + + RGraph.Registry.Set('chart.pie.exploded', obj); + + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 25); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 50); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 75); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 100); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 125); + + setTimeout(function () {RGraph.Registry.Get('chart.pie.exploded').Set('chart.exploded', []);}, 150); + + } else { + + context.lineWidth = 2; + + /** + * Draw a white segment where the one that has been clicked on was + */ + context.fillStyle = 'white'; + context.strokeStyle = 'white'; + context.beginPath(); + context.moveTo(segment[0], segment[1]); + context.arc(segment[0], segment[1], segment[2], obj.angles[segment[5]][0] / 57.3, obj.angles[segment[5]][1] / 57.3, 0); + context.stroke(); + context.fill(); + + context.lineWidth = 1; + + context.shadowColor = '#666'; + context.shadowBlur = 3; + context.shadowOffsetX = 3; + context.shadowOffsetY = 3; + + // Draw the new segment + context.beginPath(); + context.fillStyle = obj.Get('chart.colors')[segment[5]]; + context.strokeStyle = obj.Get('chart.strokestyle'); + context.moveTo(segment[0] - 3, segment[1] - 3); + context.arc(segment[0] - 3, segment[1] - 3, segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); + context.lineTo(segment[0] - 3, segment[1] - 3); + context.closePath(); + + context.stroke(); + context.fill(); + + // Turn off the shadow + RGraph.NoShadow(obj); + + /** + * If a border is defined, redraw that + */ + if (obj.Get('chart.border')) { + context.beginPath(); + context.strokeStyle = obj.Get('chart.border.color'); + context.lineWidth = 5; + context.arc(segment[0] - 3, segment[1] - 3, obj.radius - 2, RGraph.degrees2Radians(obj.angles[i][0]), RGraph.degrees2Radians(obj.angles[i][1]), 0); + context.stroke(); + } + } + + /** + * If a tooltip is defined, show it + */ + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), segment[5]); + + if (text) { + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[5]); + } + + /** + * Need to redraw the key? + */ + if (obj.Get('chart.key') && obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') { + RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); + } + + e.stopPropagation(); + + return; + } else if (obj.Get('chart.tooltips.event') == 'onclick') { + RGraph.Redraw(); + } + } + var event_name = this.Get('chart.tooltips.event') == 'onmousemove' ? 'mousemove' : 'click'; + + this.canvas.addEventListener(event_name, canvas_onclick_func, false); + RGraph.AddEventListener(this.id, event_name, canvas_onclick_func); + + + + + + + /** + * The onmousemove event for changing the cursor + */ + //this.canvas.onmousemove = function (e) + var canvas_onmousemove_func = function (e) + { + RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var segment = obj.getSegment(e); + + if (segment) { + e.target.style.cursor = 'pointer'; + + return; + } + + /** + * Put the cursor back to null + */ + e.target.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + + + + + + + + + + /** + * The window onclick function + */ + var window_onclick_func = function (e) + { + // Taken out on 02/10/11 + //RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + RGraph.Redraw(); + + /** + * Put the cursor back to null + */ + //e.target.style.cursor = 'default'; + } + window.addEventListener('click', window_onclick_func, false); + RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func); + } + + + /** + * If a border is pecified, draw it + */ + if (this.Get('chart.border')) { + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.strokeStyle = this.Get('chart.border.color'); + + this.context.arc(this.centerx, + this.centery, + this.radius - 2, + 0, + 6.28, + 0); + + this.context.stroke(); + } + + /** + * Draw the kay if desired + */ + if (this.Get('chart.key') != null) { + //this.Set('chart.key.position', 'graph'); + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + + /** + * If this is actually a donut, draw a big circle in the middle + */ + if (this.Get('chart.variant') == 'donut') { + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.variant.donut.color'); + this.context.arc(this.centerx, this.centery, this.radius / 2, 0, 6.28, 0); + this.context.stroke(); + this.context.fill(); + } + + RGraph.NoShadow(this); + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws a single segment of the pie chart + * + * @param int degrees The number of degrees for this segment + */ + RGraph.Pie.prototype.DrawSegment = function (degrees, color, last, index) + { + var context = this.context; + var canvas = this.canvas; + var subTotal = this.subTotal; + degrees = degrees * this.Get('chart.effect.roundrobin.multiplier'); + + context.beginPath(); + + context.fillStyle = color; + context.strokeStyle = this.Get('chart.strokestyle'); + context.lineWidth = 0; + + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'),this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + /** + * Exploded segments + */ + if ( (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[index] > 0) || typeof(this.Get('chart.exploded')) == 'number') { + var explosion = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[index]; + var x = 0; + var y = 0; + var h = explosion; + var t = ((subTotal + (degrees / 2)) / (360/6.2830)) - 1.57; + var x = (Math.cos(t) * explosion); + var y = (Math.sin(t) * explosion); + + this.context.moveTo(this.centerx + x, this.centery + y); + } else { + var x = 0; + var y = 0; + } + + /** + * Calculate the angles + */ + var startAngle = ((subTotal / 57.3)) - 1.57; + var endAngle = (((subTotal + degrees) / 57.3)) - 1.57; + + context.arc(this.centerx + x, + this.centery + y, + this.radius, + startAngle, + endAngle, + 0); + + context.lineTo(this.centerx + x, this.centery + y); + + // Keep hold of the angles + this.angles.push([subTotal - 90, subTotal + degrees - 90, this.centerx + x, this.centery + y]) + this.context.closePath(); + + + + //this.context.stroke(); + this.context.fill(); + + /** + * Calculate the segment angle + */ + this.Get('chart.segments').push([subTotal, subTotal + degrees]); + this.subTotal += degrees; + } + + /** + * Draws the graphs labels + */ + RGraph.Pie.prototype.DrawLabels = function () + { + var hAlignment = 'left'; + var vAlignment = 'center'; + var labels = this.Get('chart.labels'); + var context = this.context; + + /** + * Turn the shadow off + */ + RGraph.NoShadow(this); + + context.fillStyle = 'black'; + context.beginPath(); + + /** + * Draw the key (ie. the labels) + */ + if (labels && labels.length) { + + var text_size = this.Get('chart.text.size'); + + for (i=0; i<labels.length; ++i) { + + /** + * T|his ensures that if we're given too many labels, that we don't get an error + */ + if (typeof(this.Get('chart.segments')[i]) == 'undefined') { + continue; + } + + // Move to the centre + context.moveTo(this.centerx,this.centery); + + var a = this.Get('chart.segments')[i][0] + ((this.Get('chart.segments')[i][1] - this.Get('chart.segments')[i][0]) / 2) - 90; + + /** + * Alignment + */ + if (a < 90) { + hAlignment = 'left'; + vAlignment = 'center'; + } else if (a < 180) { + hAlignment = 'right'; + vAlignment = 'center'; + } else if (a < 270) { + hAlignment = 'right'; + vAlignment = 'center'; + } else if (a < 360) { + hAlignment = 'left'; + vAlignment = 'center'; + } + +var angle = ((this.angles[i][1] - this.angles[i][0]) / 2) + this.angles[i][0]; + /** + * Handle the additional "explosion" offset + */ + if (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[i] || typeof(this.Get('chart.exploded')) == 'number') { + + var t = ((this.angles[i][1] - this.angles[i][0]) / 2) / (360/6.2830); + var seperation = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[i]; + + // Adjust the angles + var explosion_offsetx = (Math.cos(angle / 57.29) * seperation); + var explosion_offsety = (Math.sin(angle / 57.29) * seperation); + } else { + var explosion_offsetx = 0; + var explosion_offsety = 0; + } + + /** + * Allow for the label sticks + */ + if (this.Get('chart.labels.sticks')) { + explosion_offsetx += (Math.cos(angle / 57.29) * this.Get('chart.labels.sticks.length')); + explosion_offsety += (Math.sin(angle / 57.29) * this.Get('chart.labels.sticks.length')); + } + + + context.fillStyle = this.Get('chart.text.color'); + + RGraph.Text(context, + this.Get('chart.text.font'), + text_size, + this.centerx + explosion_offsetx + ((this.radius + 10)* Math.cos(a / 57.3)) + (this.Get('chart.labels.sticks') ? (a < 90 || a > 270 ? 2 : -2) : 0), + this.centery + explosion_offsety + (((this.radius + 10) * Math.sin(a / 57.3))), + labels[i], + vAlignment, + hAlignment); + } + + context.fill(); + } + } + + + /** + * This function draws the pie chart sticks (for the labels) + */ + RGraph.Pie.prototype.DrawSticks = function () + { + var context = this.context; + var offset = this.Get('chart.linewidth') / 2; + var exploded = this.Get('chart.exploded'); + var sticks = this.Get('chart.labels.sticks'); + + for (var i=0; i<this.angles.length; ++i) { + + // This allows the chart.labels.sticks to be an array as well as a boolean + if (typeof(sticks) == 'object' && !sticks[i]) { + continue; + } + + var degrees = this.angles[i][1] - this.angles[i][0]; + + context.beginPath(); + context.strokeStyle = this.Get('chart.labels.sticks.color'); + context.lineWidth = 1; + + var midpoint = (this.angles[i][0] + (degrees / 2)) / 57.3; + + if (typeof(exploded) == 'object' && exploded[i]) { + var extra = exploded[i]; + } else if (typeof(exploded) == 'number') { + var extra = exploded; + } else { + var extra = 0; + } + + context.lineJoin = 'round'; + context.lineWidth = 1; + + context.arc(this.centerx, + this.centery, + this.radius + this.Get('chart.labels.sticks.length') + extra, + midpoint, + midpoint + 0.001, + 0); + context.arc(this.centerx, + this.centery, + this.radius + extra, + midpoint, + midpoint + 0.001, + 0); + + context.stroke(); + } + } + + + /** + * The (now Pie chart specific) getSegment function + * + * @param object e The event object + */ + RGraph.Pie.prototype.getSegment = function (e) + { + RGraph.FixEventObject(e); + + // The optional arg provides a way of allowing some accuracy (pixels) + var accuracy = arguments[1] ? arguments[1] : 0; + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var r = obj.radius; + var angles = obj.angles; + var ret = []; + + for (var i=0; i<angles.length; ++i) { + + var x = mouseCoords[0] - angles[i][2]; + var y = mouseCoords[1] - angles[i][3]; + var theta = Math.atan(y / x); // RADIANS + var hyp = y / Math.sin(theta); + var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; + // Put theta in DEGREES + theta *= 57.3 + + /** + * Account for the correct quadrant + */ + if (x < 0 && y >= 0) { + theta += 180; + } else if (x < 0 && y < 0) { + theta += 180; + } + + if (theta > 360) { + theta -= 360; + } + + if (theta >= angles[i][0] && theta < angles[i][1]) { + + hyp = Math.abs(hyp); + + if (!hyp || (obj.radius && hyp > obj.radius) ) { + return null; + } + + if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) { + return null; + } + + ret[0] = angles[i][2]; + ret[1] = angles[i][3]; + ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius; + ret[3] = angles[i][0]; + ret[4] = angles[i][1]; + ret[5] = i; + + + + if (ret[3] < 0) ret[3] += 360; + if (ret[4] > 360) ret[4] -= 360; + + return ret; + } + } + + return null; + } + + + RGraph.Pie.prototype.DrawBorders = function () + { + if (this.Get('chart.linewidth') > 0) { + + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + for (var i=0,len=this.angles.length; i<len; ++i) { + this.context.beginPath(); + this.context.moveTo(this.angles[i][2], this.angles[i][3]); + this.context.arc(this.angles[i][2], + this.angles[i][3], + this.radius, + ((this.angles[i][0] / 57.3)), + ((this.angles[i][0] + 0.01) / 57.3), + 0); + this.context.arc(this.angles[i][2], + this.angles[i][3], + this.radius, + ((this.angles[i][0] / 57.3)), + this.angles[i][1] / 57.3, + 0); + this.context.closePath(); + + this.context.stroke(); + } + } + } + + + /** + * Returns the radius of the pie chart + */ + RGraph.Pie.prototype.getRadius = function () + { + return Math.min(this.canvas.height - this.gutterTop - this.gutterBottom, this.canvas.width - this.gutterLeft - this.gutterRight) / 2; + } + + + /** + * A programmatic explode function + * + * @param object obj The chart object + * @param number index The zero-indexed number of the segment + * @param number size The size (in pixels) of the explosion + */ + RGraph.Pie.prototype.Explode = function (index, size) + { + if (this.Get('chart.exploded') == 0) { + this.Set('chart.exploded', []); + } + + var obj = this; + + for (var i=0; i<size; ++i) { + setTimeout( + function () + { + var exploded = obj.Get('chart.exploded'); + var value = typeof(exploded[index]) == 'number' ? exploded[index] : 0; + exploded[index] = value + 1; + obj.Set('chart.exploded', exploded); + RGraph.Clear(obj.canvas); + obj.Draw(); + }, i * 16.666); + } + + /** + * Now set the property accordingly + */ + setTimeout( + function () + { + obj.Set('chart.exploded', exploded); + }, i * 16.666 + ) + + //var Reset = function () + //{ + // pie.Set('chart.exploded', []); + // RGraph.Clear(pie.canvas); + // pie.Draw(); + //} + //window.addEventListener('click', Reset, false); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.pie.js.old b/schall/static/RGraph/libraries/RGraph.pie.js.old new file mode 100644 index 0000000..8b9dc5b --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.pie.js.old @@ -0,0 +1,975 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The pie chart constructor + * + * @param data array The data to be represented on the pie chart + */ + RGraph.Pie = function (id, data) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext("2d"); + this.canvas.__object__ = this; + this.total = 0; + this.subTotal = 0; + this.angles = []; + this.data = data; + this.properties = []; + this.type = 'pie'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + this.properties = { + 'chart.colors': ['rgb(255,0,0)', '#ddd', 'rgb(0,255,0)', 'rgb(0,0,255)', 'pink', 'yellow', '#000'], + 'chart.strokestyle': '#999', + 'chart.linewidth': 1, + 'chart.labels': [], + 'chart.labels.sticks': false, + 'chart.labels.sticks.color': '#aaa', + 'chart.segments': [], + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': 0.5, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.shadow': false, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.tooltips': [], + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.style': '3d', + 'chart.highlight.style.2d.fill': 'rgba(255,255,255,0.5)', + 'chart.highlight.style.2d.stroke': 'rgba(255,255,255,0)', + 'chart.centerx': null, + 'chart.centery': null, + 'chart.radius': null, + 'chart.border': false, + 'chart.border.color': 'rgba(255,255,255,0.5)', + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.align': 'center', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.variant': 'pie', + 'chart.variant.donut.color': 'white', + 'chart.exploded': [], + 'chart.effect.roundrobin.multiplier': 1 + } + + /** + * Calculate the total + */ + for (var i=0,len=data.length; i<len; i++) { + this.total += data[i]; + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getSegment; + } + + + /** + * A generic setter + */ + RGraph.Pie.prototype.Set = function (name, value) + { + if (name == 'chart.highlight.style.2d.color') { + name = 'chart.highlight.style.2d.fill'; + } + + this.properties[name] = value; + } + + + /** + * A generic getter + */ + RGraph.Pie.prototype.Get = function (name) + { + if (name == 'chart.highlight.style.2d.color') { + name = 'chart.highlight.style.2d.fill'; + } + + return this.properties[name]; + } + + + /** + * This draws the pie chart + */ + RGraph.Pie.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Reset this to an empty array + */ + this.Set('chart.segments', []); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + this.radius = this.Get('chart.radius') ? this.Get('chart.radius') : this.getRadius(); + // this.centerx now defined below + this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + this.subTotal = 0; + this.angles = []; + + /** + * Allow the specification of a custom centery + */ + if (typeof(this.Get('chart.centery')) == 'number') { + this.centery = this.Get('chart.centery'); + } + + /** + * Alignment (Pie is center aligned by default) Only if centerx is not defined - donut defines the centerx + */ + if (this.Get('chart.align') == 'left') { + this.centerx = this.radius + this.gutterLeft; + + } else if (this.Get('chart.align') == 'right') { + this.centerx = this.canvas.width - this.radius - this.gutterRight; + + } else { + this.centerx = this.canvas.width / 2; + } + + /** + * Allow the specification of a custom centerx + */ + if (typeof(this.Get('chart.centerx')) == 'number') { + this.centerx = this.Get('chart.centerx'); + } + + /** + * Draw the shadow if required + */ + if (this.Get('chart.shadow') && 0) { + + var offsetx = document.all ? this.Get('chart.shadow.offsetx') : 0; + var offsety = document.all ? this.Get('chart.shadow.offsety') : 0; + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.shadow.color'); + + this.context.shadowColor = this.Get('chart.shadow.color'); + this.context.shadowBlur = this.Get('chart.shadow.blur'); + this.context.shadowOffsetX = this.Get('chart.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.shadow.offsety'); + + this.context.arc(this.centerx + offsetx, this.centery + offsety, this.radius, 0, 6.28, 0); + + this.context.fill(); + + // Now turn off the shadow + RGraph.NoShadow(this); + } + + /** + * The total of the array of values + */ + this.total = RGraph.array_sum(this.data); + + for (var i=0,len=this.data.length; i<len; i++) { + var angle = (this.data[i] / this.total) * 360; + + // Draw the segment + this.DrawSegment(angle,this.Get('chart.colors')[i],i == (this.data.length - 1), i); + } + + RGraph.NoShadow(this); + + + /** + * Redraw the seperating lines + */ + this.DrawBorders(); + + /** + * Now draw the segments again with shadow turned off. This is always performed, + * not just if the shadow is on. + */ + for (var i=0; i<this.angles.length; i++) { + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[i]; + this.context.moveTo(this.angles[i][2], this.angles[i][3]); + this.context.arc(this.angles[i][2], + this.angles[i][3], + this.radius, + (this.angles[i][0] / 57.3) * this.Get('chart.effect.roundrobin.multiplier'), + (this.angles[i][1] / 57.3) * this.Get('chart.effect.roundrobin.multiplier'), + false); + this.context.lineTo(this.angles[i][2], this.angles[i][3]); + this.context.closePath(); + this.context.fill(); + this.context.stroke(); + } + + /** + * Draw label sticks + */ + if (this.Get('chart.labels.sticks')) { + this.DrawSticks(); + + // Redraw the border going around the Pie chart if the stroke style is NOT white + if ( + this.Get('chart.strokestyle') != 'white' + && this.Get('chart.strokestyle') != '#fff' + && this.Get('chart.strokestyle') != '#fffffff' + && this.Get('chart.strokestyle') != 'rgb(255,255,255)' + && this.Get('chart.strokestyle') != 'rgba(255,255,255,0)' + ) { + + // Again (?) + this.DrawBorders(); + } + } + + /** + * Draw the labels + */ + this.DrawLabels(); + + /** + * Draw the title + */ + if (this.Get('chart.align') == 'left') { + var centerx = this.radius + this.Get('chart.gutter.left'); + + } else if (this.Get('chart.align') == 'right') { + var centerx = RGraph.GetWidth(this) - (this.radius + this.gutterRight); + + } else { + var centerx = null; + } + + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + (this.canvas.height / 2) - this.radius - 5, + centerx, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Tooltips + */ + if (this.Get('chart.tooltips').length) { + + /** + * Register this object for redrawing + */ + RGraph.Register(this); + + /** + * The onclick event + */ + //this.canvas.onclick = function (e) + var canvas_onclick_func = function (e) + { + RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + var mouseCoords = RGraph.getMouseXY(e); + + var canvas = e.target; + var context = canvas.getContext('2d'); + var obj = e.target.__object__; + + + + /** + * If it's actually a donut make sure the hyp is bigger + * than the size of the hole in the middle + */ + if (obj.Get('chart.variant') == 'donut' && Math.abs(hyp) < (obj.radius / 2)) { + return; + } + + /** + * The angles for each segment are stored in "angles", + * so go through that checking if the mouse position corresponds + */ + var isDonut = obj.Get('chart.variant') == 'donut'; + var hStyle = obj.Get('chart.highlight.style'); + var segment = obj.getSegment(e); + + if (segment) { + + var x = mouseCoords[0] - segment[0]; + var y = mouseCoords[1] - segment[1]; + var theta = Math.atan(y / x); // RADIANS + var hyp = y / Math.sin(theta); + + + if ( RGraph.Registry.Get('chart.tooltip') + && segment[5] == RGraph.Registry.Get('chart.tooltip').__index__ + && RGraph.Registry.Get('chart.tooltip').__canvas__.__object__.id == obj.id) { + + return; + + } else { + RGraph.Redraw(); + } + + + if (isDonut || hStyle == '2d') { + + context.beginPath(); + + context.strokeStyle = obj.Get('chart.highlight.style.2d.stroke'); + context.fillStyle = obj.Get('chart.highlight.style.2d.fill'); + + //context.moveTo(obj.centerx, obj.centery); + + context.moveTo(segment[0], segment[1]); + context.arc(segment[0], segment[1], segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); + context.lineTo(segment[0], segment[1]); + context.closePath(); + + context.stroke(); + context.fill(); + + //Removed 7th December 2010 + //context.stroke(); + + } else if (hStyle == 'explode') { + + var exploded = []; + + exploded[segment[5]] = 0; + + RGraph.Registry.Set('chart.pie.exploded', obj); + + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 25); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 50); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 75); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 100); + setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 125); + + setTimeout(function () {RGraph.Registry.Get('chart.pie.exploded').Set('chart.exploded', []);}, 150); + + } else { + + context.lineWidth = 2; + + /** + * Draw a white segment where the one that has been clicked on was + */ + context.fillStyle = 'white'; + context.strokeStyle = 'white'; + context.beginPath(); + context.moveTo(segment[0], segment[1]); + context.arc(segment[0], segment[1], segment[2], obj.angles[segment[5]][0] / 57.3, obj.angles[segment[5]][1] / 57.3, 0); + context.stroke(); + context.fill(); + + context.lineWidth = 1; + + context.shadowColor = '#666'; + context.shadowBlur = 3; + context.shadowOffsetX = 3; + context.shadowOffsetY = 3; + + // Draw the new segment + context.beginPath(); + context.fillStyle = obj.Get('chart.colors')[segment[5]]; + context.strokeStyle = obj.Get('chart.strokestyle'); + context.moveTo(segment[0] - 3, segment[1] - 3); + context.arc(segment[0] - 3, segment[1] - 3, segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0); + context.lineTo(segment[0] - 3, segment[1] - 3); + context.closePath(); + + context.stroke(); + context.fill(); + + // Turn off the shadow + RGraph.NoShadow(obj); + + /** + * If a border is defined, redraw that + */ + if (obj.Get('chart.border')) { + context.beginPath(); + context.strokeStyle = obj.Get('chart.border.color'); + context.lineWidth = 5; + context.arc(segment[0] - 3, segment[1] - 3, obj.radius - 2, RGraph.degrees2Radians(obj.angles[i][0]), RGraph.degrees2Radians(obj.angles[i][1]), 0); + context.stroke(); + } + } + + /** + * If a tooltip is defined, show it + */ + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), segment[5]); + + if (text) { + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[5]); + } + + /** + * Need to redraw the key? + */ + if (obj.Get('chart.key') && obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') { + RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors')); + } + + e.stopPropagation(); + + return; + } else if (obj.Get('chart.tooltips.event') == 'onclick') { + RGraph.Redraw(); + } + } + var event_name = this.Get('chart.tooltips.event') == 'onmousemove' ? 'mousemove' : 'click'; + + this.canvas.addEventListener(event_name, canvas_onclick_func, false); + RGraph.AddEventListener(this.id, event_name, canvas_onclick_func); + + + + + + + /** + * The onmousemove event for changing the cursor + */ + //this.canvas.onmousemove = function (e) + var canvas_onmousemove_func = function (e) + { + RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + var obj = e.target.__object__; + var segment = obj.getSegment(e); + + if (segment) { + e.target.style.cursor = 'pointer'; + + return; + } + + /** + * Put the cursor back to null + */ + e.target.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + + + + + + + + + + /** + * The window onclick function + */ + var window_onclick_func = function (e) + { + // Taken out on 02/10/11 + //RGraph.HideZoomedCanvas(); + + e = RGraph.FixEventObject(e); + + RGraph.Redraw(); + + /** + * Put the cursor back to null + */ + //e.target.style.cursor = 'default'; + } + window.addEventListener('click', window_onclick_func, false); + RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func); + } + + + /** + * If a border is pecified, draw it + */ + if (this.Get('chart.border')) { + this.context.beginPath(); + this.context.lineWidth = 5; + this.context.strokeStyle = this.Get('chart.border.color'); + + this.context.arc(this.centerx, + this.centery, + this.radius - 2, + 0, + 6.28, + 0); + + this.context.stroke(); + } + + /** + * Draw the kay if desired + */ + if (this.Get('chart.key') != null) { + //this.Set('chart.key.position', 'graph'); + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + + /** + * If this is actually a donut, draw a big circle in the middle + */ + if (this.Get('chart.variant') == 'donut') { + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.variant.donut.color'); + this.context.arc(this.centerx, this.centery, this.radius / 2, 0, 6.28, 0); + this.context.stroke(); + this.context.fill(); + } + + RGraph.NoShadow(this); + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws a single segment of the pie chart + * + * @param int degrees The number of degrees for this segment + */ + RGraph.Pie.prototype.DrawSegment = function (degrees, color, last, index) + { + var context = this.context; + var canvas = this.canvas; + var subTotal = this.subTotal; + + context.beginPath(); + + context.fillStyle = color; + context.strokeStyle = this.Get('chart.strokestyle'); + context.lineWidth = 0; + + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'),this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + /** + * Exploded segments + */ + if ( (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[index] > 0) || typeof(this.Get('chart.exploded')) == 'number') { + var explosion = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[index]; + var x = 0; + var y = 0; + var h = explosion; + var t = (subTotal + (degrees / 2)) / (360/6.2830); + var x = (Math.cos(t) * explosion); + var y = (Math.sin(t) * explosion); + + this.context.moveTo(this.centerx + x, this.centery + y); + } else { + var x = 0; + var y = 0; + } + + /** + * Calculate the angles + */ + var startAngle = subTotal / 57.3; + var endAngle = (last ? 360 : subTotal + degrees) / 57.3; + + context.arc(this.centerx + x, + this.centery + y, + this.radius, + startAngle * this.Get('chart.effect.roundrobin.multiplier'), + endAngle * this.Get('chart.effect.roundrobin.multiplier'), + 0); + + context.lineTo(this.centerx + x, this.centery + y); + + // Keep hold of the angles + this.angles.push([subTotal, subTotal + degrees, this.centerx + x, this.centery + y]) + this.context.closePath(); + + + + //this.context.stroke(); + this.context.fill(); + + /** + * Calculate the segment angle + */ + this.Get('chart.segments').push([subTotal, subTotal + degrees]); + this.subTotal += degrees; + } + + /** + * Draws the graphs labels + */ + RGraph.Pie.prototype.DrawLabels = function () + { + var hAlignment = 'left'; + var vAlignment = 'center'; + var labels = this.Get('chart.labels'); + var context = this.context; + + /** + * Turn the shadow off + */ + RGraph.NoShadow(this); + + context.fillStyle = 'black'; + context.beginPath(); + + /** + * Draw the key (ie. the labels) + */ + if (labels && labels.length) { + + var text_size = this.Get('chart.text.size'); + + for (i=0; i<labels.length; ++i) { + + /** + * T|his ensures that if we're given too many labels, that we don't get an error + */ + if (typeof(this.Get('chart.segments')[i]) == 'undefined') { + continue; + } + + // Move to the centre + context.moveTo(this.centerx,this.centery); + + var a = this.Get('chart.segments')[i][0] + ((this.Get('chart.segments')[i][1] - this.Get('chart.segments')[i][0]) / 2); + + /** + * Alignment + */ + if (a < 90) { + hAlignment = 'left'; + vAlignment = 'center'; + } else if (a < 180) { + hAlignment = 'right'; + vAlignment = 'center'; + } else if (a < 270) { + hAlignment = 'right'; + vAlignment = 'center'; + } else if (a < 360) { + hAlignment = 'left'; + vAlignment = 'center'; + } + + + /** + * Handle the additional "explosion" offset + */ + if (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[i] || typeof(this.Get('chart.exploded')) == 'number') { + + var t = ((this.angles[i][1] - this.angles[i][0]) / 2) / (360/6.2830); + var seperation = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[i]; + var angle = ((this.angles[i][1] - this.angles[i][0]) / 2) + this.angles[i][0]; + + // Adjust the angles + var explosion_offsetx = (Math.cos(angle / 57.29) * seperation); + var explosion_offsety = (Math.sin(angle / 57.29) * seperation); + } else { + var explosion_offsetx = 0; + var explosion_offsety = 0; + } + + context.fillStyle = this.Get('chart.text.color'); + + RGraph.Text(context, + this.Get('chart.text.font'), + text_size, + this.centerx + explosion_offsetx + ((this.radius + 10)* Math.cos(a / 57.3)) + (this.Get('chart.labels.sticks') ? (a < 90 || a > 270 ? 2 : -2) : 0), + this.centery + explosion_offsety + (((this.radius + 10) * Math.sin(a / 57.3))), + labels[i], + vAlignment, + hAlignment); + } + + context.fill(); + } + } + + + /** + * This function draws the pie chart sticks (for the labels) + */ + RGraph.Pie.prototype.DrawSticks = function () + { + var context = this.context; + var segments = this.Get('chart.segments'); + var offset = this.Get('chart.linewidth') / 2; + var exploded = this.Get('chart.exploded'); + var sticks = this.Get('chart.labels.sticks'); + + for (var i=0; i<segments.length; ++i) { + + // This allows the chart.labels.sticks to be an array as well as a boolean + if (typeof(sticks) == 'object' && !sticks[i]) { + continue; + } + + var degrees = segments[i][1] - segments[i][0]; + + context.beginPath(); + context.strokeStyle = this.Get('chart.labels.sticks.color'); + context.lineWidth = 1; + + var midpoint = (segments[i][0] + (degrees / 2)) / 57.3; + + if (exploded && exploded[i]) { + var extra = exploded[i]; + } else { + var extra = 0; + } + + context.lineJoin = 'round'; + context.lineWidth = 1; + + context.arc(this.centerx, + this.centery, + this.radius + 7 + extra, + midpoint, + midpoint + 0.01, + 0); + + context.arc(this.centerx, + this.centery, + this.radius - offset + extra, + midpoint, + midpoint + 0.01, + 0); + + context.stroke(); + } + } + + + /** + * The (now Pie chart specific) getSegment function + * + * @param object e The event object + */ + RGraph.Pie.prototype.getSegment = function (e) + { + RGraph.FixEventObject(e); + + // The optional arg provides a way of allowing some accuracy (pixels) + var accuracy = arguments[1] ? arguments[1] : 0; + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var r = obj.radius; + var angles = obj.angles; + var ret = []; + + for (var i=0; i<angles.length; ++i) { + + var x = mouseCoords[0] - angles[i][2]; + var y = mouseCoords[1] - angles[i][3]; + var theta = Math.atan(y / x); // RADIANS + var hyp = y / Math.sin(theta); + var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; + // Put theta in DEGREES + theta *= 57.3 + + /** + * Account for the correct quadrant + */ + if (x < 0 && y >= 0) { + theta += 180; + } else if (x < 0 && y < 0) { + theta += 180; + } else if (x > 0 && y < 0) { + theta += 360; + } + + if (theta > 360) { + theta -= 360; + } + + if (theta >= angles[i][0] && theta < angles[i][1]) { + + hyp = Math.abs(hyp); + + if (!hyp || (obj.radius && hyp > obj.radius) ) { + return null; + } + + if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) { + return null; + } + + ret[0] = angles[i][2]; + ret[1] = angles[i][3]; + ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius; + ret[3] = angles[i][0]; + ret[4] = angles[i][1]; + ret[5] = i; + + + + if (ret[3] < 0) ret[3] += 360; + if (ret[4] > 360) ret[4] -= 360; + + return ret; + } + } + + return null; + } + + + RGraph.Pie.prototype.DrawBorders = function () + { + if (this.Get('chart.linewidth') > 0) { + + this.context.lineWidth = this.Get('chart.linewidth'); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + for (var i=0,len=this.angles.length; i<len; ++i) { + this.context.beginPath(); + this.context.moveTo(this.angles[i][2], this.angles[i][3]); + this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][0] + 0.01) / 57.3, 0); + this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][1]) / 57.3, 0); + this.context.closePath(); + + this.context.stroke(); + } + } + } + + + /** + * Returns the radius of the pie chart + */ + RGraph.Pie.prototype.getRadius = function () + { + return Math.min(this.canvas.height - this.gutterTop - this.gutterBottom, this.canvas.width - this.gutterLeft - this.gutterRight) / 2; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.radar.js b/schall/static/RGraph/libraries/RGraph.radar.js new file mode 100644 index 0000000..fd63e8f --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.radar.js @@ -0,0 +1,739 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The traditional radar chart constructor + * + * @param string id The ID of the canvas + * @param array data An array of data to represent + */ + RGraph.Radar = function (id, data) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.canvas.__object__ = this; + this.size = null;// Set in the .Draw() method + this.type = 'radar'; + this.coords = []; + this.isRGraph = true; + this.data = []; + this.max = 0; + + for (var i=1; i<arguments.length; ++i) { + this.data.push(RGraph.array_clone(arguments[i])); + this.max = Math.max(this.max, RGraph.array_max(arguments[i])); + } + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + this.properties = { + 'chart.strokestyle': 'black', + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.linewidth': 1, + 'chart.colors': ['red', 'green', 'blue'], + 'chart.colors.alpha': null, + 'chart.circle': 0, + 'chart.circle.fill': 'red', + 'chart.circle.stroke': 'black', + 'chart.labels': [], + 'chart.labels.offsetx': 10, + 'chart.labels.offsety': 10, + 'chart.background.circles': true, + 'chart.text.size': 10, + 'chart.text.font': 'Verdana', + 'chart.text.color': 'black', + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.color': 'black', + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.linewidth': 1, + + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + + 'chart.contextmenu': null, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'gray', + 'chart.highlight.fill': 'white', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.labels.axes': 'nsew', + 'chart.ymax': null + } + + // Must have at least 3 points + for (var dataset=0; dataset<this.data.length; ++dataset) { + if (this.data[dataset].length < 3) { + alert('[RADAR] You must specify at least 3 data points'); + return; + } + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getPoint; + } + + + /** + * A simple setter + * + * @param string name The name of the property to set + * @param string value The value of the property + */ + RGraph.Radar.prototype.Set = function (name, value) + { + this.properties[name] = value; + + /** + * If the name is chart.color, set chart.colors too + */ + if (name == 'chart.color') { + this.properties['chart.colors'] = [value]; + } + } + + + /** + * A simple hetter + * + * @param string name The name of the property to get + */ + RGraph.Radar.prototype.Get = function (name) + { + return this.properties[name]; + } + + + /** + * The draw method which does all the brunt of the work + */ + RGraph.Radar.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft; + this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + this.size = Math.min(this.canvas.width - this.gutterLeft - this.gutterRight, this.canvas.height - this.gutterTop - this.gutterBottom); + + // Work out the maximum value and the sum + if (!this.Get('chart.ymax')) { + + // this.max is calculated in the constructor + + this.scale = RGraph.getScale(this.max, this); + this.max = this.scale[4]; + } else { + var ymax = this.Get('chart.ymax'); + + this.scale = [ + ymax * 0.2, + ymax * 0.4, + ymax * 0.6, + ymax * 0.8, + ymax * 1 + ]; + this.max = this.scale[4]; + } + + this.DrawBackground(); + this.DrawAxes(); + this.DrawCircle(); + this.DrawAxisLabels(); + this.DrawChart(); + this.DrawLabels(); + + // Draw the title + if (this.Get('chart.title')) { + RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : null) + } + + // Draw the key if necessary + // obj, key, colors + if (this.Get('chart.key')) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + /** + * Show the context menu + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * This function enables adjusting + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the background circles + */ + RGraph.Radar.prototype.DrawBackground = function () + { + var color = '#ddd'; + + /** + * Draws the background circles + */ + if (this.Get('chart.background.circles')) { + + this.context.strokeStyle = color; + this.context.beginPath(); + + for (var r=5; r<(this.size / 2); r+=15) { + + this.context.moveTo(this.centerx, this.centery); + this.context.arc(this.centerx, this.centery,r, 0, 6.28, 0); + } + + this.context.stroke(); + } + + + /** + * Draw diagonals + */ + this.context.strokeStyle = color; + for (var i=0; i<360; i+=15) { + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, this.size / 2, (i / 360) * (2 * Math.PI), ((i+0.01) / 360) * (2 * Math.PI), 0); // The 0.01 avoids a bug in Chrome 6 + this.context.lineTo(this.centerx, this.centery); + this.context.stroke(); + } + } + + + /** + * Draws the axes + */ + RGraph.Radar.prototype.DrawAxes = function () + { + this.context.strokeStyle = 'black'; + + var halfsize = this.size / 2; + + this.context.beginPath(); + + /** + * The Y axis + */ + this.context.moveTo(this.centerx, this.centery + halfsize); + this.context.lineTo(this.centerx, this.centery - halfsize); + + + // Draw the bits at either end of the Y axis + this.context.moveTo(this.centerx - 5, this.centery + halfsize); + this.context.lineTo(this.centerx + 5, this.centery + halfsize); + this.context.moveTo(this.centerx - 5, this.centery - halfsize); + this.context.lineTo(this.centerx + 5, this.centery - halfsize); + + // Draw X axis tick marks + for (var y=(this.centery - halfsize); y<(this.centery + halfsize); y+=15) { + this.context.moveTo(this.centerx - 3, y); + this.context.lineTo(this.centerx + 3, y); + } + + /** + * The X axis + */ + this.context.moveTo(this.centerx - halfsize, this.centery); + this.context.lineTo(this.centerx + halfsize, this.centery); + + // Draw the bits at the end of the X axis + this.context.moveTo(this.centerx - halfsize, this.centery - 5); + this.context.lineTo(this.centerx - halfsize, this.centery + 5); + this.context.moveTo(this.centerx + halfsize, this.centery - 5); + this.context.lineTo(this.centerx + halfsize, this.centery + 5); + + // Draw X axis tick marks + for (var x=(this.centerx - halfsize); x<(this.centerx + halfsize); x+=15) { + this.context.moveTo(x, this.centery - 3); + this.context.lineTo(x, this.centery + 3); + } + + /** + * Finally draw it to the canvas + */ + this.context.stroke(); + } + + + /** + * The function which actually draws the radar chart + */ + RGraph.Radar.prototype.DrawChart = function () + { + var alpha = this.Get('chart.colors.alpha'); + + if (typeof(alpha) == 'number') { + var oldAlpha = this.context.globalAlpha; + this.context.globalAlpha = alpha; + } + + for (var dataset=0; dataset<this.data.length; ++dataset) { + + this.context.beginPath(); + + this.coords[dataset] = []; + + for (var i=0; i<this.data[dataset].length; ++i) { + this.coords[dataset][i] = this.GetCoordinates(dataset, i); + } + + /** + * Now go through the coords and draw the chart itself + */ + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[dataset]; + this.context.lineWidth = this.Get('chart.linewidth'); + + for (i=0; i<this.coords[dataset].length; ++i) { + if (i == 0) { + this.context.moveTo(this.coords[dataset][i][0], this.coords[dataset][i][1]); + } else { + this.context.lineTo(this.coords[dataset][i][0], this.coords[dataset][i][1]); + } + } + + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + /** + * Can now handletooltips + */ + if (this.Get('chart.tooltips')) { + + RGraph.Register(this); + + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = e.target; + var obj = canvas.__object__; + var overHotspot = false; + var point = obj.getPoint(e); + + + if (point) { + + var dataset = point[3]; + var idx = point[4]; + + if ( !RGraph.Registry.Get('chart.tooltip') + || (RGraph.Registry.Get('chart.tooltip').__index__ != idx && RGraph.Registry.Get('chart.tooltip').__dataset__ != dataset) + || (RGraph.Registry.Get('chart.tooltip').__index__ != idx && RGraph.Registry.Get('chart.tooltip').__dataset__ == dataset) + ) { + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx); + + if (typeof(text) == 'string' && text.length) { + + overHotspot = true; + obj.canvas.style.cursor = 'pointer'; + + RGraph.Clear(obj.canvas); + obj.Draw(); + + if (obj.Get('chart.tooltips.highlight')) { + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.arc(point[1], point[2], 2, 0, 6.28, 0); + obj.context.fill(); + obj.context.stroke(); + } + + RGraph.Tooltip(obj.canvas, text, e.pageX, e.pageY, idx); + + // Set the data set value on the tooltip + RGraph.Registry.Get('chart.tooltip').__index__ = idx; + RGraph.Registry.Get('chart.tooltip').__dataset__ = dataset; + + + } + //} else if (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ == idx && RGraph.Registry.Get('chart.tooltip').__dataset__ == dataset) { + } else { + overHotspot = true; + obj.canvas.style.cursor = 'pointer'; + } + } + + if (!overHotspot) { + obj.canvas.style.cursor = 'default'; + } + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + } + + // Reset the globalAlpha + if (typeof(alpha) == 'number') { + this.context.globalAlpha = oldAlpha; + } + } + + + /** + * Gets the coordinates for a particular mark + * + * @param number i The index of the data (ie which one it is) + * @return array A two element array of the coordinates + */ + RGraph.Radar.prototype.GetCoordinates = function (dataset, index) + { + // The number of data points + var len = this.data[dataset].length; + + // The magnitude of the data (NOT the x/y coords) + var mag = (this.data[dataset][index] / this.max) * (this.size / 2); + + /** + * Get the angle + */ + var angle = (6.28 / len) * index; // In radians + + /** + * Work out the X/Y coordinates + */ + var x = Math.cos(angle) * mag; + var y = Math.sin(angle) * mag; + + /** + * Put the coordinate in the right quadrant + */ + x = this.centerx + x; + y = this.centery + (index == 0 ? 0 : y); + + return [x,y]; + } + + + /** + * This function adds the labels to the chart + */ + RGraph.Radar.prototype.DrawLabels = function () + { + var labels = this.Get('chart.labels'); + + if (labels && labels.length > 0) { + + this.context.lineWidth = 1; + this.context.fillStyle = this.Get('chart.text.color'); + + var offsetx = this.Get('chart.labels.offsetx'); + var offsety = this.Get('chart.labels.offsety'); + + for (var i=0; i<labels.length; ++i) { + + var ds = 0; + + for (var dataset=0; dataset<this.data.length; ++dataset) { + if (this.data[dataset][i] > this.data[ds][i]) { + ds = dataset; + } + } + + var x = this.coords[ds][i][0]; + var y = this.coords[ds][i][1]; + var text = labels[i]; + var hAlign = 'center'; + var vAlign = 'center'; + var quartile = (i / this.coords.length); + + // ~Manually do labels on the right middle axis + if (i == 0) { + hAlign = 'left'; + vAlign = 'center'; + x += offsetx; + + } else { + + hAlign = (x < this.centerx) ? 'right' : 'left'; + vAlign = (y < this.centery) ? 'bottom' : 'top'; + x += (x < this.centerx) ? (-1 * offsetx) : offsetx; + y += (y < this.centery) ? (-1 * offsety) : offsety; + + if (i / this.data.length == 0.25) { x -= offsetx; hAlign = 'center'; + } else if (i / this.data.length == 0.5) { y -= offsety; vAlign = 'center'; + } else if (i / this.data.length == 0.75) { x += offsetx; hAlign = 'center'; } + } + + // context, font, size, x, y, text + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, text, vAlign, hAlign, true, null, 'white'); + } + } + } + + + /** + * Draws the circle. No arguments as it gets the information from the object properties. + */ + RGraph.Radar.prototype.DrawCircle = function () + { + var circle = {}; + circle.limit = this.Get('chart.circle'); + circle.fill = this.Get('chart.circle.fill'); + circle.stroke = this.Get('chart.circle.stroke'); + + if (circle.limit) { + + var r = (circle.limit / this.max) * (this.size / 2); + + this.context.fillStyle = circle.fill; + this.context.strokeStyle = circle.stroke; + + this.context.beginPath(); + this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0); + this.context.fill(); + this.context.stroke(); + } + } + + + /** + * Unsuprisingly, draws the labels + */ + RGraph.Radar.prototype.DrawAxisLabels = function () + { + /** + * Draw specific axis labels + */ + if (this.Get('chart.labels.specific')) { + this.DrawSpecificAxisLabels(); + return; + } + + this.context.lineWidth = 1; + + // Set the color to black + this.context.fillStyle = 'black'; + this.context.strokeStyle = 'black'; + + var r = (this.size/ 2); + var font_face = this.Get('chart.text.font'); + var font_size = this.Get('chart.text.size'); + var context = this.context; + var axes = this.Get('chart.labels.axes').toLowerCase(); + var color = 'rgba(255,255,255,0.8)'; + + // The "North" axis labels + if (axes.indexOf('n') > -1) { + RGraph.Text(context,font_face,font_size,this.centerx,this.centery - (r * 0.2),String(this.scale[0]),'center','center',true,false,color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "South" axis labels + if (axes.indexOf('s') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "East" axis labels + if (axes.indexOf('e') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "West" axis labels + if (axes.indexOf('w') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color); + } + + RGraph.Text(context, font_face, font_size, this.centerx, this.centery, '0', 'center', 'center', true, false, color); + } + + + /** + * Draws specific axis labels + */ + RGraph.Radar.prototype.DrawSpecificAxisLabels = function () + { + /** + * Specific Y labels + */ + var labels = this.Get('chart.labels.specific'); + var context = this.context; + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + var axes = this.Get('chart.labels.axes').toLowerCase(); + var interval = this.size / (labels.length * 2); + + for (var i=0; i<labels.length; ++i) { + + if (axes.indexOf('n') > -1) RGraph.Text(context,font,size,this.gutterLeft + (this.size / 2),this.gutterTop + (i * interval),labels[i],'center','center', true, false, 'rgba(255,255,255,0.8)'); + if (axes.indexOf('s') > -1) RGraph.Text(context,font,size,this.gutterLeft + (this.size / 2),this.gutterTop + (this.size / 2) + (i * interval) + interval,RGraph.array_reverse(labels)[i],'center','center', true, false, 'rgba(255,255,255,0.8)'); + if (axes.indexOf('w') > -1) RGraph.Text(context,font,size,this.gutterLeft + (i * interval),this.gutterTop + (this.size / 2),labels[i],'center','center', true, false, 'rgba(255,255,255,0.8)'); + if (axes.indexOf('e') > -1) RGraph.Text(context,font,size,this.gutterLeft + (i * interval) + interval + (this.size / 2),this.gutterTop + (this.size / 2),RGraph.array_reverse(labels)[i],'center','center', true, false, 'rgba(255,255,255,0.8)'); + } + } + + + /** + * This method eases getting the focussed point (if any) + * + * @param event e The event object + */ + RGraph.Radar.prototype.getPoint = function (e) + { + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + + for(var dataset=0; dataset<obj.coords.length; ++dataset) { + for (var i=0; i<obj.coords[dataset].length; ++i) { + + var x = obj.coords[dataset][i][0]; + var y = obj.coords[dataset][i][1]; + var tooltips = obj.Get('chart.tooltips'); + var idx = Number(i); + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + if ( + (tooltips[i] || tooltips) // The order here is important due to short circuiting + && mouseX < (x + 5) + && mouseX > (x - 5) + && mouseY > (y - 5) + && mouseY < (y + 5) + ) { + + // This accounts for the datasets and increases the index accordingly + for(var j=0; j<dataset; j++) { + if (typeof(obj.data[j]) == 'object') { + i += obj.data[j].length; + } + } + + return [obj, x, y, dataset, i]; + } + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.rose.js b/schall/static/RGraph/libraries/RGraph.rose.js new file mode 100644 index 0000000..99bf353 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.rose.js @@ -0,0 +1,894 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The rose chart constuctor + * + * @param object canvas + * @param array data + */ + RGraph.Rose = function (id, data) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.data = data; + this.canvas.__object__ = this; + this.type = 'rose'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + this.centerx = 0; + this.centery = 0; + this.radius = 0; + this.max = 0; + + this.properties = { + 'chart.radius': null, + 'chart.colors': ['red', 'rgb(0,255,255)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'], + 'chart.colors.sequential': false, + 'chart.colors.alpha': null, + 'chart.margin': 0, + 'chart.strokestyle': 'rgba(0,0,0,0.5)', + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.labels': null, + 'chart.labels.position': 'center', + 'chart.labels.axes': 'nsew', + 'chart.labels.offset': 0, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.text.size': 10, + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.contextmenu': null, + 'chart.tooltips': null, + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.ymax': null, + 'chart.ymin': 0, + 'chart.scale.decimals': null, + 'chart.variant': 'stacked', + 'chart.animation.grow.factor': 1 + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getSegment; + } + + + /** + * A simple setter + * + * @param string name The name of the property to set + * @param string value The value of the property + */ + RGraph.Rose.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A simple getter + * + * @param string name The name of the property to get + */ + RGraph.Rose.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * This method draws the rose chart + */ + RGraph.Rose.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + /** + * This doesn't affect the chart, but is used for compatibility + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Calculate the radius + this.radius = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2); + this.centerx = RGraph.GetWidth(this) / 2; + this.centery = RGraph.GetHeight(this) / 2; + this.angles = []; + this.total = 0; + this.startRadians = 0; + + // User specified radius + if (typeof(this.Get('chart.radius')) == 'number') { + this.radius = this.Get('chart.radius'); + } + + /** + * Change the centerx marginally if the key is defined + */ + if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) { + this.centerx = this.centerx - this.Get('chart.gutter.right') + 5; + } + + this.DrawBackground(); + this.DrawRose(); + this.DrawLabels(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Tooltips + */ + if (this.Get('chart.tooltips')) { + + /** + * Register this object for redrawing + */ + RGraph.Register(this); + + /** + * The onclick event + */ + var canvas_onclick_func = function (e) + { + var obj = e.target.__object__; + var canvas = e.target; + var context = canvas.getContext('2d'); + + e = RGraph.FixEventObject(e); + + RGraph.Redraw(); + + var segment = obj.getSegment(e); + + if (segment && obj.Get('chart.tooltips')) { + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = String(obj.Get('chart.tooltips')(segment[6])); + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') { + var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6])); + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) { + var text = String(obj.Get('chart.tooltips')[segment[6]]); + } else { + var text = null; + } + + if (text) { + context.beginPath(); + context.strokeStyle = obj.Get('chart.highlight.stroke'); + context.fillStyle = obj.Get('chart.highlight.fill'); + + // This highlights the chart + context.arc(obj.centerx, obj.centery, segment[3], segment[4] / 57.3, segment[5] / 57.3, false); + context.arc(obj.centerx, obj.centery, segment[2] + 0.01, segment[5] / 57.3, segment[4] / 57.3, true); + + context.closePath(); + + context.fill(); + context.stroke(); + + context.strokeStyle = 'rgba(0,0,0,0)'; + + // Taken out on 12th June 2011 + //obj.DrawLabels(); + + /** + * Show the tooltip + */ + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[6]); + + e.stopPropagation(); + } + + return; + } + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); + + + /** + * The onmousemove event + */ + var canvas_onmousemove_func = function (e) + { + + var obj = e.target.__object__; + var canvas = e.target; + var context = canvas.getContext('2d'); + + e = RGraph.FixEventObject(e); + + var segment = obj.getSegment(e); + + if (segment && obj.Get('chart.tooltips')) { + + /** + * Get the tooltip text + */ + if (typeof(obj.Get('chart.tooltips')) == 'function') { + var text = String(obj.Get('chart.tooltips')(segment[6])); + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') { + var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6])); + } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) { + var text = String(obj.Get('chart.tooltips')[segment[6]]); + } else { + var text = null; + } + + if (text) { + canvas.style.cursor = 'pointer'; + + /******************************************************* + * This is here in case tooltips are using the + * onmousemove event + *******************************************************/ + if (obj.Get('chart.tooltips.event') == 'onmousemove') { + if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__index__ != segment[6]) { + canvas_onclick_func(e); + } + } + + } else { + canvas.style.cursor = 'default'; + } + + return; + } + + canvas.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * This function enables adjusting + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + /** + * This method draws the rose charts background + */ + RGraph.Rose.prototype.DrawBackground = function () + { + this.context.lineWidth = 1; + + // Draw the background grey circles + this.context.beginPath(); + this.context.strokeStyle = '#ccc'; + for (var i=15; i<this.radius - (RGraph.isIE8() ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work + + //this.context.moveTo(this.centerx + i, this.centery); + + // Radius must be greater than 0 for Opera to work + this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0); + } + this.context.stroke(); + + // Draw the background lines that go from the center outwards + this.context.beginPath(); + for (var i=15; i<360; i+=15) { + + // Radius must be greater than 0 for Opera to work + this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.1) / 57.3, 0); // The 0.1 avoids a bug in Chrome 6 + + this.context.lineTo(this.centerx, this.centery); + } + this.context.stroke(); + + this.context.beginPath(); + this.context.strokeStyle = 'black'; + + // Draw the X axis + this.context.moveTo(this.centerx - this.radius, this.centery); + this.context.lineTo(this.centerx + this.radius, this.centery); + + // Draw the X ends + this.context.moveTo(this.centerx - this.radius, this.centery - 5); + this.context.lineTo(this.centerx - this.radius, this.centery + 5); + this.context.moveTo(this.centerx + this.radius, this.centery - 5); + this.context.lineTo(this.centerx + this.radius, this.centery + 5); + + // Draw the X check marks + for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) { + this.context.moveTo(i, this.centery - 3); + this.context.lineTo(i, this.centery + 3); + } + + // Draw the Y check marks + for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) { + this.context.moveTo(this.centerx - 3, i); + this.context.lineTo(this.centerx + 3, i); + } + + // Draw the Y axis + this.context.moveTo(this.centerx, this.centery - this.radius); + this.context.lineTo(this.centerx, this.centery + this.radius); + + // Draw the Y ends + this.context.moveTo(this.centerx - 5, this.centery - this.radius); + this.context.lineTo(this.centerx + 5, this.centery - this.radius); + + this.context.moveTo(this.centerx - 5, this.centery + this.radius); + this.context.lineTo(this.centerx + 5, this.centery + this.radius); + + // Stroke it + this.context.closePath(); + this.context.stroke(); + } + + + /** + * This method draws the data on the graph + */ + RGraph.Rose.prototype.DrawRose = function () + { + var max = 0; + var data = this.data; + var margin = RGraph.degrees2Radians(this.Get('chart.margin')); + + // Must be at least two data points + //if (data.length < 2) { + // alert('[ROSE] Must be at least two data points! [' + data + ']'); + // return; + //} + + // Work out the maximum value and the sum + if (!this.Get('chart.ymax')) { + // Work out the max + for (var i=0; i<data.length; ++i) { + if (typeof(data[i]) == 'number') { + max = Math.max(max, data[i]); + } else if (typeof(data[i]) == 'object' && this.Get('chart.variant') == 'non-equi-angular') { + max = Math.max(max, data[i][0]); + + // Fallback is stacked + } else { + max = Math.max(max, RGraph.array_sum(data[i])); + } + } + + this.scale = RGraph.getScale(max, this); + this.max = this.scale[4]; + } else { + var ymax = this.Get('chart.ymax'); + var ymin = this.Get('chart.ymin'); + + this.scale = [ + ((ymax - ymin) * 0.2) + ymin, + ((ymax - ymin) * 0.4) + ymin, + ((ymax - ymin) * 0.6) + ymin, + ((ymax - ymin) * 0.8) + ymin, + ((ymax - ymin) * 1.0) + ymin + ]; + this.max = this.scale[4]; + } + + this.sum = RGraph.array_sum(data); + + // Move to the centre + this.context.moveTo(this.centerx, this.centery); + + this.context.stroke(); // Stroke the background so it stays grey + + // Transparency + if (this.Get('chart.colors.alpha')) { + this.context.globalAlpha = this.Get('chart.colors.alpha'); + } + + /******************************************************* + * A non-equi-angular Rose chart + *******************************************************/ + if (typeof(this.Get('chart.variant')) == 'string' && this.Get('chart.variant') == 'non-equi-angular') { + /******************************************************* + * NON-EQUI-ANGULAR GOES HERE + *******************************************************/ + var total=0; + for (var i=0; i<data.length; ++i) { + total += data[i][1]; + } + + + for (var i=0; i<this.data.length; ++i) { + + var segmentRadians = (this.data[i][1] / total) * (2 * Math.PI); + var radius = ((this.data[i][0] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); + + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = this.Get('chart.colors')[i]; + } + + this.context.beginPath(); // Begin the segment + this.context.arc(this.centerx, + this.centery, + radius, + this.startRadians - (Math.PI / 2) + margin, + this.startRadians + segmentRadians - (Math.PI / 2) - margin, + 0); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); // End the segment + + this.context.stroke(); + this.context.fill(); + + // Store the start and end angles + this.angles.push([ + ((this.startRadians - (Math.PI / 2) + margin) * 57.3) + 90, + (((this.startRadians + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90, + 0, + radius + ]); + + this.startRadians += segmentRadians; + } + } else { + /******************************************************* + * Draw regular segments here + *******************************************************/ + for (var i=0; i<this.data.length; ++i) { + + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + + /******************************************************* + * This allows sequential colors + *******************************************************/ + if (this.Get('chart.colors.sequential')) { + this.context.fillStyle = this.Get('chart.colors')[i]; + } + + var segmentRadians = (1 / this.data.length) * (2 * Math.PI); + + if (typeof(this.data[i]) == 'number') { + this.context.beginPath(); // Begin the segment + + var radius = ((this.data[i] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); + + this.context.arc(this.centerx, + this.centery, + radius * this.Get('chart.animation.grow.factor'), + (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin, + (this.startRadians * this.Get('chart.animation.grow.factor')) + (segmentRadians) - (Math.PI / 2) - margin, + 0); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); // End the segment + this.context.stroke(); + this.context.fill(); + + // Store the start and end angles + this.angles.push([ + (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90, + ((((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90, + 0, + radius * this.Get('chart.animation.grow.factor') + ]); + /******************************************************* + * Draw a stacked segment + *******************************************************/ + } else if (typeof(this.data[i]) == 'object') { + + var margin = this.Get('chart.margin') / (180 / Math.PI); + + for (var j=0; j<this.data[i].length; ++j) { + + this.context.fillStyle = this.Get('chart.colors')[j]; + if (j == 0) { + this.context.beginPath(); // Begin the segment + var startRadius = 0; + var endRadius = ((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); + + this.context.arc(this.centerx, + this.centery, + endRadius * this.Get('chart.animation.grow.factor'), + (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin, + (this.startRadians * this.Get('chart.animation.grow.factor'))+ segmentRadians - (Math.PI / 2) - margin, + 0); + this.context.lineTo(this.centerx, this.centery); + this.context.closePath(); // End the segment + this.context.stroke(); + this.context.fill(); + + this.angles.push([ + (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90 , + ((((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90, + 0, + endRadius * this.Get('chart.animation.grow.factor') + ]); + + } else { + + this.context.beginPath(); // Begin the segment + var startRadius = endRadius; // This comes from the prior iteration of this loop + var endRadius = (((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10)) + startRadius; + + this.context.arc(this.centerx, + this.centery, + startRadius * this.Get('chart.animation.grow.factor'), + (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin, + (this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin, + 0); + + this.context.arc(this.centerx, + this.centery, + endRadius * this.Get('chart.animation.grow.factor'), + (this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin, + (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin, + true); + + this.context.closePath(); // End the segment + this.context.stroke(); + this.context.fill(); + + this.angles.push([ + (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90, + (((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin) * 57.3) + 90, + startRadius * this.Get('chart.animation.grow.factor'), + endRadius * this.Get('chart.animation.grow.factor') + ]); + } + } + } + + this.startRadians += segmentRadians; + } + } + + // Turn off the transparency + if (this.Get('chart.colors.alpha')) { + this.context.globalAlpha = 1; + } + + // Draw the title if any has been set + if (this.Get('chart.title')) { + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + (this.canvas.height / 2) - this.radius, + this.centerx, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + } + } + + + /** + * Unsuprisingly, draws the labels + */ + RGraph.Rose.prototype.DrawLabels = function () + { + this.context.lineWidth = 1; + var key = this.Get('chart.key'); + + if (key && key.length) { + RGraph.DrawKey(this, key, this.Get('chart.colors')); + } + + // Set the color to black + this.context.fillStyle = 'black'; + this.context.strokeStyle = 'black'; + + var r = this.radius - 10; + var font_face = this.Get('chart.text.font'); + var font_size = this.Get('chart.text.size'); + var context = this.context; + var axes = this.Get('chart.labels.axes').toLowerCase(); + + // Draw any labels + + if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) { + this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r + 10); + } + + + var color = 'rgba(255,255,255,0.8)'; + + // The "North" axis labels + if (axes.indexOf('n') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + } + + // The "South" axis labels + if (axes.indexOf('s') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + } + + // The "East" axis labels + if (axes.indexOf('e') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + } + + // The "West" axis labels + if (axes.indexOf('w') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); + } + + RGraph.Text(context, font_face, font_size, this.centerx, this.centery, typeof(this.Get('chart.ymin')) == 'number' ? String(Number(this.Get('chart.ymin')).toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color); + } + + + /** + * Draws the circular labels that go around the charts + * + * @param labels array The labels that go around the chart + */ + RGraph.Rose.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r) + { + var variant = this.Get('chart.variant'); + var position = this.Get('chart.labels.position'); + var r = r + 10 + this.Get('chart.labels.offset'); + + for (var i=0; i<labels.length; ++i) { + + if (typeof(variant) == 'string' && variant == 'non-equi-angular') { + + var a = Number(this.angles[i][0]) + ((this.angles[i][1] - this.angles[i][0]) / 2); + a -= 90; + var halign = 'center'; // Default halign + + var x = Math.cos(a / 57.29577866666) * (r + 10); + var y = Math.sin(a / 57.29577866666) * (r + 10); + + RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign); + + } else { + + var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2)); + var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0); + var halign = 'center'; // Default halign + + // Horizontal alignment + //if (a == 0) { + // var halign = 'left'; + //} else if (a == 180) { + // var halign = 'right'; + //} + + var x = Math.cos(a / 57.29577866666) * (r + 10); + var y = Math.sin(a / 57.29577866666) * (r + 10); + + RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign); + } + } + } + + + /** + * This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object + * and it will pass you back the corresponding segment details as an array: + * + * [x, y, r, startAngle, endAngle] + * + * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas). + * + * @param object e Your event object + */ + RGraph.Rose.prototype.getSegment = function (e) + { + RGraph.FixEventObject(e); + + // The optional arg provides a way of allowing some accuracy (pixels) + var accuracy = arguments[1] ? arguments[1] : 0; + + var obj = e.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var x = mouseCoords[0] - obj.centerx; + var y = mouseCoords[1] - obj.centery; + var r = obj.radius; + var theta = Math.atan(y / x); // RADIANS + var hyp = y / Math.sin(theta); + var angles = obj.angles; + var ret = []; + var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; + + + + // Put theta in DEGREES + theta *= 57.3 + + // hyp should not be greater than radius if it's a Rose chart + if (obj.type == 'rose') { + if ( (isNaN(hyp) && Math.abs(mouseCoords[0]) < (obj.centerx - r) ) + || (isNaN(hyp) && Math.abs(mouseCoords[0]) > (obj.centerx + r)) + || (!isNaN(hyp) && Math.abs(hyp) > r)) { + return; + } + } + + /** + * Account for the correct quadrant + */ + if (x < 0 && y >= 0) { + theta += 180; + } else if (x < 0 && y < 0) { + theta += 180; + } else if (x > 0 && y < 0) { + theta += 360; + } + + /** + * Account for the rose chart angle displacement + */ + theta += 90; + + if (theta > 360) { + theta -= 360; + } + + hyp = Math.abs(hyp); + + for (var i=0; i<angles.length; ++i) { + if (theta >= angles[i][0] && theta < angles[i][1] && hyp > angles[i][2] && hyp < angles[i][3]) { + + if (!(hyp > angles[i][2] && hyp < angles[i][3])) { + return null; + } + + if (!hyp) { + return null; + } + + ret[0] = obj.centerx; + ret[1] = obj.centery; + ret[2] = angles[i][2]; // Start angle + ret[3] = angles[i][3]; // End angle + + ret[4] = angles[i][0]; // Start radius + ret[5] = angles[i][1]; // End radius + ret[6] = i; + + ret[4] -= 90; + ret[5] -= 90; + + if (x > 0 && y < 0) { + ret[4] += 360; + ret[5] += 360; + } + + if (ret[4] < 0) ret[4] += 360; + if (ret[5] > 360) ret[5] -= 360; + + return ret; + } + } + + return null; + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.rscatter.js b/schall/static/RGraph/libraries/RGraph.rscatter.js new file mode 100644 index 0000000..cc7949d --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.rscatter.js @@ -0,0 +1,653 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The chart constuctor + * + * @param object canvas + * @param array data + */ + RGraph.Rscatter = function (id, data) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.data = data; + this.canvas.__object__ = this; + this.type = 'rscatter'; + this.hasTooltips = false; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + this.centerx = 0; + this.centery = 0; + this.radius = 0; + this.max = 0; + + this.properties = { + 'chart.radius': null, + 'chart.colors': [], // This is used internally for the key + 'chart.colors.default': 'black', + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.labels': null, + 'chart.labels.position': 'center', + 'chart.labels.axes': 'nsew', + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.text.size': 10, + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.contextmenu': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.tooltips.hotspot': 3, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.resizable': false, + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.ymax': null, + 'chart.ymin': 0, + 'chart.tickmarks': 'cross', + 'chart.ticksize': 3, + 'chart.scale.decimals': null, + 'chart.scale.round': false + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getPoint; + } + + + /** + * A simple setter + * + * @param string name The name of the property to set + * @param string value The value of the property + */ + RGraph.Rscatter.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A simple getter + * + * @param string name The name of the property to get + */ + RGraph.Rscatter.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * This method draws the rose chart + */ + RGraph.Rscatter.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + /** + * This doesn't affect the chart, but is used for compatibility + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Calculate the radius + this.radius = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2); + this.centerx = RGraph.GetWidth(this) / 2; + this.centery = RGraph.GetHeight(this) / 2; + this.coords = []; + + /** + * If there's a user specified radius, use that + */ + if (typeof(this.Get('chart.radius')) == 'number') { + this.radius = this.Get('chart.radius'); + } + + /** + * Work out the scale + */ + var max = this.Get('chart.ymax'); + var min = this.Get('chart.ymin'); + + if (typeof(max) == 'number') { + this.max = max; + this.scale = [((max - min) * 0.2) + min,((max - min) * 0.4) + min,((max - min) * 0.6) + min,((max - min) * 0.8) + min,((max - min) * 1.0) + min,]; + + } else { + for (var i=0; i<this.data.length; ++i) { + this.max = Math.max(this.max, this.data[i][1]); + } + this.scale = RGraph.getScale(this.max, this); + this.max = this.scale[4]; + + // Hmmmmmmmm + if (String(this.scale[0]).indexOf('e') == -1) { + + var decimals = this.Get('chart.scale.decimals'); + + this.scale[0] = Number(this.scale[0]).toFixed(decimals); + this.scale[1] = Number(this.scale[1]).toFixed(decimals); + this.scale[2] = Number(this.scale[2]).toFixed(decimals); + this.scale[3] = Number(this.scale[3]).toFixed(decimals); + this.scale[4] = Number(this.scale[4]).toFixed(decimals); + } + } + + /** + * Change the centerx marginally if the key is defined + */ + if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) { + this.centerx = this.centerx - this.Get('chart.gutter.right') + 5; + } + + /** + * Populate the colors array for the purposes of generating the key + */ + if (typeof(this.Get('chart.key')) == 'object' && RGraph.is_array(this.Get('chart.key')) && this.Get('chart.key')[0]) { + for (var i=0; i<this.data.length; ++i) { + if (this.data[i][2] && typeof(this.data[i][2]) == 'string') { + this.Get('chart.colors').push(this.data[i][2]); + } + } + } + + this.DrawBackground(); + this.DrawRscatter(); + this.DrawLabels(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Tooltips + */ + if (this.hasTooltips) { + + /** + * Register this object for redrawing + */ + RGraph.Register(this); + + /** + * The onmousemove event + */ + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var obj = event.target.__object__; + var canvas = obj.canvas; + var context = obj.context; + var overHotspot = false; + var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends + var point = obj.getPoint(e); + + if (point) { + + overHotspot = true; + canvas.style.cursor = 'pointer'; + var tooltip = obj.data[point[3]][3]; + + if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__text__ != tooltip) { + + if (obj.Get('chart.tooltips.highlight')) { + RGraph.Redraw(); + } + + /** + * Get the tooltip text + */ + if (typeof(tooltip) == 'function') { + var text = String(tooltip(i)); + + } else { + var text = String(tooltip); + } + + RGraph.Tooltip(canvas, text, e.pageX + 5, e.pageY - 5, point[3]); + + /** + * Highlight the tickmark + */ + if (obj.Get('chart.tooltips.highlight')) { + context.beginPath(); + context.fillStyle = 'rgba(255,255,255,0.5)'; + context.arc(point[1], point[2], 3, 0, 6.2830, 0); + context.fill(); + } + } + } + + if (!overHotspot) { + canvas.style.cursor = 'default'; + } + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + // Draw the title if any has been set + if (this.Get('chart.title')) { + RGraph.DrawTitle(this.canvas, + this.Get('chart.title'), + (this.canvas.height / 2) - this.radius - 5, + this.centerx, + this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + /** + * This method draws the rose charts background + */ + RGraph.Rscatter.prototype.DrawBackground = function () + { + this.context.lineWidth = 1; + + // Draw the background grey circles + this.context.strokeStyle = '#ccc'; + for (var i=15; i<this.radius - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work + //this.context.moveTo(this.centerx + i, this.centery); + + // Radius must be greater than 0 for Opera to work + this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0); + } + this.context.stroke(); + + // Draw the background lines that go from the center outwards + this.context.beginPath(); + for (var i=15; i<360; i+=15) { + + // Radius must be greater than 0 for Opera to work + this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.01) / 57.3, 0); + + this.context.lineTo(this.centerx, this.centery); + } + this.context.stroke(); + + this.context.beginPath(); + this.context.strokeStyle = 'black'; + + // Draw the X axis + this.context.moveTo(this.centerx - this.radius, this.centery); + this.context.lineTo(this.centerx + this.radius, this.centery); + + // Draw the X ends + this.context.moveTo(this.centerx - this.radius, this.centery - 5); + this.context.lineTo(this.centerx - this.radius, this.centery + 5); + this.context.moveTo(this.centerx + this.radius, this.centery - 5); + this.context.lineTo(this.centerx + this.radius, this.centery + 5); + + // Draw the X check marks + for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) { + this.context.moveTo(i, this.centery - 3); + this.context.lineTo(i, this.centery + 3); + } + + // Draw the Y check marks + for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) { + this.context.moveTo(this.centerx - 3, i); + this.context.lineTo(this.centerx + 3, i); + } + + // Draw the Y axis + this.context.moveTo(this.centerx, this.centery - this.radius); + this.context.lineTo(this.centerx, this.centery + this.radius); + + // Draw the Y ends + this.context.moveTo(this.centerx - 5, this.centery - this.radius); + this.context.lineTo(this.centerx + 5, this.centery - this.radius); + + this.context.moveTo(this.centerx - 5, this.centery + this.radius); + this.context.lineTo(this.centerx + 5, this.centery + this.radius); + + // Stroke it + this.context.closePath(); + this.context.stroke(); + } + + + /** + * This method draws a set of data on the graph + */ + RGraph.Rscatter.prototype.DrawRscatter = function () + { + var data = this.data; + + for (var i=0; i<data.length; ++i) { + + var d1 = data[i][0]; + var d2 = data[i][1]; + var a = d1 / (180 / Math.PI); // RADIANS + var r = ( (d2 - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin')) ) * this.radius; + var x = Math.sin(a) * r; + var y = Math.cos(a) * r; + var color = data[i][2] ? data[i][2] : this.Get('chart.colors.default'); + var tooltip = data[i][3] ? data[i][3] : null; + + if (tooltip && tooltip.length) { + this.hasTooltips = true; + } + + /** + * Account for the correct quadrant + */ + x = x + this.centerx; + y = this.centery - y; + + + this.DrawTick(x, y, color); + + // Populate the coords array with the coordinates and the tooltip + this.coords.push([x, y, color, tooltip]); + } + } + + + /** + * Unsuprisingly, draws the labels + */ + RGraph.Rscatter.prototype.DrawLabels = function () + { + this.context.lineWidth = 1; + var key = this.Get('chart.key'); + + // Set the color to black + this.context.fillStyle = 'black'; + this.context.strokeStyle = 'black'; + + var r = this.radius; + var color = this.Get('chart.text.color'); + var font_face = this.Get('chart.text.font'); + var font_size = this.Get('chart.text.size'); + var context = this.context; + var axes = this.Get('chart.labels.axes').toLowerCase(); + + this.context.fillStyle = this.Get('chart.text.color'); + + // Draw any labels + if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) { + this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r); + } + + + var color = 'rgba(255,255,255,0.8)'; + + // The "North" axis labels + if (axes.indexOf('n') > -1) { + RGraph.Text(context,font_face,font_size,this.centerx,this.centery - ((r) * 0.2),String(this.scale[0]),'center','center',true,false,color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "South" axis labels + if (axes.indexOf('s') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.2), String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "East" axis labels + if (axes.indexOf('e') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // The "West" axis labels + if (axes.indexOf('w') > -1) { + RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color); + RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color); + } + + // Draw the center minimum value (but only if there's at least one axes labels stipulated) + if (this.Get('chart.labels.axes').length > 0) { + RGraph.Text(context, font_face, font_size, this.centerx, this.centery, this.Get('chart.ymin') > 0 ? String(this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color); + } + + /** + * Draw the key + */ + if (key && key.length) { + RGraph.DrawKey(this, key, this.Get('chart.colors')); + } + } + + + /** + * Draws the circular labels that go around the charts + * + * @param labels array The labels that go around the chart + */ + RGraph.Rscatter.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r) + { + var position = this.Get('chart.labels.position'); + var r = r + 10; + + for (var i=0; i<labels.length; ++i) { + + + var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2)); + var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0); + + var x = Math.cos(a / 57.29577866666) * (r + 10); + var y = Math.sin(a / 57.29577866666) * (r + 10); + + RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center'); + } + } + + + /** + * Draws a single tickmark + */ + RGraph.Rscatter.prototype.DrawTick = function (x, y, color) + { + var tickmarks = this.Get('chart.tickmarks'); + var ticksize = this.Get('chart.ticksize'); + + this.context.strokeStyle = color; + this.context.fillStyle = color; + + // Cross + if (tickmarks == 'cross') { + + this.context.beginPath(); + this.context.moveTo(x + ticksize, y + ticksize); + this.context.lineTo(x - ticksize, y - ticksize); + this.context.stroke(); + + this.context.beginPath(); + this.context.moveTo(x - ticksize, y + ticksize); + this.context.lineTo(x + ticksize, y - ticksize); + this.context.stroke(); + + // Circle + } else if (tickmarks == 'circle') { + + this.context.beginPath(); + this.context.arc(x, y, ticksize, 0, 6.2830, false); + this.context.fill(); + + // Square + } else if (tickmarks == 'square') { + + this.context.beginPath(); + this.context.fillRect(x - ticksize, y - ticksize, 2 * ticksize, 2 * ticksize); + this.context.fill(); + + // Diamond shape tickmarks + } else if (tickmarks == 'diamond') { + + this.context.beginPath(); + this.context.moveTo(x, y - ticksize); + this.context.lineTo(x + ticksize, y); + this.context.lineTo(x, y + ticksize); + this.context.lineTo(x - ticksize, y); + this.context.closePath(); + this.context.fill(); + + // Plus style tickmarks + } else if (tickmarks == 'plus') { + + this.context.lineWidth = 1; + + this.context.beginPath(); + this.context.moveTo(x, y - ticksize); + this.context.lineTo(x, y + ticksize); + this.context.moveTo(x - ticksize, y); + this.context.lineTo(x + ticksize, y); + this.context.stroke(); + } + } + + + /** + * This function makes it much easier to get the (if any) point that is currently being hovered over. + * + * @param object e The event object + */ + RGraph.Rscatter.prototype.getPoint = function (e) + { + var canvas = e.target; + var obj = canvas.__object__; + var context = obj.context; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var overHotspot = false; + var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends + + for (var i=0; i<obj.coords.length; ++i) { + + var x = obj.coords[i][0]; + var y = obj.coords[i][1]; + var tooltip = obj.coords[i][3]; + + if ( + mouseX < (x + offset) && + mouseX > (x - offset) && + mouseY < (y + offset) && + mouseY > (y - offset) && + typeof(tooltip) == 'string' && + tooltip.length > 0 + ) { + + return [obj, x, y, i]; + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.scatter.js b/schall/static/RGraph/libraries/RGraph.scatter.js new file mode 100644 index 0000000..3fde9f3 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.scatter.js @@ -0,0 +1,1662 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The scatter graph constructor + * + * @param object canvas The cxanvas object + * @param array data The chart data + */ + RGraph.Scatter = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.canvas.__object__ = this; + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.max = 0; + this.coords = []; + this.data = []; + this.type = 'scatter'; + this.isRGraph = true; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config properties + this.properties = { + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': true, + 'chart.background.grid.width': 1, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.hbars': null, + 'chart.background.vbars': null, + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':true, + 'chart.background.grid.autofit.numhlines': 5, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.hotspot': 3, + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.numyticks': 10, + 'chart.tickmarks': 'cross', + 'chart.ticksize': 5, + 'chart.xticks': true, + 'chart.xaxis': true, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.xmin': 0, + 'chart.xmax': 0, + 'chart.ymax': null, + 'chart.ymin': null, + 'chart.scale.decimals': null, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.labels': [], + 'chart.labels.ingraph': null, + 'chart.labels.above': false, + 'chart.labels.above.size': 8, + 'chart.labels.above.decimals': 0, + 'chart.ylabels': true, + 'chart.ylabels.count': 5, + 'chart.ylabels.invert': false, + 'chart.ylabels.specific': null, + 'chart.ylabels.inside': false, + 'chart.contextmenu': null, + 'chart.defaultcolor': 'black', + 'chart.xaxispos': 'bottom', + 'chart.yaxispos': 'left', + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.linewidth': 1, + 'chart.crosshairs.coords': false, + 'chart.crosshairs.coords.fixed':true, + 'chart.crosshairs.coords.fadeout':false, + 'chart.crosshairs.coords.labels.x': 'X', + 'chart.crosshairs.coords.labels.y': 'Y', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.line': false, + 'chart.line.linewidth': 1, + 'chart.line.colors': ['green', 'red'], + 'chart.line.shadow.color': 'rgba(0,0,0,0)', + 'chart.line.shadow.blur': 2, + 'chart.line.shadow.offsetx': 3, + 'chart.line.shadow.offsety': 3, + 'chart.line.stepped': false, + 'chart.noaxes': false, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.axis.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.boxplot.width': 8, + 'chart.boxplot.capped': true, + 'chart.resizable': false, + 'chart.resize.handle.background': null, + 'chart.xmin': 0, + 'chart.labels.specific.align': 'left', + 'chart.xscale': false, + 'chart.xscale.units.pre': '', + 'chart.xscale.units.post': '', + 'chart.xscale.numlabels': 10, + 'chart.xscale.formatter': null, + 'chart.noendxtick': false, + 'chart.noendytick': true + } + + // Handle multiple datasets being given as one argument + if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0][0]) == 'number') { + // Store the data set(s) + for (var i=0; i<arguments[1].length; ++i) { + this.data[i] = arguments[1][i]; + } + + // Handle multiple data sets being supplied as seperate arguments + } else { + // Store the data set(s) + for (var i=1; i<arguments.length; ++i) { + this.data[i - 1] = arguments[i]; + } + } + + // Check for support + if (!this.canvas) { + alert('[SCATTER] No canvas support'); + return; + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getPoint; + } + + + /** + * A simple setter + * + * @param string name The name of the property to set + * @param string value The value of the property + */ + RGraph.Scatter.prototype.Set = function (name, value) + { + /** + * This is here because the key expects a name of "chart.colors" + */ + if (name == 'chart.line.colors') { + this.properties['chart.colors'] = value; + } + + /** + * Allow compatibility with older property names + */ + if (name == 'chart.tooltip.hotspot') { + name = 'chart.tooltips.hotspot'; + } + + /** + * chart.yaxispos should be left or right + */ + if (name == 'chart.yaxispos' && value != 'left' && value != 'right') { + alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left"); + value = 'left'; + } + + /** + * Check for xaxispos + */ + if (name == 'chart.xaxispos' ) { + if (value != 'bottom' && value != 'center') { + alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center'); + value = 'center'; + } + } + + this.properties[name.toLowerCase()] = value; + } + + + /** + * A simple getter + * + * @param string name The name of the property to set + */ + RGraph.Scatter.prototype.Get = function (name) + { + return this.properties[name]; + } + + + /** + * The function you call to draw the line chart + */ + RGraph.Scatter.prototype.Draw = function () + { + // MUST be the first thing done! + if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) { + RGraph.DrawBackgroundImage(this); + return; + } + + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Go through all the data points and see if a tooltip has been given + this.Set('chart.tooltips', false); + this.hasTooltips = false; + var overHotspot = false; + + // Reset the coords array + this.coords = []; + + if (!RGraph.isIE8()) { + for (var i=0; i<this.data.length; ++i) { + for (var j =0;j<this.data[i].length; ++j) { + if (this.data[i][j] && this.data[i][j][3] && typeof(this.data[i][j][3]) == 'string' && this.data[i][j][3].length) { + this.Set('chart.tooltips', [1]); // An array + this.hasTooltips = true; + } + } + } + } + + // Reset the maximum value + this.max = 0; + + // Work out the maximum Y value + if (this.Get('chart.ymax') && this.Get('chart.ymax') > 0) { + + this.scale = []; + this.max = this.Get('chart.ymax'); + this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + + this.scale[0] = ((this.max - this.min) * (1/5)) + this.min; + this.scale[1] = ((this.max - this.min) * (2/5)) + this.min; + this.scale[2] = ((this.max - this.min) * (3/5)) + this.min; + this.scale[3] = ((this.max - this.min) * (4/5)) + this.min; + this.scale[4] = ((this.max - this.min) * (5/5)) + this.min; + + var decimals = this.Get('chart.scale.decimals'); + + this.scale = [ + Number(this.scale[0]).toFixed(decimals), + Number(this.scale[1]).toFixed(decimals), + Number(this.scale[2]).toFixed(decimals), + Number(this.scale[3]).toFixed(decimals), + Number(this.scale[4]).toFixed(decimals) + ]; + + } else { + + var i = 0; + var j = 0; + + for (i=0; i<this.data.length; ++i) { + for (j=0; j<this.data[i].length; ++j) { + this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RGraph.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1])); + } + } + + this.scale = RGraph.getScale(this.max, this); + + this.max = this.scale[4]; + this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + + if (this.min) { + this.scale[0] = ((this.max - this.min) * (1/5)) + this.min; + this.scale[1] = ((this.max - this.min) * (2/5)) + this.min; + this.scale[2] = ((this.max - this.min) * (3/5)) + this.min; + this.scale[3] = ((this.max - this.min) * (4/5)) + this.min; + this.scale[4] = ((this.max - this.min) * (5/5)) + this.min; + } + + + if (typeof(this.Get('chart.scale.decimals')) == 'number') { + var decimals = this.Get('chart.scale.decimals'); + + this.scale = [ + Number(this.scale[0]).toFixed(decimals), + Number(this.scale[1]).toFixed(decimals), + Number(this.scale[2]).toFixed(decimals), + Number(this.scale[3]).toFixed(decimals), + Number(this.scale[4]).toFixed(decimals) + ]; + } + } + + this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + + // Progressively Draw the chart + RGraph.background.Draw(this); + + /** + * Draw any horizontal bars that have been specified + */ + if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length) { + RGraph.DrawBars(this); + } + + /** + * Draw any vertical bars that have been specified + */ + if (this.Get('chart.background.vbars') && this.Get('chart.background.vbars').length) { + this.DrawVBars(); + } + + if (!this.Get('chart.noaxes')) { + this.DrawAxes(); + } + + this.DrawLabels(); + + i = 0; + for(i=0; i<this.data.length; ++i) { + this.DrawMarks(i); + + // Set the shadow + this.context.shadowColor = this.Get('chart.line.shadow.color'); + this.context.shadowOffsetX = this.Get('chart.line.shadow.offsetx'); + this.context.shadowOffsetY = this.Get('chart.line.shadow.offsety'); + this.context.shadowBlur = this.Get('chart.line.shadow.blur'); + + this.DrawLine(i); + + // Turn the shadow off + RGraph.NoShadow(this); + } + + + if (this.Get('chart.line')) { + for (var i=0;i<this.data.length; ++i) { + this.DrawMarks(i); // Call this again so the tickmarks appear over the line + } + } + + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Install the event handler for tooltips + */ + if (this.hasTooltips) { + + /** + * Register all charts + */ + RGraph.Register(this); + + var overHotspot = false; + + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = e.target; + var obj = canvas.__object__; + var context = obj.context; + var mouseCoords = RGraph.getMouseXY(e); + var point = obj.getPoint(e); + var overHotspot = false; + + if (point) { + + var __dataset__ = point[2]; + var __index__ = point[3]; + var __text__ = point[4]; + var overHotspot = true; + + + + if (point[4]) { + canvas.style.cursor = 'pointer'; + + if ( + !RGraph.Registry.Get('chart.tooltip') || + RGraph.Registry.Get('chart.tooltip').__text__ != __text__ || + RGraph.Registry.Get('chart.tooltip').__index__ != __index__ || + RGraph.Registry.Get('chart.tooltip').__dataset__ != __dataset__ + ) { + + if (obj.Get('chart.tooltips.highlight')) { + RGraph.Redraw(); + } + + /** + * Get the tooltip text + */ + if (typeof(__text__) == 'function') { + var text = String(__text__(i)); + + } else { + var text = String(__text__); + } + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, __index__); + + RGraph.Registry.Get('chart.tooltip').__index__ = __index__; + + if (RGraph.Registry.Get('chart.tooltip')) { + RGraph.Registry.Get('chart.tooltip').__dataset__ = __dataset__; + } + + + + /** + * Draw a circle around the mark. Also highlight the boxplot if necessary + */ + if (obj.Get('chart.tooltips.highlight') && typeof(point[0]) == 'object') { + context.beginPath(); + context.strokeStyle = 'black'; + context.fillStyle = 'rgba(255,255,255,0.5)'; + context.strokeRect(point[0][0], point[1][0], point[0][1], point[1][1]); + context.fillRect(point[0][0], point[1][0], point[0][1], point[1][1]); + context.stroke(); + context.fill(); + + } else if (obj.Get('chart.tooltips.highlight') && typeof(point[0]) == 'number') { + context.beginPath(); + context.fillStyle = 'rgba(255,255,255,0.5)'; + context.arc(point[0], point[1], 3, 0, 6.28, 0); + context.fill(); + } + } + } + } + + /** + * Reset the pointer + */ + if (!overHotspot || !point[4]) { + canvas.style.cursor = 'default'; + } + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + + /** + * Draw the key if necessary + */ + if (this.Get('chart.key') && this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.line.colors')); + } + + + /** + * Draw " above" labels if enabled + */ + if (this.Get('chart.labels.above')) { + this.DrawAboveLabels(); + } + + /** + * Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js + */ + this.DrawInGraphLabels(this); + + + /** + * Draw crosschairs + */ + RGraph.DrawCrosshairs(this); + + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the axes of the scatter graph + */ + RGraph.Scatter.prototype.DrawAxes = function () + { + var canvas = this.canvas; + var context = this.context; + var graphHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + + context.beginPath(); + context.strokeStyle = this.Get('chart.axis.color'); + context.lineWidth = 1; + + // Draw the Y axis + if (this.Get('chart.yaxispos') == 'left') { + context.moveTo(this.gutterLeft, this.gutterTop); + context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + } else { + context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop); + context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom); + } + + + // Draw the X axis + if (this.Get('chart.xaxis')) { + if (this.Get('chart.xaxispos') == 'center') { + context.moveTo(this.gutterLeft, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2)); + context.lineTo(this.canvas.width - this.gutterRight, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2)); + } else { + context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom); + } + } + + /** + * Draw the Y tickmarks + */ + var numyticks = this.Get('chart.numyticks'); + + for (y=this.gutterTop; y < this.canvas.height - this.gutterBottom + (this.Get('chart.xaxispos') == 'center' ? 1 : 0) ; y+=(graphHeight / numyticks)) { + + // This is here to accomodate the X axis being at the center + if (y == (this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2)) ) continue; + + if (this.Get('chart.yaxispos') == 'left') { + context.moveTo(this.gutterLeft, y); + context.lineTo(this.gutterLeft - 3, y); + } else { + context.moveTo(this.canvas.width - this.gutterRight +3, y); + context.lineTo(this.canvas.width - this.gutterRight, y); + } + + /** + * Draw an extra tick if the X axis isn't being shown + */ + if (this.Get('chart.xaxis') == false && this.Get('chart.noendytick') == false) { + this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + this.context.lineTo(this.gutterLeft - 3, this.canvas.height - this.gutterBottom); + } + } + + + /** + * Draw the X tickmarks + */ + if (this.Get('chart.xticks') && this.Get('chart.xaxis')) { + var x = 0; + var y = (this.Get('chart.xaxispos') == 'center') ? this.gutterTop + (this.grapharea / 2): (this.canvas.height - this.gutterBottom); + this.xTickGap = (this.Get('chart.labels') && this.Get('chart.labels').length) ? ((this.canvas.width - this.gutterLeft - this.gutterRight ) / this.Get('chart.labels').length) : (this.canvas.width - this.gutterLeft - this.gutterRight) / 10; + + for (x = (this.gutterLeft + (this.Get('chart.yaxispos') == 'left' ? this.xTickGap : 0) ); + x <= (this.canvas.width - this.gutterRight - (this.Get('chart.yaxispos') == 'left' ? 0 : 1)); + x += this.xTickGap) { + + if (this.Get('chart.yaxispos') == 'left' && this.Get('chart.noendxtick') == true && x == (this.canvas.width - this.gutterRight) ) { + continue; + } else if (this.Get('chart.yaxispos') == 'right' && this.Get('chart.noendxtick') == true && x == this.gutterLeft) { + continue; + } + + context.moveTo(x, y - (this.Get('chart.xaxispos') == 'center' ? 3 : 0)); + context.lineTo(x, y + 3); + } + } + + context.stroke(); + } + + + + + + + + + + + + /** + * Draws the labels on the scatter graph + */ + RGraph.Scatter.prototype.DrawLabels = function () + { + this.context.fillStyle = this.Get('chart.text.color'); + var font = this.Get('chart.text.font'); + var xMin = this.Get('chart.xmin'); + var xMax = this.Get('chart.xmax'); + var yMax = this.scale[4]; + var yMin = this.Get('chart.ymin'); + var text_size = this.Get('chart.text.size'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var numYLabels = this.Get('chart.ylabels.count'); + var invert = this.Get('chart.ylabels.invert'); + var inside = this.Get('chart.ylabels.inside'); + var context = this.context; + var canvas = this.canvas; + var boxed = false; + + this.halfTextHeight = text_size / 2; + + + this.halfGraphHeight = (this.canvas.height - this.gutterTop - this.gutterBottom) / 2; + + /** + * Draw the Y yaxis labels, be it at the top or center + */ + if (this.Get('chart.ylabels')) { + + var xPos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5; + var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right'; + + /** + * Now change the two things above if chart.ylabels.inside is specified + */ + if (inside) { + if (this.Get('chart.yaxispos') == 'left') { + xPos = this.Get('chart.gutter.left') + 5; + align = 'left'; + boxed = true; + } else { + xPos = this.canvas.width - this.Get('chart.gutter.right') - 5; + align = 'right'; + boxed = true; + } + } + + if (this.Get('chart.xaxispos') == 'center') { + + + /** + * Specific Y labels + */ + if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific') != null && this.Get('chart.ylabels.specific').length) { + + var labels = this.Get('chart.ylabels.specific'); + + if (this.Get('chart.ymin') > 0) { + labels = []; + for (var i=0; i<(this.Get('chart.ylabels.specific').length - 1); ++i) { + labels.push(this.Get('chart.ylabels.specific')[i]); + } + } + + for (var i=0; i<labels.length; ++i) { + var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) ); + RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align, boxed); + } + + var reversed_labels = RGraph.array_reverse(labels); + + for (var i=0; i<reversed_labels.length; ++i) { + var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) ); + RGraph.Text(context,font, text_size, xPos, y, reversed_labels[i], 'center', align, boxed); + } + + /** + * Draw the center label if chart.ymin is specified + */ + if (this.Get('chart.ymin') > 0) { + RGraph.Text(context, font, text_size, xPos, (this.grapharea / 2) + this.Get('chart.gutter.top'), this.Get('chart.ylabels.specific')[this.Get('chart.ylabels.specific').length - 1], 'center', align, boxed); + } + + return; + } + + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + // Draw the top halves labels + RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed); + + + if (numYLabels >= 5) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed); + } + + // Draw the bottom halves labels + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed); + } + + if (numYLabels == 5) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed); + } + + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (5/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed); + + } else if (numYLabels == 10) { + // 10 Y labels + var interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/20) ),RGraph.number_format(this,(this.max - (this.max * (i/10))).toFixed(this.Get('chart.scale.decimals')),units_pre, units_post),'center', align, boxed); + RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/20) ) + (this.grapharea / 2) + (this.grapharea / 20),'-' + RGraph.number_format(this, ((this.max * (i/10)) + (this.max * (1/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed); + } + + } else { + alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only'); + } + + } else { + + var xPos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : this.canvas.width - this.gutterRight + 5; + var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right'; + + if (inside) { + if (this.Get('chart.yaxispos') == 'left') { + xPos = this.Get('chart.gutter.left') + 5; + align = 'left'; + boxed = true; + } else { + xPos = this.canvas.width - this.Get('chart.gutter.right') - 5; + align = 'right'; + boxed = true; + } + } + /** + * Specific Y labels + */ + if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) { + + var labels = this.Get('chart.ylabels.specific'); + + // Lose the last label + if (this.Get('chart.ymin') > 0) { + labels = []; + for (var i=0; i<(this.Get('chart.ylabels.specific').length - 1); ++i) { + labels.push(this.Get('chart.ylabels.specific')[i]); + } + } + + for (var i=0; i<labels.length; ++i) { + var y = this.gutterTop + (i * (this.grapharea / labels.length) ); + + RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align, boxed); + } + + /** + * Draw the center label if chart.ymin is specified + */ + if (this.Get('chart.ymin') > 0) { + RGraph.Text(context, font, text_size, xPos, this.canvas.height - this.Get('chart.gutter.bottom'), this.Get('chart.ylabels.specific')[this.Get('chart.ylabels.specific').length - 1], 'center', align, boxed); + } + + return; + } + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + if (invert) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, 0, units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (5/5) ), RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed); + + if (numYLabels >= 5) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed); + } + } else { + RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed); + + if (numYLabels >= 5) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed); + } + + if (numYLabels >= 3) { + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed); + RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed); + } + } + } else if (numYLabels == 10) { + var interval = (this.grapharea / numYLabels) / 2; + if (invert) { + for (var i=numYLabels; i>=0; --i) { + RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * ((10-i)/10) ),RGraph.number_format(this,(this.max - (this.max * (i/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post),'center', align, boxed); + } + } else { + // 10 Y labels + for (var i=0; i<numYLabels; ++i) { + + RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/10) ),RGraph.number_format(this, (this.max - ((this.max - this.min) * (i/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post),'center', align, boxed); + } + } + } else { + alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only'); + } + + if (this.Get('chart.ymin')) { + RGraph.Text(context, font, text_size, xPos, this.canvas.height - this.gutterBottom,RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center', align, boxed); + } + } + } + + + + + /** + * Draw an X scale + */ + if (this.Get('chart.xscale')) { + + var numXLabels = this.Get('chart.xscale.numlabels'); + var interval = (this.canvas.width - this.gutterLeft - this.gutterRight) / numXLabels; + var y = this.canvas.height - this.gutterBottom + 5 + (text_size / 2); + var units_pre_x = this.Get('chart.xscale.units.pre'); + var units_post_x = this.Get('chart.xscale.units.post'); + + + if (!this.Get('chart.xmax')) { + var xmax = 0; + + for (var ds=0; ds<this.data.length; ++ds) { + for (var point=0; point<this.data[ds].length; ++point) { + xmax = Math.max(xmax, this.data[ds][point][0]); + } + } + + this.Set('chart.xmax', RGraph.getScale(xmax)[4]); + } + + + for (var i=0; i<numXLabels; ++i) { + + var num = ( (this.Get('chart.xmax') - this.Get('chart.xmin')) * ((i+1) / numXLabels)) + this.Get('chart.xmin'); + var x = this.gutterLeft + ((i+1) * interval); + + if (typeof(this.Get('chart.xscale.formatter')) == 'function') { + var text = this.Get('chart.xscale.formatter')(this, num); + } else { + var text = RGraph.number_format(this, + num.toFixed(this.Get('chart.scale.decimals')), + units_pre_x, + units_post_x); + } + + RGraph.Text(context, font, text_size, x, y, text, 'center', 'center'); + } + + /** + * Draw X labels + */ + } else { + // Put the text on the X axis + var graphArea = this.canvas.width - this.gutterLeft - this.gutterRight; + var xInterval = graphArea / this.Get('chart.labels').length; + var xPos = this.gutterLeft; + var yPos = (this.canvas.height - this.gutterBottom) + 15; + var labels = this.Get('chart.labels'); + + /** + * Text angle + */ + var angle = 0; + var valign = null; + var halign = 'center'; + + if (this.Get('chart.text.angle') > 0) { + angle = -1 * this.Get('chart.text.angle'); + valign = 'center'; + halign = 'right'; + yPos -= 10; + } + + for (i=0; i<labels.length; ++i) { + + if (typeof(labels[i]) == 'object') { + + if (this.Get('chart.labels.specific.align') == 'center') { + var rightEdge = 0; + + if (labels[i+1] && labels[i+1][1]) { + rightEdge = labels[i+1][1]; + } else { + rightEdge = this.Get('chart.xmax'); + } + + var offset = (rightEdge - labels[i][1]) / 2; + + } else { + var offset = 0; + } + + + RGraph.Text(context, + font, + this.Get('chart.text.size'), + this.gutterLeft + (graphArea * ((labels[i][1] - xMin + offset) / (this.Get('chart.xmax') - xMin))) + 5, + yPos, + String(labels[i][0]), + valign, + angle != 0 ? 'right' : (this.Get('chart.labels.specific.align') == 'center' ? 'center' : 'left'), + null, + angle + ); + + /** + * Draw the gray indicator line + */ + this.context.beginPath(); + this.context.strokeStyle = '#bbb'; + this.context.moveTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom + 20); + this.context.stroke(); + + } else { + RGraph.Text(context, font, this.Get('chart.text.size'), xPos + (this.xTickGap / 2), yPos, String(labels[i]), valign, halign, null, angle); + } + + // Do this for the next time around + xPos += xInterval; + } + + /** + * Draw the final indicator line + */ + if (typeof(labels[0]) == 'object') { + this.context.beginPath(); + this.context.strokeStyle = '#bbb'; + this.context.moveTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom + 20); + this.context.stroke(); + } + } + } + + + + + + + + + + + + + + + /** + * Draws the actual scatter graph marks + * + * @param i integer The dataset index + */ + RGraph.Scatter.prototype.DrawMarks = function (i) + { + /** + * Reset the coords array + */ + this.coords[i] = []; + + /** + * Plot the values + */ + var xmax = this.Get('chart.xmax'); + var default_color = this.Get('chart.defaultcolor'); + + for (var j=0; j<this.data[i].length; ++j) { + /** + * This is here because tooltips are optional + */ + var data_point = this.data[i]; + + var xCoord = data_point[j][0]; + var yCoord = data_point[j][1]; + var color = data_point[j][2] ? data_point[j][2] : default_color; + var tooltip = (data_point[j] && data_point[j][3]) ? data_point[j][3] : null; + + + this.DrawMark( + i, + xCoord, + yCoord, + xmax, + this.scale[4], + color, + tooltip, + this.coords[i], + data_point + ); + } + } + + + /** + * Draws a single scatter mark + */ + RGraph.Scatter.prototype.DrawMark = function (index, x, y, xMax, yMax, color, tooltip, coords, data) + { + /** + * Inverted Y scale handling + */ + if (this.Get('chart.ylabels.invert')) { + if (typeof(y) == 'number') { + y = yMax - y; + } + } + + var tickmarks = this.Get('chart.tickmarks'); + var tickSize = this.Get('chart.ticksize'); + var xMin = this.Get('chart.xmin'); + var x = ((x - xMin) / (xMax - xMin)) * (this.canvas.width - this.gutterLeft - this.gutterRight); + var originalX = x; + var originalY = y; + + + /** + * This allows chart.tickmarks to be an array + */ + + if (tickmarks && typeof(tickmarks) == 'object') { + tickmarks = tickmarks[index]; + } + + + /** + * This allows chart.ticksize to be an array + */ + if (typeof(tickSize) == 'object') { + var tickSize = tickSize[index]; + var halfTickSize = tickSize / 2; + } else { + var halfTickSize = tickSize / 2; + } + + + /** + * This bit is for boxplots only + */ + if ( typeof(y) == 'object' + && typeof(y[0]) == 'number' + && typeof(y[1]) == 'number' + && typeof(y[2]) == 'number' + && typeof(y[3]) == 'number' + && typeof(y[4]) == 'number' + ) { + + var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + this.Set('chart.boxplot', true); + this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom; + + if (this.Get('chart.xaxispos') == 'center') { + this.graphheight /= 2; + } + + var y0 = (this.graphheight) - ((y[4] - yMin) / (yMax - yMin)) * (this.graphheight); + var y1 = (this.graphheight) - ((y[3] - yMin) / (yMax - yMin)) * (this.graphheight); + var y2 = (this.graphheight) - ((y[2] - yMin) / (yMax - yMin)) * (this.graphheight); + var y3 = (this.graphheight) - ((y[1] - yMin) / (yMax - yMin)) * (this.graphheight); + var y4 = (this.graphheight) - ((y[0] - yMin) / (yMax - yMin)) * (this.graphheight); + + /** + * Inverted labels + */ + if (this.Get('chart.ylabels.invert')) { + y0 = this.graphheight - y0; + y1 = this.graphheight - y1; + y2 = this.graphheight - y2; + y3 = this.graphheight - y3; + y4 = this.graphheight - y4; + } + + var col1 = y[5]; + var col2 = y[6]; + + // Override the boxWidth + if (typeof(y[7]) == 'number') { + var boxWidth = y[7]; + } + + var y = this.graphheight - y2; + + } else { + var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0; + var y = (( (y - yMin) / (yMax - yMin)) * (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom)); + } + + /** + * Account for the X axis being at the centre + */ + if (this.Get('chart.xaxispos') == 'center') { + y /= 2; + y += this.halfGraphHeight; + } + + // This is so that points are on the graph, and not the gutter + x += this.gutterLeft; + y = this.canvas.height - this.gutterBottom - y; + + this.context.beginPath(); + + // Color + this.context.strokeStyle = color; + + /** + * Boxplots + */ + if ( this.Get('chart.boxplot') + && typeof(y0) == 'number' + && typeof(y1) == 'number' + && typeof(y2) == 'number' + && typeof(y3) == 'number' + && typeof(y4) == 'number' + ) { + + var boxWidth = boxWidth ? boxWidth : this.Get('chart.boxplot.width'); + var halfBoxWidth = boxWidth / 2; + + this.context.beginPath(); + + // Draw the upper coloured box if a value is specified + if (col1) { + this.context.fillStyle = col1; + this.context.fillRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y2 - y1); + } + + // Draw the lower coloured box if a value is specified + if (col2) { + this.context.fillStyle = col2; + this.context.fillRect(x - halfBoxWidth, y2 + this.gutterTop, boxWidth, y3 - y2); + } + + this.context.strokeRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y3 - y1); + this.context.stroke(); + + // Now draw the whiskers + this.context.beginPath(); + if (this.Get('chart.boxplot.capped')) { + this.context.moveTo(x - halfBoxWidth, y0 + this.gutterTop); + this.context.lineTo(x + halfBoxWidth, y0 + this.gutterTop); + } + + this.context.moveTo(x, y0 + this.gutterTop); + this.context.lineTo(x, y1 + this.gutterTop); + + if (this.Get('chart.boxplot.capped')) { + this.context.moveTo(x - halfBoxWidth, y4 + this.gutterTop); + this.context.lineTo(x + halfBoxWidth, y4 + this.gutterTop); + } + + this.context.moveTo(x, y4 + this.gutterTop); + this.context.lineTo(x, y3 + this.gutterTop); + + this.context.stroke(); + } + + + /** + * Draw the tickmark, but not for boxplots + */ + if (!y0 && !y1 && !y2 && !y3 && !y4) { + + this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom; + + + + if (tickmarks == 'circle') { + this.context.arc(x, y, halfTickSize, 0, 6.28, 0); + this.context.fillStyle = color; + this.context.fill(); + + } else if (tickmarks == 'plus') { + + this.context.moveTo(x, y - halfTickSize); + this.context.lineTo(x, y + halfTickSize); + this.context.moveTo(x - halfTickSize, y); + this.context.lineTo(x + halfTickSize, y); + this.context.stroke(); + + } else if (tickmarks == 'square') { + this.context.strokeStyle = color; + this.context.fillStyle = color; + this.context.fillRect( + x - halfTickSize, + y - halfTickSize, + tickSize, + tickSize + ); + //this.context.fill(); + + } else if (tickmarks == 'cross') { + + this.context.moveTo(x - halfTickSize, y - halfTickSize); + this.context.lineTo(x + halfTickSize, y + halfTickSize); + this.context.moveTo(x + halfTickSize, y - halfTickSize); + this.context.lineTo(x - halfTickSize, y + halfTickSize); + + this.context.stroke(); + + /** + * Diamond shape tickmarks + */ + } else if (tickmarks == 'diamond') { + this.context.fillStyle = this.context.strokeStyle; + + this.context.moveTo(x, y - halfTickSize); + this.context.lineTo(x + halfTickSize, y); + this.context.lineTo(x, y + halfTickSize); + this.context.lineTo(x - halfTickSize, y); + this.context.lineTo(x, y - halfTickSize); + + this.context.fill(); + this.context.stroke(); + + /** + * Custom tickmark style + */ + } else if (typeof(tickmarks) == 'function') { + + var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight + var xVal = ((x - this.gutterLeft) / graphWidth) * xMax; + var yVal = ((this.graphheight - (y - this.gutterTop)) / this.graphheight) * yMax; + + tickmarks(this, data, x, y, xVal, yVal, xMax, yMax, color) + + /** + * No tickmarks + */ + } else if (tickmarks == null) { + + /** + * Unknown tickmark type + */ + } else { + alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks ); + } + } + + /** + * Add the tickmark to the coords array + */ + if ( this.Get('chart.boxplot') + && typeof(y0) == 'number' + && typeof(y1) == 'number' + && typeof(y2) == 'number' + && typeof(y3) == 'number' + && typeof(y4) == 'number') { + + x = [x - halfBoxWidth, x + halfBoxWidth]; + y = [y0 + this.gutterTop, y1 + this.gutterTop, y2 + this.gutterTop, y3 + this.gutterTop, y4 + this.gutterTop]; + + } + + coords.push([x, y, tooltip]); + } + + + /** + * Draws an optional line connecting the tick marks. + * + * @param i The index of the dataset to use + */ + RGraph.Scatter.prototype.DrawLine = function (i) + { + if (this.Get('chart.line') && this.coords[i].length >= 2) { + + this.context.lineCap = 'round'; + this.context.lineJoin = 'round'; + this.context.lineWidth = this.GetLineWidth(i);// i is the index of the set of coordinates + this.context.strokeStyle = this.Get('chart.line.colors')[i]; + this.context.beginPath(); + + var len = this.coords[i].length; + + for (var j=0; j<this.coords[i].length; ++j) { + + var xPos = this.coords[i][j][0]; + var yPos = this.coords[i][j][1]; + + if (j == 0) { + this.context.moveTo(xPos, yPos); + } else { + + // Stepped? + var stepped = this.Get('chart.line.stepped'); + + if ( (typeof(stepped) == 'boolean' && stepped) + || (typeof(stepped) == 'object' && stepped[i]) + ) { + this.context.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]); + } + + this.context.lineTo(xPos, yPos); + } + } + + this.context.stroke(); + } + + /** + * Set the linewidth back to 1 + */ + this.context.lineWidth = 1; + } + + + /** + * Returns the linewidth + * + * @param number i The index of the "line" (/set of coordinates) + */ + RGraph.Scatter.prototype.GetLineWidth = function (i) + { + var linewidth = this.Get('chart.line.linewidth'); + + if (typeof(linewidth) == 'number') { + return linewidth; + + } else if (typeof(linewidth) == 'object') { + if (linewidth[i]) { + return linewidth[i]; + } else { + return linewidth[0]; + } + + alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers'); + } + } + + + /** + * Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function + * is not common + */ + RGraph.Scatter.prototype.DrawVBars = function () + { + var canvas = this.canvas; + var context = this.context; + var vbars = this.Get('chart.background.vbars'); + var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight; + + if (vbars) { + + var xmax = this.Get('chart.xmax'); + + for (var i=0; i<vbars.length; ++i) { + var startX = ((vbars[i][0] / xmax) * graphWidth) + this.gutterLeft; + var width = (vbars[i][1] / xmax) * graphWidth; + + context.beginPath(); + context.fillStyle = vbars[i][2]; + context.fillRect(startX, this.gutterTop, width, (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom)); + context.fill(); + } + } + } + + + + + + /** + * Draws in-graph labels. + * + * @param object obj The graph object + */ + RGraph.Scatter.prototype.DrawInGraphLabels = function (obj) + { + var canvas = obj.canvas; + var context = obj.context; + var labels = obj.Get('chart.labels.ingraph'); + var labels_processed = []; + + // Defaults + var fgcolor = 'black'; + var bgcolor = 'white'; + var direction = 1; + + if (!labels) { + return; + } + + /** + * Preprocess the labels array. Numbers are expanded + */ + for (var i=0; i<labels.length; ++i) { + if (typeof(labels[i]) == 'number') { + for (var j=0; j<labels[i]; ++j) { + labels_processed.push(null); + } + } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') { + labels_processed.push(labels[i]); + + } else { + labels_processed.push(''); + } + } + + /** + * Turn off any shadow + */ + RGraph.NoShadow(obj); + + if (labels_processed && labels_processed.length > 0) { + + var i=0; + + for (var set=0; set<obj.coords.length; ++set) { + for (var point = 0; point<obj.coords[set].length; ++point) { + if (labels_processed[i]) { + var x = obj.coords[set][point][0]; + var y = obj.coords[set][point][1]; + var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25; + + var text_x = x; + var text_y = y - 5 - length; + + context.moveTo(x, y - 5); + context.lineTo(x, y - 5 - length); + + context.stroke(); + context.beginPath(); + + // This draws the arrow + context.moveTo(x, y - 5); + context.lineTo(x - 3, y - 10); + context.lineTo(x + 3, y - 10); + context.closePath(); + + + context.beginPath(); + + // Fore ground color + context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black'; + + RGraph.Text(context, + obj.Get('chart.text.font'), + obj.Get('chart.text.size'), + text_x, + text_y, + (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i], + 'bottom', + 'center', + true, + null, + (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white'); + context.fill(); + } + + i++; + } + } + } + } + + + /** + * This function makes it much easier to get the (if any) point that is currently being hovered over. + * + * @param object e The event object + */ + RGraph.Scatter.prototype.getPoint = function (e) + { + var canvas = e.target; + var obj = canvas.__object__; + var context = obj.context; + var mouseXY = RGraph.getMouseXY(e); + var mouseX = mouseXY[0]; + var mouseY = mouseXY[1]; + var overHotspot = false; + var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends + + for (var set=0; set<obj.coords.length; ++set) { + for (var i=0; i<obj.coords[set].length; ++i) { + + var xCoord = obj.coords[set][i][0]; + var yCoord = obj.coords[set][i][1]; + + if (typeof(yCoord) == 'number') { + if (mouseX <= (xCoord + offset) && + mouseX >= (xCoord - offset) && + mouseY <= (yCoord + offset) && + mouseY >= (yCoord - offset)) { + + return [xCoord, yCoord, set, i, obj.data[set][i][3]]; + } + } else { + + var mark = obj.data[set][i]; + + + /** + * Determine the width + */ + var width = obj.Get('chart.boxplot.width'); + + if (typeof(mark[1][7]) == 'number') { + width = mark[1][7]; + } + + if ( typeof(xCoord) == 'object' + && mouseX > xCoord[0] + && mouseX < xCoord[1] + && mouseY < yCoord[3] + && mouseY > yCoord[1] + ) { + + return [[xCoord[0], xCoord[1] - xCoord[0]], [yCoord[1], yCoord[3] - yCoord[1]], set, i, obj.data[set][i][3]]; + } + } + } + } + } + + + /** + * Draws the above line labels + */ + RGraph.Scatter.prototype.DrawAboveLabels = function () + { + var context = this.context; + var size = this.Get('chart.labels.above.size'); + var font = this.Get('chart.text.font'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + + + for (var set=0; set<this.coords.length; ++set) { + for (var point=0; point<this.coords[set].length; ++point) { + + var x_val = this.data[set][point][0]; + var y_val = this.data[set][point][1]; + + + var x_pos = this.coords[set][point][0]; + var y_pos = this.coords[set][point][1]; + + RGraph.Text(context, + font, + size, + x_pos, + y_pos - 5 - size, + x_val.toFixed(this.Get('chart.labels.above.decimals')) + ', ' + y_val.toFixed(this.Get('chart.labels.above.decimals')), + 'center', + 'center', + true, + null, + 'rgba(255, 255, 255, 0.7)'); + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.skeleton.js b/schall/static/RGraph/libraries/RGraph.skeleton.js new file mode 100644 index 0000000..fd93edc --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.skeleton.js @@ -0,0 +1,333 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + + + + /** + * This is the RGraph.skeleton.js file which you can use as a base for creating new graph types. + */ + + + + + /** + * Having this here means that the RGraph libraries can be included in any order, instead of you having + * to include the common core library first. + */ + if (typeof(RGraph) == 'undefined') RGraph = {}; + + + + + /** + * The chart constructor. This function sets up the object. It takes the ID (the HTML attribute) of the canvas as the + * first argument and the data as the second. If you need to change this, you can. + * + * @param string id The canvas tag ID + * @param array data The chart data + */ + RGraph.Skeleton = function (id, data) + { + /** + * Set these as object properties so they don't have to be constantly retrieved. Note that using a dollar + * function - $() - can cause conflicts with popular javascript libraries, eg jQuery. It's therefore best + * to stick to document.getElementById(). Setting the canvas and context as object properties means you + * can reference them like this: myObj.canvas + * myObj.context + */ + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + + /** + * This puts a reference to this object on to the canvas. Useful in event handling. + */ + this.canvas.__object__ = this; + + /** + * This defines the type of this graph type and should be a one word description. + */ + this.type = 'skeleton'; + + /** + * This facilitates easy object identification, and should be true + */ + this.isRGraph = true; + + /** + * This does a few things, for example adding the .fillText() method to the canvas 2D context when + * it doesn't exist. This facilitates the graphs to be still shown in older browser (though without + * text obviously). You'll find the function in RGraph.common.core.js + */ + RGraph.OldBrowserCompat(this.context); + + + /** + * Some example background properties, as used by the method RGraph.background.Draw() + */ + this.properties = { + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.colors': ['red','blue'], + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': true, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':false, + 'chart.background.grid.autofit.align':false, + 'chart.background.grid.autofit.numhlines': 7, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.contextmenu': null, + 'chart.labels': null, + 'chart.labels.ingraph': null, + 'chart.labels.above': false, + 'chart.labels.above.decimals': 0, + 'chart.labels.above.size': null, + 'chart.scale.decimals': 0, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.title.yaxis.position': 'left', + 'chart.text.color': 'black', + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.font': 'Verdana', + 'chart.resizable': false, + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.hmargin': 5, + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.highlight': true + } + + /** + * A simple check that the browser has canvas support + */ + if (!this.canvas) { + alert('[SKELETON] No canvas support'); + return; + } + + /** + * Store the data that was passed to this constructor + */ + this.data = data; + + /** + * This can be used to store the coordinates of shapes on the graph + */ + this.coords = []; + + + /** + * If you add a .get*(e) method to ease getting the shape that's currently being hovered over + * or has been clicked on (in the same vein as the Bar charts .getBar() method or the Line charts + * .getPoint() method) then you should set this so that common methods have a common function + * name to call - for example the context menu uses the common name .getShape(e) to add the + * details to the context menu. + */ + this.getShape = this.getXXX; + } + + + + + /** + * A setter method for setting graph properties. It can be used like this: obj.Set('chart.background.grid', false); + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Skeleton.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + + + /** + * A getter method for retrieving graph properties. It can be used like this: obj.Get('chart.background.grid'); + * This can be used inside your methods that draw the graph. + * + * @param name string The name of the property to get + */ + RGraph.Skeleton.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + + + /** + * The function you call to draw the chart after you have set all of the graph properties + */ + RGraph.Skeleton.prototype.Draw = function () + { + /** + * This draws the background image, which when loaded draws the graph, hence the return + */ + if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) { + RGraph.DrawBackgroundImage(this); + return; + } + + /** + * Fire the custom RGraph onbeforedraw event (which should be fired before the chart is drawn) + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + + + + /************************* + * Draw the chart here... * + *************************/ + + + + + /** + * These call common functions, that facilitate some of RGraphs features + */ + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Draw "in graph" labels + */ + if (this.Get('chart.labels.ingraph')) { + RGraph.DrawInGraphLabels(this); + } + + /** + * Draw crosschairs + */ + if (this.Get('chart.crosshairs')) { + RGraph.DrawCrosshairs(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * This function enables adjusting + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the custom RGraph ondraw event (which should be fired when you have drawn the chart) + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } diff --git a/schall/static/RGraph/libraries/RGraph.thermometer.js b/schall/static/RGraph/libraries/RGraph.thermometer.js new file mode 100644 index 0000000..e3594a6 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.thermometer.js @@ -0,0 +1,418 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The chart constructor. This function sets up the object. It takes the ID (the HTML attribute) of the canvas as the + * first argument and the data as the second. If you need to change this, you can. + * + * @param string id The canvas tag ID + * @param number min The minimum value + * @param number max The maximum value + * @param number value The value reported by the thermometer + */ + RGraph.Thermometer = function (id, min, max, value) + { + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + + this.type = 'thermometer'; + this.isRGraph = true; + this.min = min; + this.max = max; + this.value = value; + this.coords = []; + this.graphArea = []; + this.currentValue = null; + + RGraph.OldBrowserCompat(this.context); + + this.properties = { + 'chart.colors': ['red'], + 'chart.gutter.left': 15, + 'chart.gutter.right': 15, + 'chart.gutter.top': 15, + 'chart.gutter.bottom': 15, + 'chart.ticksize': 5, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.text.size': 10, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.title': '', + 'chart.title.hpos': 0.5, + 'chart.title.side': '', + 'chart.title.side.bold': true, + 'chart.title.side.font': null, + 'chart.shadow': true, + 'chart.shadow.offsetx': 0, + 'chart.shadow.offsety': 0, + 'chart.shadow.blur': 15, + 'chart.shadow.color': 'gray', + 'chart.resizable': false, + 'chart.contextmenu': null, + 'chart.adjustable': false, + 'chart.value.label': true, + 'chart.scale.visible': false, + 'chart.scale.decimals': 0, + 'chart.ylabels.count': 10, + 'chart.annotatable': false, + 'chart.annotate.color': 'black' + } + + /** + * A simple check that the browser has canvas support + */ + if (!this.canvas) { + alert('[THERMOMETER] No canvas support'); + return; + } + } + + + + + /** + * A setter. + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Thermometer.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + + + /** + * A getter. + * + * @param name string The name of the property to get + */ + RGraph.Thermometer.prototype.Get = function (name) + { + return this.properties[name]; + } + + + + + /** + * Draws the thermometer + */ + RGraph.Thermometer.prototype.Draw = function () + { + /** + * Fire the custom RGraph onbeforedraw event (which should be fired before the chart is drawn) + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + + + /** + * Draw the background + */ + this.DrawBackground(); + + /** + * Draw the bar that represents the value + */ + this.DrawBar(); + + /** + * Draw the tickmarks/hatchmarks + */ + this.DrawTickMarks(); + + /** + * Draw the label + */ + this.DrawLabels(); + + /** + * Draw the title + */ + if (this.Get('chart.title')) { + this.DrawTitle(); + } + + /** + * Draw the side title + */ + if (this.Get('chart.title.side')) { + this.DrawSideTitle(); + } + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * Instead of using RGraph.common.adjusting.js, handle them here + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + + + + /** + * Fire the custom RGraph ondraw event (which should be fired when you have drawn the chart) + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + + + + /** + * Draws the thermometer itself + */ + RGraph.Thermometer.prototype.DrawBackground = function () + { + var canvas = this.canvas; + var context = this.context; + var bulbRadius = (this.canvas.width - this.gutterLeft - this.gutterRight) / 2; + + // Cache the bulbRadius as an object variable + this.bulbRadius = bulbRadius; + + // Draw the black background that becomes the border + context.beginPath(); + context.fillStyle = 'black'; + + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + context.fillRect(this.gutterLeft + 12,this.gutterTop + bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom - bulbRadius - bulbRadius); + context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius, 0, 6.28, 0); + context.arc(this.gutterLeft + bulbRadius,this.gutterTop + bulbRadius,(this.canvas.width - this.gutterLeft - this.gutterRight - 24)/ 2,0,6.28,0); + context.fill(); + + RGraph.NoShadow(this); + + // Draw the white inner content background that creates the border + context.beginPath(); + context.fillStyle = 'white'; + context.fillRect(this.gutterLeft + 12 + 1,this.gutterTop + bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24 - 2,RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom - bulbRadius - bulbRadius); + context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius - 1, 0, 6.28, 0); + context.arc(this.gutterLeft + bulbRadius,this.gutterTop + bulbRadius,((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24)/ 2) - 1,0,6.28,0); + context.fill(); + + // Draw the bottom content of the thermometer + context.beginPath(); + context.fillStyle = this.Get('chart.colors')[0]; + context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius - 1, 0, 6.28, 0); + context.fillRect(this.gutterLeft + 12 + 1, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius - bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24 - 2, bulbRadius); + context.fill(); + + // Save the X/Y/width/height + this.graphArea[0] = this.gutterLeft + 12 + 1; + this.graphArea[1] = this.gutterTop + bulbRadius; + this.graphArea[2] = this.canvas.width - this.gutterLeft - this.gutterRight - 24 - 2; + this.graphArea[3] = (this.canvas.height - this.gutterBottom - bulbRadius - bulbRadius) - (this.graphArea[1]); + } + + + /** + * This draws the bar that indicates the value of the thermometer + */ + RGraph.Thermometer.prototype.DrawBar = function () + { + var barHeight = ((this.value - this.min) / (this.max - this.min)) * this.graphArea[3]; + var context = this.context; + + // Draw the actual bar that indicates the value + context.beginPath(); + context.fillStyle = this.Get('chart.colors')[0]; + context.fillRect(this.graphArea[0],this.graphArea[1] + this.graphArea[3] - barHeight,this.graphArea[2],barHeight); + context.fill(); + + this.coords = [this.graphArea[0],this.graphArea[1] + this.graphArea[3] - barHeight,this.graphArea[2],barHeight]; + } + + + /** + * Draws the tickmarks of the thermometer + */ + RGraph.Thermometer.prototype.DrawTickMarks = function () + { + var ticksize = this.Get('chart.ticksize'); + + // Left hand side tickmarks + for (var i=this.graphArea[1]; i<=(this.graphArea[1] + this.graphArea[3]); i += (this.graphArea[3] / 10)) { + this.context.beginPath(); + this.context.moveTo(this.gutterLeft + 12, i); + this.context.lineTo(this.gutterLeft + 12 + ticksize, i); + this.context.stroke(); + } + + // Right hand side tickmarks + for (var i=this.graphArea[1]; i<=(this.graphArea[1] + this.graphArea[3]); i += (this.graphArea[3] / 10)) { + this.context.beginPath(); + this.context.moveTo(RGraph.GetWidth(this) - (this.gutterRight + 12), i); + this.context.lineTo(RGraph.GetWidth(this) - (this.gutterRight + 12 + ticksize), i); + this.context.stroke(); + } + } + + + /** + * Draws the labels of the thermometer. Now (4th August 2011) draws + * the scale too + */ + RGraph.Thermometer.prototype.DrawLabels = function () + { + if (this.Get('chart.value.label')) { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + + var text = this.Get('chart.scale.visible') ? String(this.value) : this.Get('chart.units.pre') + String(this.value) + this.Get('chart.units.post'); + + RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size'),this.gutterLeft + this.bulbRadius,this.coords[1] + this.Get('chart.text.size'),text,'center','center',true,null,'white'); + this.context.fill(); + } + + + /** + * Draw the scale if requested + */ + if (this.Get('chart.scale.visible')) { + this.DrawScale(); + } + } + + + /** + * Draws the title + */ + RGraph.Thermometer.prototype.DrawTitle = function () + { + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size') + 2,this.gutterLeft + ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / 2),this.gutterTop,String(this.Get('chart.title')),'center','center',null,null,null,true); + this.context.fill(); + } + + + /** + * Draws the title + */ + RGraph.Thermometer.prototype.DrawSideTitle = function () + { + var font = this.Get('chart.title.side.font') ? this.Get('chart.title.side.font') : this.Get('chart.text.font'); + var size = this.Get('chart.title.side.size') ? this.Get('chart.title.side.size') : this.Get('chart.text.size') + 2; + + this.context.beginPath(); + this.context.fillStyle = this.Get('chart.text.color'); + RGraph.Text(this.context, + font, + size, + this.gutterLeft * this.Get('chart.title.hpos'), + this.canvas.height / 2, + String(this.Get('chart.title.side')), + 'center', + 'center', + null, + 270, + null, + true); + this.context.fill(); + } + + + /** + * Draw the scale if requested + */ + RGraph.Thermometer.prototype.DrawScale = function () + { + var numLabels = this.Get('chart.ylabels.count') - 1; // The -1 is so that the number of labels tallies with what is displayed + var step = (this.max - this.min) / numLabels; + + this.context.fillStyle = this.Get('chart.text.color'); + + this.context.beginPath(); + for (var i=0; i<=numLabels; ++i) { + + var text = this.Get('chart.units.pre') + (i == 0 ? String(this.min.toFixed(this.Get('chart.scale.decimals'))) : String((this.min + (i * step)).toFixed(this.Get('chart.scale.decimals')))) + this.Get('chart.units.post'); + var x = this.canvas.width - this.gutterRight; + var y = this.canvas.height - this.gutterBottom - (2 * this.bulbRadius) - ((this.graphArea[3] / numLabels) * i); + + RGraph.Text(this.context, + this.Get('chart.text.font'), + this.Get('chart.text.size'), + x - 6, + y, + text, + 'center'); + } + this.context.fill(); + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.vprogress.js b/schall/static/RGraph/libraries/RGraph.vprogress.js new file mode 100644 index 0000000..dfc1a04 --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.vprogress.js @@ -0,0 +1,625 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The progress bar constructor + * + * @param int id The ID of the canvas tag + * @param int value The indicated value of the meter. + * @param int max The end value (the upper most) of the meter + */ + RGraph.VProgress = function (id, value, max) + { + this.id = id; + this.max = max; + this.value = value; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext('2d'); + this.canvas.__object__ = this; + this.type = 'vprogress'; + this.coords = []; + this.isRGraph = true; + this.currentValue = null; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + this.properties = { + 'chart.colors': ['#0c0'], + 'chart.strokestyle': '#999', + 'chart.tickmarks': true, + 'chart.tickmarks.zerostart':false, + 'chart.tickmarks.color': 'black', + 'chart.tickmarks.inner': false, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.numticks': 10, + 'chart.numticks.inner': 50, + 'chart.background.color': '#eee', + 'chart.shadow': false, + 'chart.shadow.color': 'rgba(0,0,0,0.5)', + 'chart.shadow.blur': 3, + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.title': '', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.width': 0, + 'chart.height': 0, + 'chart.text.size': 10, + 'chart.text.color': 'black', + 'chart.text.font': 'Verdana', + 'chart.contextmenu': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.tooltips': [], + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.mode': 'canvas', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.background': true, + 'chart.zoom.action': 'zoom', + 'chart.arrows': false, + 'chart.margin': 0, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.label.inner': false, + 'chart.labels.count': 10, + 'chart.labels.position': 'right', + 'chart.adjustable': false, + 'chart.min': 0, + 'chart.scale.decimals': 0, + 'chart.key': [], + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.halign': 'right', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.linewidth': 1 + } + + // Check for support + if (!this.canvas) { + alert('[PROGRESS] No canvas support'); + return; + } + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A generic setter + * + * @param string name The name of the property to set + * @param string value The value of the poperty + */ + RGraph.VProgress.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A generic getter + * + * @param string name The name of the property to get + */ + RGraph.VProgress.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * Draws the progress bar + */ + RGraph.VProgress.prototype.Draw = function () + { + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * Set the current value + */ + this.currentValue = this.value; + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + // Figure out the width and height + this.width = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight; + this.height = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + this.coords = []; + + this.Drawbar(); + this.DrawTickMarks(); + this.DrawLabels(); + + this.context.stroke(); + this.context.fill(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + /** + * Alternatively, show the tooltip if requested + */ + if (typeof(this.Get('chart.tooltips')) == 'function' || this.Get('chart.tooltips').length) { + + // Need to register this object for redrawing + RGraph.Register(this); + + /** + * Install the window onclick handler + */ + var window_onclick = function (){RGraph.Redraw();} + window.addEventListener('click', window_onclick, false); + RGraph.AddEventListener('window_' + this.id, 'click', window_onclick); + + + /** + * Install the onclick event handler for the tooltips + */ + //this.canvas.onclick = function (e) + var canvas_onclick_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Redraw(); + + + if (bar) { + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]); + + /** + * Show a tooltip if it's defined + */ + if (text) { + + obj.context.beginPath(); + obj.context.strokeStyle = obj.Get('chart.highlight.stroke'); + obj.context.fillStyle = obj.Get('chart.highlight.fill'); + obj.context.strokeRect(bar[1], bar[2], bar[3], bar[4]); + obj.context.fillRect(bar[1], bar[2], bar[3], bar[4]); + + obj.context.stroke(); + obj.context.fill(); + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]); + } + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + } + this.canvas.addEventListener('click', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); + + + /** + * If the cursor is over a hotspot, change the cursor to a hand + */ + //this.canvas.onmousemove = function (e) + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Change the mouse pointer + */ + if (bar) { + + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]); + + if (text) { + canvas.style.cursor = 'pointer'; + return; + } + } + + canvas.style.cursor = 'default'; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + // Draw a key if necessary + if (this.Get('chart.key').length) { + RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors')); + } + + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Instead of using RGraph.common.adjusting.js, handle them here + */ + if (this.Get('chart.adjustable')) { + RGraph.AllowAdjusting(this); + } + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draw the bar itself + */ + RGraph.VProgress.prototype.Drawbar = function () + { + // Set a shadow if requested + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } + + // Draw the shadow for MSIE + if (RGraph.isIE8() && this.Get('chart.shadow')) { + this.context.fillStyle = this.Get('chart.shadow.color'); + this.context.fillRect(this.gutterLeft + this.Get('chart.shadow.offsetx'), this.gutterTop + this.Get('chart.shadow.offsety'), this.width, this.height); + } + + // Draw the outline + this.context.fillStyle = this.Get('chart.background.color'); + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height); + this.context.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height); + + // Turn off any shadow + RGraph.NoShadow(this); + + this.context.strokeStyle = this.Get('chart.strokestyle'); + this.context.fillStyle = this.Get('chart.colors')[0]; + var margin = this.Get('chart.margin'); + var barHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom; + + // Draw the actual bar itself + if (typeof(this.value) == 'number') { + + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.strokestyle'); + + } else if (typeof(this.value) == 'object') { + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + var startPoint = this.canvas.height - this.gutterBottom; + + for (var i=0; i<this.value.length; ++i) { + + var segmentHeight = ( (this.value[i] - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * barHeight; + + this.context.fillStyle = this.Get('chart.colors')[i]; + + this.context.fillRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight); + this.context.strokeRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight); + + + // Store the coords + this.coords.push([this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight]); + + startPoint -= segmentHeight; + + } + + this.context.stroke(); + this.context.fill(); + } + + /** + * Inner tickmarks + */ + if (this.Get('chart.tickmarks.inner')) { + + var spacing = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / this.Get('chart.numticks.inner'); + + this.context.lineWidth = 1; + this.context.strokeStyle = this.Get('chart.strokestyle'); + + this.context.beginPath(); + + for (var y = this.gutterTop; y<RGraph.GetHeight(this) - this.gutterBottom; y+=spacing) { + this.context.moveTo(this.gutterLeft, y); + this.context.lineTo(this.gutterLeft + 3, y); + + this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, y); + this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight - 3, y); + } + + this.context.stroke(); + } + + /** + * Draw the actual bar + */ + var barHeight = Math.min(this.height, ( (this.value - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * this.height); + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.strokestyle'); + + if (typeof(this.value) == 'number') { + this.context.strokeRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight); + this.context.fillRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight); + } + + + /** + * Draw the arrows indicating the level if requested + */ + if (this.Get('chart.arrows')) { + var x = this.gutterLeft - 4; + var y = RGraph.GetHeight(this) - this.gutterBottom - barHeight; + + this.context.lineWidth = 1; + this.context.fillStyle = 'black'; + this.context.strokeStyle = 'black'; + + this.context.beginPath(); + this.context.moveTo(x, y); + this.context.lineTo(x - 4, y - 2); + this.context.lineTo(x - 4, y + 2); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + x += this.width + 8; + + this.context.beginPath(); + this.context.moveTo(x, y); + this.context.lineTo(x + 4, y - 2); + this.context.lineTo(x + 4, y + 2); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + } + + + + + /** + * Draw the "in-bar" label + */ + if (this.Get('chart.label.inner')) { + this.context.beginPath(); + this.context.fillStyle = 'black'; + RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') + 2, RGraph.GetWidth(this) / 2, RGraph.GetHeight(this) - this.gutterBottom - barHeight - 5, String(this.Get('chart.units.pre') + this.value + this.Get('chart.units.post')), 'bottom', 'center'); + this.context.fill(); + } + + + // Store the coords + this.coords.push([this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight]); + } + + /** + * The function that draws the tick marks. Apt name... + */ + RGraph.VProgress.prototype.DrawTickMarks = function () + { + this.context.strokeStyle = this.Get('chart.tickmarks.color'); + + if (this.Get('chart.tickmarks')) { + this.context.beginPath(); + for (var i=0; this.Get('chart.tickmarks.zerostart') ? i<=this.Get('chart.numticks') : i<this.Get('chart.numticks'); i++) { + + var startX = this.Get('chart.labels.position') == 'left' ? this.gutterLeft : this.canvas.width - this.Get('chart.gutter.right'); + var endX = this.Get('chart.labels.position') == 'left' ? startX - 4 : startX + 4; + var yPos = (this.height * (i / this.Get('chart.numticks'))) + this.gutterTop + + this.context.moveTo(startX, yPos); + this.context.lineTo(endX, yPos); + } + this.context.stroke(); + } + } + + + /** + * The function that draws the labels + */ + RGraph.VProgress.prototype.DrawLabels = function () + { + this.context.fillStyle = this.Get('chart.text.color'); + + var context = this.context; + var position = this.Get('chart.labels.position'); + var xAlignment = position == 'left' ? 'right' : 'left'; + var yAlignment = 'center'; + var count = this.Get('chart.labels.count'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + var text_size = this.Get('chart.text.size'); + var text_font = this.Get('chart.text.font'); + + if (this.Get('chart.tickmarks')) { + + for (var i=0; i<count ; ++i) { + + var text = String( + ((( (this.max - this.Get('chart.min')) / count) * (count - i)) + this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals')) + ); + + RGraph.Text(context, + text_font, + text_size, + position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5), + (((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / count) * i) + this.gutterTop, + units_pre + text + units_post, + yAlignment, + xAlignment); + } + + /** + * Show zero? + */ + if (this.Get('chart.tickmarks.zerostart') && this.Get('chart.min') == 0) { + RGraph.Text(context, + text_font, + text_size, + position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5), + RGraph.GetHeight(this) - this.gutterBottom, units_pre + String(this.Get('chart.min').toFixed(this.Get('chart.scale.decimals'))) + units_post, + yAlignment, + xAlignment); + } + + /** + * chart.ymin is set + */ + if (this.Get('chart.min') != 0) { + RGraph.Text(context, + text_font, + text_size, + position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5), + RGraph.GetHeight(this) - this.gutterBottom, units_pre + String(this.Get('chart.min').toFixed(this.Get('chart.scale.decimals'))) + units_post, + yAlignment, + xAlignment); + } + } + + // Draw the title text + if (this.Get('chart.title')) { + RGraph.Text(context, + text_font, + text_size + 2, + this.gutterLeft + ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2), // X + this.gutterTop - text_size, // Y + this.Get('chart.title'), + null, + 'center',null, null, null, true); + } + } + + + /** + * Returns the focused bar + * + * @param event e The event object + */ + RGraph.VProgress.prototype.getBar = function (e) + { + var obj = e.target.__object__; + var mouseCoords = RGraph.getMouseXY(e) + + for (var i=0; i<obj.coords.length; i++) { + + var mouseCoords = RGraph.getMouseXY(e); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + var idx = i; + + if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) { + return [obj, left, top, width, height, idx]; + } + } + }
\ No newline at end of file diff --git a/schall/static/RGraph/libraries/RGraph.waterfall.js b/schall/static/RGraph/libraries/RGraph.waterfall.js new file mode 100644 index 0000000..f3bf96c --- /dev/null +++ b/schall/static/RGraph/libraries/RGraph.waterfall.js @@ -0,0 +1,790 @@ +/** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bar chart constructor + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.Waterfall = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'waterfall'; + this.max = 0; + this.isRGraph = true; + this.coords = []; + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config + this.properties = { + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': true, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':true, + 'chart.background.grid.autofit.numhlines': 5, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.background.grid.autofit.align': false, + 'chart.background.image': null, + 'chart.background.hbars': null, // ??? + 'chart.xaxispos': 'bottom', + 'chart.numyticks': 10, + 'chart.hmargin': 5, + 'chart.strokestyle': '#666', + 'chart.axis.color': 'black', + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.labels': [], + 'chart.ylabels': true, + 'chart.text.color': 'black', + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.font': 'Verdana', + 'chart.ymax': null, + 'chart.title': '', + 'chart.title.color': 'black', + 'chart.title.background': null, + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.font': null, + 'chart.title.xaxis': '', + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.title.yaxis.align': 'left', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.colors': ['green', 'red', 'blue'], + 'chart.shadow': false, + 'chart.shadow.color': '#666', + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.highlight': true, + 'chart.tooltips.override': null, + 'chart.highlight.stroke': 'black', + 'chart.highlight.fill': 'rgba(255,255,255,0.5)', + 'chart.contextmenu': null, + 'chart.units.pre': '', + 'chart.units.post': '', + //'chart.scale.decimals': 0, + //'chart.scale.point': '.', + //'chart.scale.thousand': ',', + //'chart.scale.formatter': null, + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.mode': 'canvas', + 'chart.zoom.thumbnail.width': 75, + 'chart.zoom.thumbnail.height': 75, + 'chart.zoom.background': true, + 'chart.resizable': false, + 'chart.resize.handle.background': null, + 'chart.noaxes': false, + 'chart.noxaxis': false, + 'chart.noyaxis': false, + 'chart.axis.color': 'black', + 'chart.total': true, + 'chart.multiplier.x': 1, + 'chart.multiplier.w': 1 + } + + // Check for support + if (!this.canvas) { + alert('[WATERFALL] No canvas support'); + return; + } + + // Store the data + this.data = data; + + + /** + * Set the .getShape commonly named method + */ + this.getShape = this.getBar; + } + + + /** + * A setter + * + * @param name string The name of the property to set + * @param value mixed The value of the property + */ + RGraph.Waterfall.prototype.Set = function (name, value) + { + this.properties[name.toLowerCase()] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Waterfall.prototype.Get = function (name) + { + return this.properties[name.toLowerCase()]; + } + + + /** + * The function you call to draw the bar chart + */ + RGraph.Waterfall.prototype.Draw = function () + { + // MUST be the first thing done! + if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) { + RGraph.DrawBackgroundImage(this); + return; + } + + + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + /** + * Clear all of this canvases event handlers (the ones installed by RGraph) + */ + RGraph.ClearEventListeners(this.id); + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.Get('chart.gutter.left'); + this.gutterRight = this.Get('chart.gutter.right'); + this.gutterTop = this.Get('chart.gutter.top'); + this.gutterBottom = this.Get('chart.gutter.bottom'); + + /** + * Stop the coords array from growing uncontrollably + */ + this.coords = []; + + /** + * This gets used a lot + */ + this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + + /** + * Work out a few things. They need to be here because they depend on things you can change after you instantiate the object + */ + this.max = 0; + this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + this.graphwidth = this.canvas.width - this.gutterLeft - this.gutterRight; + this.halfTextHeight = this.Get('chart.text.size') / 2; + + + /** + * Work out the maximum value + * + * Normally the last bar is a total and not taken into account when the scale is generated. + * However it is not necessarily shown, so if it's not being shown it should not be + * accounted for. + */ + this.max = this.getMax(this.data); + this.scale = RGraph.getScale(typeof(this.Get('chart.ymax')) == 'number' ? this.Get('chart.ymax') : this.max, this); + this.max = this.scale[4]; + var decimals = this.Get('chart.scale.decimals'); + + this.scale = [ + (this.max * (1/5)).toFixed(decimals), + (this.max * (2/5)).toFixed(decimals), + (this.max * (3/5)).toFixed(decimals), + (this.max * (4/5)).toFixed(decimals), + typeof(this.max) == 'number' ? this.max.toFixed(decimals) : this.max + ]; + + + // Progressively Draw the chart + RGraph.background.Draw(this); + + this.DrawAxes(); + this.Drawbars(); + this.DrawLabels(); + + /** + * Setup the context menu if required + */ + if (this.Get('chart.contextmenu')) { + RGraph.ShowContext(this); + } + + + /** + * Draw crosschairs + */ + if (this.Get('chart.crosshairs')) { + RGraph.DrawCrosshairs(this); + } + + /** + * If the canvas is annotatable, do install the event handlers + */ + if (this.Get('chart.annotatable')) { + RGraph.Annotate(this); + } + + /** + * This bit shows the mini zoom window if requested + */ + if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { + RGraph.ShowZoomWindow(this); + } + + + /** + * This function enables resizing + */ + if (this.Get('chart.resizable')) { + RGraph.AllowResizing(this); + } + + /** + * Tooltips + */ + if (this.Get('chart.tooltips')) { + + RGraph.Register(this); + + /** + * Install the onclick event handler for the tooltips + */ + var canvas_onclick_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + + /** + * Loop through the bars determining if the mouse is over a bar + */ + if (bar) { + + /** + * First, if the event is onmousemove end the tooltip is already being shown, do nothing + */ + if (obj.Get('chart.tooltips.event') == 'onmousemove' && RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ == bar[5]) { + return; + } + + /** + * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn + * This "deselects" any already selected bar + */ + RGraph.Redraw(); + + var x = bar[1]; + var y = bar[2]; + var w = bar[3]; + var h = bar[4]; + + if (!obj.Get('chart.tooltips')[bar[5]]) { + return; + } + + + // Draw the highlight (if necessary) + if (obj.Get('chart.tooltips.highlight')) { + context.beginPath(); + context.fillStyle = obj.Get('chart.highlight.fill'); + context.strokeStyle = obj.Get('chart.highlight.stroke'); + context.strokeRect(x,y,w,h); + context.fillRect(x,y,w,h); + context.stroke(); + context.fill(); + } + + /** + * Get the tooltip text + */ + var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]); + + canvas.style.cursor = text ? 'pointer' : 'default'; + + RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]); + } + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + + return false; + } + this.canvas.addEventListener(this.Get('chart.tooltips.event') == 'onclick' ? 'click' : 'mousemove', canvas_onclick_func, false); + RGraph.AddEventListener(this.id, this.Get('chart.tooltips.event') == 'onclick' ? 'click' : 'mousemove', canvas_onclick_func); + + /** + * Install the window onclick handler + */ + var window_onclick_func = function (){RGraph.Redraw();}; + window.addEventListener('click', window_onclick_func, false); + RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func); + + /** + * Install the onmousemove event handler for the tooltips + */ + var canvas_onmousemove_func = function (e) + { + e = RGraph.FixEventObject(e); + + var canvas = document.getElementById(this.id); + var context = canvas.getContext('2d'); + var obj = canvas.__object__; + var bar = obj.getBar(e); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + if (bar) { + if (obj.Get('chart.tooltips')[bar[5]]) { + canvas.style.cursor = 'pointer'; + e.stopPropagation(); + } + + return; + } + + canvas.style.cursor = 'default'; + + /** + * Stop the event bubbling + */ + e.stopPropagation(); + + return false; + } + this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); + RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); + + + + + + + + + + + + + + } + + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + /** + * Draws the charts axes + */ + RGraph.Waterfall.prototype.DrawAxes = function () + { + if (this.Get('chart.noaxes')) { + return; + } + + this.context.beginPath(); + this.context.strokeStyle = this.Get('chart.axis.color'); + this.context.lineWidth = 1; + + // Draw the Y axis + if (this.Get('chart.noyaxis') == false) { + this.context.moveTo(this.gutterLeft, this.gutterTop); + this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + } + + // Draw the X axis + if (this.Get('chart.noxaxis') == false) { + // Center X axis + if (this.Get('chart.xaxispos') == 'center') { + this.context.moveTo(this.gutterLeft, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop); + this.context.lineTo(this.canvas.width - this.gutterRight, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop); + } else { + this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + this.context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom); + } + } + + var numYTicks = this.Get('chart.numyticks'); + + // Draw the Y tickmarks + if (this.Get('chart.noyaxis') == false) { + + var yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / numYTicks; + + for (y=this.gutterTop; y < (this.canvas.height - this.gutterBottom); y += yTickGap) { + + if (this.Get('chart.xaxispos') == 'bottom' || (y != ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop)) { + this.context.moveTo(this.gutterLeft, y); + this.context.lineTo(this.gutterLeft - 3, y); + } + } + + /** + * If the X axis is not being shown, draw an extra tick + */ + if (this.Get('chart.noxaxis') || this.Get('chart.xaxispos') == 'center') { + this.context.moveTo(this.gutterLeft - 3, this.canvas.height - this.gutterBottom); + this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom); + } + } + + + // Draw the X tickmarks + if (this.Get('chart.noxaxis') == false) { + + xTickGap = (this.canvas.width - this.gutterLeft - this.gutterRight ) / (this.data.length + (this.Get('chart.total') ? 1 : 0)); + + if (this.Get('chart.xaxispos') == 'center') { + yStart = ((this.canvas.height - this.gutterBottom - this.gutterTop) / 2) + this.gutterTop - 3; + yEnd = ((this.canvas.height - this.gutterBottom - this.gutterTop) / 2) + this.gutterTop + 3; + } else { + yStart = this.canvas.height - this.gutterBottom; + yEnd = (this.canvas.height - this.gutterBottom) + 3; + } + + for (x=this.gutterLeft + xTickGap; x<=RGraph.GetWidth(this) - this.gutterRight; x+=xTickGap) { + this.context.moveTo(x, yStart); + this.context.lineTo(x, yEnd); + } + + if (this.Get('chart.noyaxis')) { + this.context.moveTo(this.gutterLeft, yStart); + this.context.lineTo(this.gutterLeft, yEnd); + } + } + + /** + * If the Y axis is not being shown, draw an extra tick + */ + if (this.Get('chart.noyaxis') && this.Get('chart.noxaxis') == false) { + this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom); + this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom + 3); + } + + this.context.stroke(); + } + + + /** + * Draws the labels for the graph + */ + RGraph.Waterfall.prototype.DrawLabels = function () + { + var context = this.context; + var numYLabels = 5; // Make this configurable + var interval = this.grapharea / numYLabels; + var font = this.Get('chart.text.font'); + var size = this.Get('chart.text.size'); + var color = this.Get('chart.text.color'); + var units_pre = this.Get('chart.units.pre'); + var units_post = this.Get('chart.units.post'); + + this.context.beginPath(); + this.context.fillStyle = color; + + /** + * First, draw the Y labels + */ + if (this.Get('chart.ylabels')) { + if (this.Get('chart.xaxispos') == 'center') { + + var halfInterval = interval / 2; + var halfWay = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, + font, + size, + this.gutterLeft - 5, + this.gutterTop + (i * halfInterval), + RGraph.number_format(this, this.scale[4 - i], units_pre, units_post), + 'center', + 'right'); + } + + for (var i=0; i<numYLabels; ++i) { + RGraph.Text(context, + font, + size, + this.gutterLeft - 5, + halfWay + (i * halfInterval) + halfInterval, + '-' + RGraph.number_format(this, this.scale[i], units_pre, units_post), + 'center', + 'right'); + } + + } else { + + for (var i=1; i<=numYLabels; ++i) { + RGraph.Text(context, + font, + size, + this.gutterLeft - 5, + this.canvas.height - this.gutterBottom - (i * interval), + RGraph.number_format(this, this.scale[i - 1], units_pre, units_post), + 'center', + 'right'); + + } + } + } + + + + /** + * Now, draw the X labels + */ + if (this.Get('chart.labels').length > 0) { + + // Recalculate the interval for the X labels + interval = (this.canvas.width - this.gutterLeft - this.gutterRight) / this.Get('chart.labels').length; + + var halign = 'center'; + var angle = this.Get('chart.text.angle'); + + if (angle) { + halign = 'right'; + angle *= -1; + } + + var labels = this.Get('chart.labels'); + + for (var i=0; i<labels.length; ++i) { + + // context, font, size, x, y, text[, valign[, halign[, border[, angle[, background[, bold[, indicator]]]]]]] + RGraph.Text(context, + font, + size, + this.gutterLeft + (i * interval) + (interval / 2), + this.canvas.height - this.gutterBottom + 5 + this.halfTextHeight, + labels[i], + 'center', + halign, + null, + angle); + + } + } + + this.context.stroke(); + this.context.fill(); + } + + + /** + * Draws the bars on to the chart + */ + RGraph.Waterfall.prototype.Drawbars = function () + { + var context = this.context; + var canvas = this.canvas; + var hmargin = this.Get('chart.hmargin'); + var runningTotal = 0; + + + for (var i=0; i<this.data.length; ++i) { + context.beginPath(); + context.strokeStyle = this.Get('chart.strokestyle'); + + var x = this.gutterLeft + hmargin + (((this.graphwidth / (this.data.length + (this.Get('chart.total') ? 1 : 0))) * i) * this.Get('chart.multiplier.x')); + var y = this.gutterTop + this.grapharea - (i == 0 ? ((this.data[0] / this.max) * this.grapharea) : (this.data[i] > 0 ? ((runningTotal + this.data[i]) / this.max) * this.grapharea : (runningTotal / this.max) * this.grapharea)); + var w = ((this.canvas.width - this.gutterLeft - this.gutterRight) / (this.data.length + (this.Get('chart.total') ? 1 : 0 )) ) - (2 * this.Get('chart.hmargin')); + w = w * this.Get('chart.multiplier.w'); + var h = (Math.abs(this.data[i]) / this.max) * this.grapharea; + + if (this.Get('chart.xaxispos') == 'center') { + h /= 2; + y = (i == 0 ? ((this.data[0] / this.max) * this.grapharea) : (this.data[i] > 0 ? ((runningTotal + this.data[i]) / this.max) * this.grapharea : (runningTotal / this.max) * this.grapharea)); + y = this.gutterTop + (this.grapharea/2) - (y / 2); + } + + // Color + context.fillStyle = this.data[i] >= 0 ? this.Get('chart.colors')[0] : this.Get('chart.colors')[1]; + + + if (this.Get('chart.shadow')) { + RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur')); + } else { + RGraph.NoShadow(this); + } + + context.strokeRect(x, y, w, h); + context.fillRect(x, y, w, h); + + this.coords.push([x, y, w, h]); + + runningTotal += this.data[i]; + + context.stroke(); + context.fill(); + } + +if (this.Get('chart.total')) { + + // This is the height of the final bar + h = (runningTotal / this.max) * (this.grapharea / (this.Get('chart.xaxispos') == 'center' ? 2 : 1)); + + /** + * Set the Y (ie the start point) value + */ + if (this.Get('chart.xaxispos') == 'center') { + y = runningTotal > 0 ? this.centery - h : this.centery - h; + } else { + y = this.canvas.height - this.gutterBottom - h; + } + + // This is the X position of the final bar + x = x + (this.Get('chart.hmargin') * 2) + w; + + + // Final color + this.context.fillStyle = this.Get('chart.colors')[2]; + + this.context.beginPath(); + this.context.strokeRect(x, y, w, h); + this.context.fillRect(x, y, w, h); + this.context.stroke(); + this.context.fill(); + + this.coords.push([x, y - (runningTotal > 0 ? 0 : Math.abs(h)), w, Math.abs(h)]); +} + + RGraph.NoShadow(this); + + /** + * This draws the connecting lines + */ + for (var i=1; i<this.coords.length; ++i) { + context.strokeStyle = 'gray'; + context.beginPath(); + if (this.data[i - 1] > 0) { + context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1]); + context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1]); + } else { + context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1] + this.coords[i - 1][3]); + context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1] + this.coords[i - 1][3]); + } + context.stroke(); + } + } + + + /** + * Not used by the class during creating the graph, but is used by event handlers + * to get the coordinates (if any) of the selected bar + * + * @param object e The event object + */ + RGraph.Waterfall.prototype.getBar = function (e) + { + var canvas = e.target; + var obj = e.target.__object__; + var mouseCoords = RGraph.getMouseXY(e); + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i<obj.coords.length; i++) { + + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + + var left = obj.coords[i][0]; + var top = obj.coords[i][1]; + var width = obj.coords[i][2]; + var height = obj.coords[i][3]; + + if ( mouseX >= left + && mouseX <= left + width + && mouseY >= top + && mouseY <= top + height) { + + return [obj, left, top, width, height, i]; + } + } + + return null; + } + + + /** + * The Waterfall is slightly different to Bar/Line charts so has this function to get the max value + */ + RGraph.Waterfall.prototype.getMax = function (data) + { + var runningTotal = 0; + var max = 0; + + for (var i=0; i<data.length; ++i) { + runningTotal += data[i]; + max = Math.max(max, Math.abs(runningTotal)); + } + + return max; + }
\ No newline at end of file |