From 770ba5201f5c60b2bb36602ff9d359f641e33125 Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Sun, 23 Oct 2011 21:14:40 +0200 Subject: Charting with flask, rgraph and custom "timeseries database" --- schall/static/RGraph/libraries/RGraph.bar.js | 1840 ++++++++++++ schall/static/RGraph/libraries/RGraph.bipolar.js | 787 +++++ .../RGraph/libraries/RGraph.common.adjusting.js | 1167 ++++++++ .../RGraph/libraries/RGraph.common.annotate.js | 340 +++ .../RGraph/libraries/RGraph.common.context.js | 579 ++++ .../static/RGraph/libraries/RGraph.common.core.js | 2991 ++++++++++++++++++++ .../RGraph/libraries/RGraph.common.effects.js | 1516 ++++++++++ .../RGraph/libraries/RGraph.common.resizing.js | 471 +++ .../RGraph/libraries/RGraph.common.tooltips.js | 847 ++++++ .../static/RGraph/libraries/RGraph.common.zoom.js | 886 ++++++ schall/static/RGraph/libraries/RGraph.fuel.js | 364 +++ schall/static/RGraph/libraries/RGraph.funnel.js | 679 +++++ schall/static/RGraph/libraries/RGraph.gantt.js | 514 ++++ schall/static/RGraph/libraries/RGraph.gauge.js | 464 +++ schall/static/RGraph/libraries/RGraph.hbar.js | 966 +++++++ schall/static/RGraph/libraries/RGraph.hprogress.js | 589 ++++ schall/static/RGraph/libraries/RGraph.led.js | 232 ++ schall/static/RGraph/libraries/RGraph.line.js | 2217 +++++++++++++++ schall/static/RGraph/libraries/RGraph.meter.js | 573 ++++ .../static/RGraph/libraries/RGraph.modaldialog.js | 244 ++ schall/static/RGraph/libraries/RGraph.odo.js | 815 ++++++ schall/static/RGraph/libraries/RGraph.pie.js | 1042 +++++++ schall/static/RGraph/libraries/RGraph.pie.js.old | 975 +++++++ schall/static/RGraph/libraries/RGraph.radar.js | 739 +++++ schall/static/RGraph/libraries/RGraph.rose.js | 894 ++++++ schall/static/RGraph/libraries/RGraph.rscatter.js | 653 +++++ schall/static/RGraph/libraries/RGraph.scatter.js | 1662 +++++++++++ schall/static/RGraph/libraries/RGraph.skeleton.js | 333 +++ .../static/RGraph/libraries/RGraph.thermometer.js | 418 +++ schall/static/RGraph/libraries/RGraph.vprogress.js | 625 ++++ schall/static/RGraph/libraries/RGraph.waterfall.js | 790 ++++++ 31 files changed, 27212 insertions(+) create mode 100644 schall/static/RGraph/libraries/RGraph.bar.js create mode 100644 schall/static/RGraph/libraries/RGraph.bipolar.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.adjusting.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.annotate.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.context.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.core.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.effects.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.resizing.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.tooltips.js create mode 100644 schall/static/RGraph/libraries/RGraph.common.zoom.js create mode 100644 schall/static/RGraph/libraries/RGraph.fuel.js create mode 100644 schall/static/RGraph/libraries/RGraph.funnel.js create mode 100644 schall/static/RGraph/libraries/RGraph.gantt.js create mode 100644 schall/static/RGraph/libraries/RGraph.gauge.js create mode 100644 schall/static/RGraph/libraries/RGraph.hbar.js create mode 100644 schall/static/RGraph/libraries/RGraph.hprogress.js create mode 100644 schall/static/RGraph/libraries/RGraph.led.js create mode 100644 schall/static/RGraph/libraries/RGraph.line.js create mode 100644 schall/static/RGraph/libraries/RGraph.meter.js create mode 100644 schall/static/RGraph/libraries/RGraph.modaldialog.js create mode 100644 schall/static/RGraph/libraries/RGraph.odo.js create mode 100644 schall/static/RGraph/libraries/RGraph.pie.js create mode 100644 schall/static/RGraph/libraries/RGraph.pie.js.old create mode 100644 schall/static/RGraph/libraries/RGraph.radar.js create mode 100644 schall/static/RGraph/libraries/RGraph.rose.js create mode 100644 schall/static/RGraph/libraries/RGraph.rscatter.js create mode 100644 schall/static/RGraph/libraries/RGraph.scatter.js create mode 100644 schall/static/RGraph/libraries/RGraph.skeleton.js create mode 100644 schall/static/RGraph/libraries/RGraph.thermometer.js create mode 100644 schall/static/RGraph/libraries/RGraph.vprogress.js create mode 100644 schall/static/RGraph/libraries/RGraph.waterfall.js (limited to 'schall/static/RGraph/libraries') 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 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 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.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 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 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 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 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 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; i0; --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=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= 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= 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= 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[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 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[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 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.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.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 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 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 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 '; + + // This makes the colours go across two levels + if (i == 5) { + str += '
'; + } + } + + 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 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; iURL:'; + div.innerHTML += '
A link using the URL: View
' + + + + /** + * 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 ' + 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= 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=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 (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 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 (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=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 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 0) { + + for (var i=0; i 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 = '' + obj.Get('chart.crosshairs.coords.labels.x') + ': ' + xCoord + '
' + obj.Get('chart.crosshairs.coords.labels.y') + ': ' + 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 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 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=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 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 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 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 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 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 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 (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 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= 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 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 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 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.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= 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 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 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 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= 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= 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 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 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 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=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= 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= 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 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 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 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 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 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 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 (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 (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) { + 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 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= (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.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= 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 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 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 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 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= 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 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 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= 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 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 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 -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 (x - 5) + && mouseY > (y - 5) + && mouseY < (y + 5) + ) { + + // This accounts for the datasets and increases the index accordingly + for(var j=0; j 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 -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 (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[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 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 -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 (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 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 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 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 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 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 0) { + angle = -1 * this.Get('chart.text.angle'); + valign = 'center'; + halign = 'right'; + yPos -= 10; + } + + for (i=0; i= 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 0) { + + var i=0; + + for (var set=0; set= (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= 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 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 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 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= 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