summaryrefslogtreecommitdiff
path: root/schall/static/RGraph/libraries
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2011-10-23 21:14:40 +0200
committerYves Fischer <yvesf-git@xapek.org>2011-10-30 11:33:54 +0100
commit770ba5201f5c60b2bb36602ff9d359f641e33125 (patch)
tree2200757f04efbc281c4b3714bfe2e4fe911193b6 /schall/static/RGraph/libraries
parent77f5e686612898974e13f2a5df57c20e4e529363 (diff)
downloadmini-octopus-770ba5201f5c60b2bb36602ff9d359f641e33125.tar.gz
mini-octopus-770ba5201f5c60b2bb36602ff9d359f641e33125.zip
Charting with flask, rgraph and custom "timeseries database"
Diffstat (limited to 'schall/static/RGraph/libraries')
-rw-r--r--schall/static/RGraph/libraries/RGraph.bar.js1840
-rw-r--r--schall/static/RGraph/libraries/RGraph.bipolar.js787
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.adjusting.js1167
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.annotate.js340
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.context.js579
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.core.js2991
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.effects.js1516
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.resizing.js471
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.tooltips.js847
-rw-r--r--schall/static/RGraph/libraries/RGraph.common.zoom.js886
-rw-r--r--schall/static/RGraph/libraries/RGraph.fuel.js364
-rw-r--r--schall/static/RGraph/libraries/RGraph.funnel.js679
-rw-r--r--schall/static/RGraph/libraries/RGraph.gantt.js514
-rw-r--r--schall/static/RGraph/libraries/RGraph.gauge.js464
-rw-r--r--schall/static/RGraph/libraries/RGraph.hbar.js966
-rw-r--r--schall/static/RGraph/libraries/RGraph.hprogress.js589
-rw-r--r--schall/static/RGraph/libraries/RGraph.led.js232
-rw-r--r--schall/static/RGraph/libraries/RGraph.line.js2217
-rw-r--r--schall/static/RGraph/libraries/RGraph.meter.js573
-rw-r--r--schall/static/RGraph/libraries/RGraph.modaldialog.js244
-rw-r--r--schall/static/RGraph/libraries/RGraph.odo.js815
-rw-r--r--schall/static/RGraph/libraries/RGraph.pie.js1042
-rw-r--r--schall/static/RGraph/libraries/RGraph.pie.js.old975
-rw-r--r--schall/static/RGraph/libraries/RGraph.radar.js739
-rw-r--r--schall/static/RGraph/libraries/RGraph.rose.js894
-rw-r--r--schall/static/RGraph/libraries/RGraph.rscatter.js653
-rw-r--r--schall/static/RGraph/libraries/RGraph.scatter.js1662
-rw-r--r--schall/static/RGraph/libraries/RGraph.skeleton.js333
-rw-r--r--schall/static/RGraph/libraries/RGraph.thermometer.js418
-rw-r--r--schall/static/RGraph/libraries/RGraph.vprogress.js625
-rw-r--r--schall/static/RGraph/libraries/RGraph.waterfall.js790
31 files changed, 27212 insertions, 0 deletions
diff --git a/schall/static/RGraph/libraries/RGraph.bar.js b/schall/static/RGraph/libraries/RGraph.bar.js
new file mode 100644
index 0000000..b5946c6
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.bar.js
@@ -0,0 +1,1840 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The bar chart constructor
+ *
+ * @param object canvas The canvas object
+ * @param array data The chart data
+ */
+ RGraph.Bar = function (id, data)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'bar';
+ this.max = 0;
+ this.stackedOrGrouped = false;
+ this.isRGraph = true;
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config type stuff
+ this.properties = {
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
+ 'chart.background.grid': true,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.hsize': 20,
+ 'chart.background.grid.vsize': 20,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':true,
+ 'chart.background.grid.autofit.numhlines': 5,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.background.image': null,
+ 'chart.ytickgap': 20,
+ 'chart.smallyticks': 3,
+ 'chart.largeyticks': 5,
+ 'chart.numyticks': 10,
+ 'chart.hmargin': 5,
+ 'chart.strokecolor': '#666',
+ 'chart.axis.color': 'black',
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.labels': null,
+ 'chart.labels.ingraph': null,
+ 'chart.labels.above': false,
+ 'chart.labels.above.decimals': 0,
+ 'chart.labels.above.size': null,
+ 'chart.labels.above.angle': null,
+ 'chart.ylabels': true,
+ 'chart.ylabels.count': 5,
+ 'chart.ylabels.inside': false,
+ 'chart.xlabels.offset': 0,
+ 'chart.xaxispos': 'bottom',
+ 'chart.yaxispos': 'left',
+ 'chart.text.color': 'black',
+ 'chart.text.size': 10,
+ 'chart.text.angle': 0,
+ 'chart.text.font': 'Verdana',
+ 'chart.ymin': 0,
+ 'chart.ymax': null,
+ 'chart.title': '',
+ 'chart.title.font': null,
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.xaxis': '',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': null,
+ 'chart.colors': ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'],
+ 'chart.colors.sequential': false,
+ 'chart.colors.reverse': false,
+ 'chart.grouping': 'grouped',
+ 'chart.variant': 'bar',
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.tooltips': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.background.hbars': null,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.halign': 'right',
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.text.size': 10,
+ 'chart.key.linewidth': 1,
+ 'chart.contextmenu': null,
+ 'chart.line': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.scale.decimals': 0,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.crosshairs': false,
+ 'chart.crosshairs.color': '#333',
+ 'chart.crosshairs.hline': true,
+ 'chart.crosshairs.vline': true,
+ 'chart.linewidth': 1,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false,
+ 'chart.noaxes': false,
+ 'chart.noxaxis': false,
+ 'chart.noyaxis': false
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[BAR] No canvas support');
+ return;
+ }
+
+ /**
+ * Determine whether the chart will contain stacked or grouped bars
+ */
+ for (i=0; i<data.length; ++i) {
+ if (typeof(data[i]) == 'object') {
+ this.stackedOrGrouped = true;
+ }
+ }
+
+ // Store the data
+ this.data = data;
+
+ // Used to store the coords of the bars
+ this.coords = [];
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Bar.prototype.Set = function (name, value)
+ {
+ name = name.toLowerCase();
+
+ if (name == 'chart.labels.abovebar') {
+ name = 'chart.labels.above';
+ }
+
+ if (name == 'chart.strokestyle') {
+ name = 'chart.strokecolor';
+ }
+
+ /**
+ * Check for xaxispos
+ */
+ if (name == 'chart.xaxispos' ) {
+ if (value != 'bottom' && value != 'center' && value != 'top') {
+ alert('[BAR] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
+ value = 'center';
+ }
+
+ if (value == 'top') {
+ for (var i=0; i<this.data.length; ++i) {
+ if (typeof(this.data[i]) == 'number' && this.data[i] > 0) {
+ alert('[BAR] The data element with index ' + i + ' should be negative');
+ }
+ }
+ }
+ }
+
+ this.properties[name] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Bar.prototype.Get = function (name)
+ {
+ if (name == 'chart.labels.abovebar') {
+ name = 'chart.labels.above';
+ }
+
+ var value = this.properties[name];
+
+ return value;
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.Bar.prototype.Draw = function ()
+ {
+ // MUST be the first thing done!
+ if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
+ RGraph.DrawBackgroundImage(this);
+ return;
+ }
+
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Convert any null values to 0. Won't make any difference to the bar (as opposed to the line chart)
+ */
+ for (var i=0; i<this.data.length; ++i) {
+ if (this.data[i] == null) {
+ this.data[i] = 0;
+ }
+ }
+
+
+ // Cache this in a class variable as it's used rather a lot
+
+ /**
+ * Check for tooltips and alert the user that they're not supported with pyramid charts
+ */
+ if ( (this.Get('chart.variant') == 'pyramid' || this.Get('chart.variant') == 'dot')
+ && typeof(this.Get('chart.tooltips')) == 'object'
+ && this.Get('chart.tooltips')
+ && this.Get('chart.tooltips').length > 0) {
+
+ alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');
+ }
+
+ /**
+ * Stop the coords array from growing uncontrollably
+ */
+ this.coords = [];
+
+ /**
+ * Work out a few things. They need to be here because they depend on things you can change before you
+ * call Draw() but after you instantiate the object
+ */
+ this.max = 0;
+ this.grapharea = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+ this.halfgrapharea = this.grapharea / 2;
+ this.halfTextHeight = this.Get('chart.text.size') / 2;
+
+ // Progressively Draw the chart
+ RGraph.background.Draw(this);
+
+
+ //If it's a sketch chart variant, draw the axes first
+ if (this.Get('chart.variant') == 'sketch') {
+ this.DrawAxes();
+ this.Drawbars();
+ } else {
+ this.Drawbars();
+ this.DrawAxes();
+ }
+
+ this.DrawLabels();
+
+
+ // Draw the key if necessary
+ if (this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+
+ /**
+ * Is a line is defined, draw it
+ */
+ var line = this.Get('chart.line');
+
+ if (line) {
+
+ line.__bar__ = this;
+
+ // Check the X axis positions
+ if (this.Get('chart.xaxispos') != line.Get('chart.xaxispos')) {
+ alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised");
+ }
+
+ line.Set('chart.gutter.left', this.Get('chart.gutter.left'));
+ line.Set('chart.gutter.right', this.Get('chart.gutter.right'));
+ line.Set('chart.gutter.top', this.Get('chart.gutter.top'));
+ line.Set('chart.gutter.bottom', this.Get('chart.gutter.bottom'));
+
+ line.Set('chart.hmargin', (this.canvas.width - this.gutterLeft - this.gutterRight) / (line.original_data[0].length * 2));
+
+ // If a BAR custom yMax is set, use that
+ if (this.Get('chart.ymax') && !line.Get('chart.ymax')) {
+ line.Set('chart.ymax', this.Get('chart.ymax'));
+
+ } else if (line.Get('chart.ymax')) {
+ line.Set('chart.ymax', line.Get('chart.ymax'));
+ }
+
+ // The boolean is used to specify that the Line chart .Draw() method is being called by the Bar chart
+ line.Draw(true);
+ }
+
+
+ /**
+ * Draw "in graph" labels
+ */
+ if (this.Get('chart.labels.ingraph')) {
+ RGraph.DrawInGraphLabels(this);
+ }
+
+ /**
+ * Draw crosschairs
+ */
+ if (this.Get('chart.crosshairs')) {
+ RGraph.DrawCrosshairs(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * This function enables adjusting
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the charts axes
+ */
+ RGraph.Bar.prototype.DrawAxes = function ()
+ {
+ if (this.Get('chart.noaxes')) {
+ return;
+ }
+
+ var xaxispos = this.Get('chart.xaxispos');
+ var yaxispos = this.Get('chart.yaxispos');
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.axis.color');
+ this.context.lineWidth = 1;
+
+ // Draw the Y axis
+ if (this.Get('chart.noyaxis') == false) {
+ if (yaxispos == 'right') {
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ } else {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+
+ // Draw the X axis
+ if (this.Get('chart.noxaxis') == false) {
+ if (xaxispos == 'center') {
+ this.context.moveTo(this.gutterLeft, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop);
+ this.context.lineTo(this.canvas.width - this.gutterRight, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop);
+ } else if (xaxispos == 'top') {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(this.canvas.width - this.gutterRight, this.gutterTop);
+ } else {
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+
+ var numYTicks = this.Get('chart.numyticks');
+
+ // Draw the Y tickmarks
+ if (this.Get('chart.noyaxis') == false) {
+ var yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / numYTicks;
+ var xpos = yaxispos == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight;
+
+ for (y=this.gutterTop;
+ xaxispos == 'center' ? y <= (RGraph.GetHeight(this) - this.gutterBottom) : y < (RGraph.GetHeight(this) - this.gutterBottom + (xaxispos == 'top' ? 1 : 0));
+ y += yTickGap) {
+
+ if (xaxispos == 'center' && y == (RGraph.GetHeight(this) / 2)) continue;
+
+ // X axis at the top
+ if (xaxispos == 'top' && y == this.gutterTop) continue;
+
+ this.context.moveTo(xpos, y);
+ this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y);
+ }
+
+ /**
+ * If the X axis is not being shown, draw an extra tick
+ */
+ if (this.Get('chart.noxaxis')) {
+ if (xaxispos == 'center') {
+ this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), this.canvas.height / 2);
+ this.context.lineTo(xpos, RGraph.GetHeight(this) / 2);
+ } else if (xaxispos == 'top') {
+ this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), this.gutterTop);
+ this.context.lineTo(xpos, this.gutterTop);
+ } else {
+ this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(xpos, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+ }
+
+
+ // Draw the X tickmarks
+ if (this.Get('chart.noxaxis') == false) {
+
+ xTickGap = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.data.length;
+
+ if (xaxispos == 'bottom') {
+ yStart = this.canvas.height - this.gutterBottom;
+ yEnd = (this.canvas.height - this.gutterBottom) + 3;
+ } else if (xaxispos == 'top') {
+ yStart = this.gutterTop - 3;
+ yEnd = this.gutterTop;
+ } else if (xaxispos == 'center') {
+ yStart = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + 3;
+ yEnd = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - 3;
+ }
+
+ //////////////// X TICKS ////////////////
+ var noEndXTick = this.Get('chart.noendxtick');
+
+ for (x=this.gutterLeft + (yaxispos == 'left' ? xTickGap : 0); x<this.canvas.width - this.gutterRight + (yaxispos == 'left' ? 5 : 0); x+=xTickGap) {
+
+ if (yaxispos == 'left' && !noEndXTick && x > this.gutterLeft) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+
+ } else if (yaxispos == 'left' && noEndXTick && x > this.gutterLeft && x < (this.canvas.width - this.gutterRight) ) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+
+ } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && !noEndXTick) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+
+ } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && x > (this.gutterLeft) && noEndXTick) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+ }
+ }
+
+ if (this.Get('chart.noyaxis')) {
+ this.context.moveTo(this.gutterLeft, yStart);
+ this.context.lineTo(this.gutterLeft, yEnd);
+ }
+
+ //////////////// X TICKS ////////////////
+ }
+
+ /**
+ * If the Y axis is not being shown, draw an extra tick
+ */
+ if (this.Get('chart.noyaxis') && this.Get('chart.noxaxis') == false) {
+ if (xaxispos == 'center') {
+ this.context.moveTo(this.gutterLeft, (RGraph.GetHeight(this) / 2) - 3);
+ this.context.lineTo(this.gutterLeft, (RGraph.GetHeight(this) / 2) + 3);
+ } else {
+ this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom + 3);
+ }
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draws the bars
+ */
+ RGraph.Bar.prototype.Drawbars = function ()
+ {
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.strokeStyle = this.Get('chart.strokecolor');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+ var prevX = 0;
+ var prevY = 0;
+ var decimals = this.Get('chart.scale.decimals');
+
+ /**
+ * Work out the max value
+ */
+ if (this.Get('chart.ymax')) {
+
+ this.max = this.Get('chart.ymax');
+ this.min = this.Get('chart.ymin');
+
+ this.scale = [
+ (((this.max - this.min) * (1/5)) + this.min).toFixed(decimals),
+ (((this.max - this.min) * (2/5)) + this.min).toFixed(decimals),
+ (((this.max - this.min) * (3/5)) + this.min).toFixed(decimals),
+ (((this.max - this.min) * (4/5)) + this.min).toFixed(decimals),
+ (((this.max - this.min) * (5/5) + this.min)).toFixed(decimals)
+ ];
+ } else {
+
+ this.min = this.Get('chart.ymin');
+
+ for (i=0; i<this.data.length; ++i) {
+ if (typeof(this.data[i]) == 'object') {
+ var value = this.Get('chart.grouping') == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;
+
+ } else {
+ var value = Number(this.data[i]);
+ }
+
+ this.max = Math.max(Math.abs(this.max), Math.abs(value));
+ }
+
+ this.scale = RGraph.getScale(this.max, this);
+ this.max = this.scale[4];
+
+ if (this.Get('chart.ymin')) {
+
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale[0] = ((Number(this.scale[4] - this.min) * 0.2) + this.min).toFixed(decimals);
+ this.scale[1] = ((Number(this.scale[4] - this.min) * 0.4) + this.min).toFixed(decimals);
+ this.scale[2] = ((Number(this.scale[4] - this.min) * 0.6) + this.min).toFixed(decimals);
+ this.scale[3] = ((Number(this.scale[4] - this.min) * 0.8) + this.min).toFixed(decimals);
+ this.scale[4] = ((Number(this.scale[4] - this.min) * 1.0) + this.min).toFixed(decimals);
+
+ } else {
+ if (this.Get('chart.scale.decimals')) {
+
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale[0] = Number(this.scale[0]).toFixed(decimals);
+ this.scale[1] = Number(this.scale[1]).toFixed(decimals);
+ this.scale[2] = Number(this.scale[2]).toFixed(decimals);
+ this.scale[3] = Number(this.scale[3]).toFixed(decimals);
+ this.scale[4] = Number(this.scale[4]).toFixed(decimals);
+ }
+ }
+ }
+
+ /**
+ * Draw horizontal bars here
+ */
+ if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) {
+ RGraph.DrawBars(this);
+ }
+
+ var variant = this.Get('chart.variant');
+
+ /**
+ * Draw the 3D axes is necessary
+ */
+ if (variant == '3d') {
+ RGraph.Draw3DAxes(this);
+ }
+
+ /**
+ * Get the variant once, and draw the bars, be they regular, stacked or grouped
+ */
+
+ // Get these variables outside of the loop
+ var xaxispos = this.Get('chart.xaxispos');
+ var width = (this.canvas.width - this.gutterLeft - this.gutterRight ) / this.data.length;
+ var orig_height = height;
+ var hmargin = this.Get('chart.hmargin');
+ var shadow = this.Get('chart.shadow');
+ var shadowColor = this.Get('chart.shadow.color');
+ var shadowBlur = this.Get('chart.shadow.blur');
+ var shadowOffsetX = this.Get('chart.shadow.offsetx');
+ var shadowOffsetY = this.Get('chart.shadow.offsety');
+ var strokeStyle = this.Get('chart.strokecolor');
+ var colors = this.Get('chart.colors');
+
+ for (i=0; i<this.data.length; ++i) {
+
+ // Work out the height
+ //The width is up outside the loop
+ var height = ((RGraph.array_sum(this.data[i]) < 0 ? RGraph.array_sum(this.data[i]) + this.min : RGraph.array_sum(this.data[i]) - this.min) / (this.max - this.min) ) * (this.canvas.height - this.gutterTop - this.gutterBottom);
+
+ // Half the height if the Y axis is at the center
+ if (xaxispos == 'center') {
+ height /= 2;
+ }
+
+ var x = (i * width) + this.gutterLeft;
+ var y = xaxispos == 'center' ? ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - height
+ : this.canvas.height - height - this.gutterBottom;
+
+ // xaxispos is top
+ if (xaxispos == 'top') {
+ y = this.gutterTop + Math.abs(height);
+ }
+
+
+ // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
+ if (height < 0) {
+ y += height;
+ height = Math.abs(height);
+ }
+
+ /**
+ * Turn on the shadow if need be
+ */
+ if (shadow) {
+ this.context.shadowColor = shadowColor;
+ this.context.shadowBlur = shadowBlur;
+ this.context.shadowOffsetX = shadowOffsetX;
+ this.context.shadowOffsetY = shadowOffsetY;
+ }
+
+ /**
+ * Draw the bar
+ */
+ this.context.beginPath();
+ if (typeof(this.data[i]) == 'number') {
+
+ var barWidth = width - (2 * hmargin);
+
+ // Set the fill color
+ this.context.strokeStyle = strokeStyle;
+ this.context.fillStyle = colors[0];
+
+ /**
+ * Sequential colors
+ */
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = colors[i];
+ }
+
+ if (variant == 'sketch') {
+
+ this.context.lineCap = 'round';
+
+ var sketchOffset = 3;
+
+ this.context.beginPath();
+
+ this.context.strokeStyle = colors[0];
+
+ /**
+ * Sequential colors
+ */
+ if (this.Get('chart.colors.sequential')) {
+ this.context.strokeStyle = colors[i];
+ }
+
+ // Left side
+ this.context.moveTo(x + hmargin + 2, y + height - 2);
+ this.context.lineTo(x + hmargin , y - 2);
+
+ // The top
+ this.context.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0));
+ this.context.bezierCurveTo(x + ((hmargin + width) * 0.33),y + 5 + (this.data[i] < 0 ? height - 10: 0),x + ((hmargin + width) * 0.66),y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0));
+
+
+ // The right side
+ this.context.moveTo(x + hmargin + width - 2, y + -2);
+ this.context.lineTo(x + hmargin + width - 3, y + height - 3);
+
+ for (var r=0.2; r<=0.8; r+=0.2) {
+ this.context.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1);
+ this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2));
+ }
+
+ this.context.stroke();
+
+ // Regular bar
+ } else if (variant == 'bar' || variant == '3d' || variant == 'glass' || variant == 'bevel') {
+
+ if (RGraph.isIE8() && shadow) {
+ this.DrawIEShadow([x + hmargin, y, barWidth, height]);
+ }
+
+ if (variant == 'glass') {
+ RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
+ RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);
+ } else {
+ this.context.strokeRect(x + hmargin, y, barWidth, height);
+ this.context.fillRect(x + hmargin, y, barWidth, height);
+ }
+
+
+ // This bit draws the text labels that appear above the bars if requested
+ if (this.Get('chart.labels.above')) {
+
+ // Turn off any shadow
+ if (shadow) {
+ RGraph.NoShadow(this);
+ }
+
+ var yPos = y - 3;
+
+ // Account for negative bars
+ if (this.data[i] < 0) {
+ yPos += height + 6 + (this.Get('chart.text.size') - 4);
+ }
+
+ // Account for chart.xaxispos=top
+ if (this.Get('chart.xaxispos') == 'top') {
+ yPos = this.gutterTop + height + 6 + (typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 4);
+ }
+
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ RGraph.Text(this.context,
+ this.Get('chart.text.font'),
+ typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,
+ x + hmargin + (barWidth / 2),
+ yPos,
+ RGraph.number_format(this, Number(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')),this.Get('chart.units.pre'),this.Get('chart.units.post')),
+ this.Get('chart.labels.above.angle') ? 'bottom' : null,
+ this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 && (this.Get('chart.xaxispos') != 'top') ? 'right' : 'left') : 'center',
+ null,
+ this.Get('chart.labels.above.angle')
+ );
+ }
+
+ // 3D effect
+ if (variant == '3d') {
+
+ var prevStrokeStyle = this.context.strokeStyle;
+ var prevFillStyle = this.context.fillStyle;
+
+ // Draw the top
+ this.context.beginPath();
+ this.context.moveTo(x + hmargin, y);
+ this.context.lineTo(x + hmargin + 10, y - 5);
+ this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);
+ this.context.lineTo(x + hmargin + barWidth, y);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the right hand side
+ this.context.beginPath();
+ this.context.moveTo(x + hmargin + barWidth, y);
+ this.context.lineTo(x + hmargin + barWidth + 10, y - 5);
+ this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5);
+ this.context.lineTo(x + hmargin + barWidth, y + height);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the darker top section
+ this.context.beginPath();
+ this.context.fillStyle = 'rgba(255,255,255,0.3)';
+ this.context.moveTo(x + hmargin, y);
+ this.context.lineTo(x + hmargin + 10, y - 5);
+ this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);
+ this.context.lineTo(x + hmargin + barWidth, y);
+ this.context.lineTo(x + hmargin, y);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the darker right side section
+ this.context.beginPath();
+ this.context.fillStyle = 'rgba(0,0,0,0.4)';
+ this.context.moveTo(x + hmargin + barWidth, y);
+ this.context.lineTo(x + hmargin + barWidth + 10, y - 5);
+ this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height);
+ this.context.lineTo(x + hmargin + barWidth, y + height);
+ this.context.lineTo(x + hmargin + barWidth, y);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ this.context.strokeStyle = prevStrokeStyle;
+ this.context.fillStyle = prevFillStyle;
+
+ // Glass variant
+ } else if (variant == 'glass') {
+
+ var grad = this.context.createLinearGradient(
+ x + hmargin,
+ y,
+ x + hmargin + (barWidth / 2),
+ y
+ );
+ grad.addColorStop(0, 'rgba(255,255,255,0.9)');
+ grad.addColorStop(1, 'rgba(255,255,255,0.5)');
+
+ this.context.beginPath();
+ this.context.fillStyle = grad;
+ this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2);
+ this.context.fill();
+ }
+
+ // Dot chart
+ } else if (variant == 'dot') {
+
+ this.context.beginPath();
+ this.context.moveTo(x + (width / 2), y);
+ this.context.lineTo(x + (width / 2), y + height);
+ this.context.stroke();
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0);
+
+ // Set the colour for the dots
+ this.context.fillStyle = this.Get('chart.colors')[0];
+
+ /**
+ * Sequential colors
+ */
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = colors[i];
+ }
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Pyramid chart
+ } else if (variant == 'pyramid') {
+
+ this.context.beginPath();
+ var startY = (this.Get('chart.xaxispos') == 'center' ? (RGraph.GetHeight(this) / 2) : (RGraph.GetHeight(this) - this.gutterBottom));
+
+ this.context.moveTo(x + hmargin, startY);
+ this.context.lineTo(
+ x + hmargin + (barWidth / 2),
+ y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)
+ );
+ this.context.lineTo(x + hmargin + barWidth, startY);
+
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Arrow chart
+ } else if (variant == 'arrow') {
+
+ var startY = (this.Get('chart.xaxispos') == 'center' ? (RGraph.GetHeight(this) / 2) : (RGraph.GetHeight(this) - this.gutterBottom));
+
+ this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1;
+ this.context.lineCap = 'round';
+
+ this.context.beginPath();
+
+ this.context.moveTo(x + hmargin + (barWidth / 2), startY);
+ this.context.lineTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));
+ this.context.arc(x + hmargin + (barWidth / 2),
+ y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
+ 5,
+ this.data[i] > 0 ? 0.78 : 5.6,
+ this.data[i] > 0 ? 0.79 : 5.48,
+ this.data[i] < 0);
+
+ this.context.moveTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));
+ this.context.arc(x + hmargin + (barWidth / 2),
+ y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
+ 5,
+ this.data[i] > 0 ? 2.355 : 4,
+ this.data[i] > 0 ? 2.4 : 3.925,
+ this.data[i] < 0);
+
+ this.context.stroke();
+
+ this.context.lineWidth = 1;
+
+ // Unknown variant type
+ } else {
+ alert('[BAR] Warning! Unknown chart.variant: ' + variant);
+ }
+
+ this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
+
+
+ /**
+ * Stacked bar
+ */
+ } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') {
+
+ if (this.min) {
+ alert("[ERROR] Stacked Bar charts with a Y min are not supported");
+ }
+
+ var barWidth = width - (2 * hmargin);
+ var redrawCoords = [];// Necessary to draw if the shadow is enabled
+ var startY = 0;
+ var dataset = this.data[i];
+
+ for (j=0; j<dataset.length; ++j) {
+
+ // Stacked bar chart and X axis pos in the middle - poitless since negative values are not permitted
+ if (xaxispos == 'center') {
+ alert("[BAR] It's pointless having the X axis position at the center on a stacked bar chart.");
+ return;
+ }
+
+ // Negative values not permitted for the stacked chart
+ if (this.data[i][j] < 0) {
+ alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');
+ return;
+ }
+
+ /**
+ * Set the fill and stroke colors
+ */
+ this.context.strokeStyle = strokeStyle
+ this.context.fillStyle = colors[j];
+
+ if (this.Get('chart.colors.reverse')) {
+ this.context.fillStyle = colors[this.data[i].length - j - 1];
+ }
+
+ var height = (dataset[j] / this.max) * (this.canvas.height - this.gutterTop - this.gutterBottom );
+
+ // If the X axis pos is in the center, we need to half the height
+ if (xaxispos == 'center') {
+ height /= 2;
+ }
+
+ var totalHeight = (RGraph.array_sum(dataset) / this.max) * (this.canvas.height - hmargin - this.gutterTop - this.gutterBottom);
+
+ /**
+ * Store the coords for tooltips
+ */
+ this.coords.push([x + hmargin, y, width - (2 * hmargin), height]);
+
+ // MSIE shadow
+ if (RGraph.isIE8() && shadow) {
+ this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);
+ }
+
+ this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height);
+ this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);
+
+
+ if (j == 0) {
+ var startY = y;
+ var startX = x;
+ }
+
+ /**
+ * Store the redraw coords if the shadow is enabled
+ */
+ if (shadow) {
+ redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, colors[j]]);
+ }
+
+ /**
+ * Stacked 3D effect
+ */
+ if (variant == '3d') {
+
+ var prevFillStyle = this.context.fillStyle;
+ var prevStrokeStyle = this.context.strokeStyle;
+
+
+ // Draw the top side
+ if (j == 0) {
+ this.context.beginPath();
+ this.context.moveTo(startX + hmargin, y);
+ this.context.lineTo(startX + 10 + hmargin, y - 5);
+ this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);
+ this.context.lineTo(startX + barWidth + hmargin, y);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ // Draw the side section
+ this.context.beginPath();
+ this.context.moveTo(startX + barWidth + hmargin, y);
+ this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);
+ this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);
+ this.context.lineTo(startX + barWidth + hmargin , y + height);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ // Draw the darker top side
+ if (j == 0) {
+ this.context.fillStyle = 'rgba(255,255,255,0.3)';
+ this.context.beginPath();
+ this.context.moveTo(startX + hmargin, y);
+ this.context.lineTo(startX + 10 + hmargin, y - 5);
+ this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);
+ this.context.lineTo(startX + barWidth + hmargin, y);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ // Draw the darker side section
+ this.context.fillStyle = 'rgba(0,0,0,0.4)';
+ this.context.beginPath();
+ this.context.moveTo(startX + barWidth + hmargin, y);
+ this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);
+ this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);
+ this.context.lineTo(startX + barWidth + hmargin , y + height);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ this.context.strokeStyle = prevStrokeStyle;
+ this.context.fillStyle = prevFillStyle;
+ }
+
+ y += height;
+ }
+
+ // This bit draws the text labels that appear above the bars if requested
+ if (this.Get('chart.labels.above')) {
+
+ // Turn off any shadow
+ RGraph.NoShadow(this);
+
+ this.context.fillStyle = this.Get('chart.text.color');
+ RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (barWidth / 2) + this.Get('chart.hmargin'),startY - (this.Get('chart.shadow') && this.Get('chart.shadow.offsety') < 0 ? 7 : 4),String(this.Get('chart.units.pre') + RGraph.array_sum(this.data[i]).toFixed(this.Get('chart.labels.above.decimals')) + this.Get('chart.units.post')),this.Get('chart.labels.above.angle') ? 'bottom' : null,this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 ? 'right' : 'left') : 'center',null,this.Get('chart.labels.above.angle'));
+
+ // Turn any shadow back on
+ if (shadow) {
+ this.context.shadowColor = shadowColor;
+ this.context.shadowBlur = shadowBlur;
+ this.context.shadowOffsetX = shadowOffsetX;
+ this.context.shadowOffsetY = shadowOffsetY;
+ }
+ }
+
+
+ /**
+ * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the
+ * shadow spilling over to higher up bars
+ */
+ if (shadow) {
+
+ RGraph.NoShadow(this);
+
+ for (k=0; k<redrawCoords.length; ++k) {
+ this.context.strokeStyle = strokeStyle;
+ this.context.fillStyle = redrawCoords[k][4];
+ this.context.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
+ this.context.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ // Reset the redraw coords to be empty
+ redrawCoords = [];
+ }
+ /**
+ * Grouped bar
+ */
+ } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') {
+
+ var redrawCoords = [];
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+ for (j=0; j<this.data[i].length; ++j) {
+ // Set the fill and stroke colors
+ this.context.strokeStyle = strokeStyle;
+ this.context.fillStyle = colors[j];
+
+ var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;
+ var height = ((this.data[i][j] + (this.data[i][j] < 0 ? this.min : (-1 * this.min) )) / (this.max - this.min) ) * (this.canvas.height - this.gutterTop - this.gutterBottom );
+
+ // If the X axis pos is in the center, we need to half the height
+ if (xaxispos == 'center') {
+ height /= 2;
+ }
+
+ var startX = x + hmargin + (j * individualBarWidth);
+
+ /**
+ * Determine the start positioning for the bar
+ */
+ if (xaxispos == 'top') {
+ var startY = this.Get('chart.gutter.top');
+ var height = Math.abs(height);
+
+ } else if (xaxispos == 'center') {
+ var startY = this.gutterTop + (this.grapharea / 2) - height;
+
+ } else {
+ var startY = this.canvas.height - this.gutterBottom - height;
+ var height = Math.abs(height);
+ }
+
+ /**
+ * Draw MSIE shadow
+ */
+ if (RGraph.isIE8() && shadow) {
+ this.DrawIEShadow([startX, startY, individualBarWidth, height]);
+ }
+
+ this.context.strokeRect(startX, startY, individualBarWidth, height);
+ this.context.fillRect(startX, startY, individualBarWidth, height);
+ y += height;
+
+ // This bit draws the text labels that appear above the bars if requested
+ if (this.Get('chart.labels.above')) {
+
+ this.context.strokeStyle = 'rgba(0,0,0,0)';
+
+ // Turn off any shadow
+ if (shadow) {
+ RGraph.NoShadow(this);
+ }
+
+ var yPos = y - 3;
+
+ // Account for negative bars
+ if (this.data[i][j] < 0) {
+ yPos += height + 6 + (this.Get('chart.text.size') - 4);
+ }
+
+ this.context.fillStyle = this.Get('chart.text.color');
+ RGraph.Text(this.context,this.Get('chart.text.font'),typeof(this.Get('chart.labels.above.size')) == 'number' ? this.Get('chart.labels.above.size') : this.Get('chart.text.size') - 3,startX + (individualBarWidth / 2),startY - 2,RGraph.number_format(this, this.data[i][j].toFixed(this.Get('chart.labels.above.decimals'))),null,this.Get('chart.labels.above.angle') ? (this.Get('chart.labels.above.angle') > 0 ? 'right' : 'left') : 'center', null, this.Get('chart.labels.above.angle'));
+
+ // Turn any shadow back on
+ if (shadow) {
+ this.context.shadowColor = shadowColor;
+ this.context.shadowBlur = shadowBlur;
+ this.context.shadowOffsetX = shadowOffsetX;
+ this.context.shadowOffsetY = shadowOffsetY;
+ }
+ }
+
+ /**
+ * Grouped 3D effect
+ */
+ if (variant == '3d') {
+ var prevFillStyle = this.context.fillStyle;
+ var prevStrokeStyle = this.context.strokeStyle;
+
+ // Draw the top side
+ this.context.beginPath();
+ this.context.moveTo(startX, startY);
+ this.context.lineTo(startX + 10, startY - 5);
+ this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);
+ this.context.lineTo(startX + individualBarWidth, startY);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ // Draw the side section
+ this.context.beginPath();
+ this.context.moveTo(startX + individualBarWidth, startY);
+ this.context.lineTo(startX + individualBarWidth + 10, startY - 5);
+ this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
+ this.context.lineTo(startX + individualBarWidth , startY + height);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+
+ // Draw the darker top side
+ this.context.fillStyle = 'rgba(255,255,255,0.3)';
+ this.context.beginPath();
+ this.context.moveTo(startX, startY);
+ this.context.lineTo(startX + 10, startY - 5);
+ this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);
+ this.context.lineTo(startX + individualBarWidth, startY);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ // Draw the darker side section
+ this.context.fillStyle = 'rgba(0,0,0,0.4)';
+ this.context.beginPath();
+ this.context.moveTo(startX + individualBarWidth, startY);
+ this.context.lineTo(startX + individualBarWidth + 10, startY - 5);
+ this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);
+ this.context.lineTo(startX + individualBarWidth , startY + height);
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ this.context.strokeStyle = prevStrokeStyle;
+ this.context.fillStyle = prevFillStyle;
+ }
+
+ this.coords.push([startX, startY, individualBarWidth, height]);
+
+ // Facilitate shadows going to the left
+ if (this.Get('chart.shadow')) {
+ redrawCoords.push([startX, startY, individualBarWidth, height]);
+ }
+ }
+
+ /**
+ * Redraw the bar if shadows are going to the left
+ */
+ if (redrawCoords.length) {
+
+ RGraph.NoShadow(this);
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+ this.context.beginPath();
+ for (var j=0; j<redrawCoords.length; ++j) {
+
+ this.context.fillStyle = this.Get('chart.colors')[j];
+ this.context.strokeStyle = this.Get('chart.strokecolor');
+
+ this.context.fillRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
+ this.context.strokeRect(redrawCoords[j][0], redrawCoords[j][1], redrawCoords[j][2], redrawCoords[j][3]);
+ }
+ this.context.fill();
+ this.context.stroke();
+
+ redrawCoords = [];
+ }
+ }
+
+ this.context.closePath();
+ }
+
+ /**
+ * Turn off any shadow
+ */
+ RGraph.NoShadow(this);
+
+
+ /**
+ * Install the onclick event handler
+ */
+ if (this.Get('chart.tooltips')) {
+
+ // Need to register this object for redrawing
+ RGraph.Register(this);
+
+ RGraph.InstallBarTooltipEventListeners(this);
+ }
+ }
+
+ /**
+ * Draws the labels for the graph
+ */
+ RGraph.Bar.prototype.DrawLabels = function ()
+ {
+ var context = this.context;
+ var text_angle = this.Get('chart.text.angle');
+ var text_size = this.Get('chart.text.size');
+ var labels = this.Get('chart.labels');
+
+
+ // Draw the Y axis labels:
+ if (this.Get('chart.ylabels')) {
+ this.Drawlabels_top();
+ this.Drawlabels_center();
+ this.Drawlabels_bottom();
+ }
+
+ /**
+ * The X axis labels
+ */
+ if (typeof(labels) == 'object' && labels) {
+
+ var yOffset = 13 + Number(this.Get('chart.xlabels.offset'));
+
+ /**
+ * Text angle
+ */
+ var angle = 0;
+ var halign = 'center';
+
+ if (text_angle > 0) {
+ angle = -1 * text_angle;
+ halign = 'right';
+ yOffset -= 5;
+
+ if (this.Get('chart.xaxispos') == 'top') {
+ halign = 'left';
+ yOffset += 5;
+ }
+ }
+
+ // Draw the X axis labels
+ context.fillStyle = this.Get('chart.text.color');
+
+ // How wide is each bar
+ var barWidth = (RGraph.GetWidth(this) - this.gutterRight - this.gutterLeft) / labels.length;
+
+ // Reset the xTickGap
+ xTickGap = (RGraph.GetWidth(this) - this.gutterRight - this.gutterLeft) / labels.length
+
+ // Draw the X tickmarks
+ var i=0;
+ var font = this.Get('chart.text.font');
+
+ for (x=this.gutterLeft + (xTickGap / 2); x<=RGraph.GetWidth(this) - this.gutterRight; x+=xTickGap) {
+ RGraph.Text(context, font,
+ text_size,
+ x + (this.Get('chart.text.angle') == 90 ? 0 : 0),
+ this.Get('chart.xaxispos') == 'top' ? this.gutterTop - yOffset + text_size - 1: (RGraph.GetHeight(this) - this.gutterBottom) + yOffset,
+ String(labels[i++]),
+ (this.Get('chart.text.angle') == 90 ? 'center' : null),
+ halign,
+ null,
+ angle);
+ }
+ }
+ }
+
+ /**
+ * Draws the X axis at the top
+ */
+ RGraph.Bar.prototype.Drawlabels_top = function ()
+ {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+ this.context.strokeStyle = 'black';
+
+ if (this.Get('chart.xaxispos') == 'top') {
+
+ var context = this.context;
+ var interval = (this.grapharea * (1/5) );
+ var text_size = this.Get('chart.text.size');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
+ var font = this.Get('chart.text.font');
+ var numYLabels = this.Get('chart.ylabels.count');
+
+ if (this.Get('chart.ylabels.inside') == true) {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right';
+ var boxed = true;
+ } else {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : this.canvas.width - this.gutterRight + 5;
+ var boxed = false;
+ }
+
+ /**
+ * Draw specific Y labels here so that the local variables can be reused
+ */
+ if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) {
+
+ var labels = RGraph.array_reverse(this.Get('chart.ylabels.specific'));
+ var grapharea = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+
+ for (var i=0; i<labels.length; ++i) {
+
+ var y = this.gutterTop + (grapharea * (i / labels.length)) + (grapharea / labels.length);
+
+ RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
+ }
+
+ return;
+ }
+
+ // 1(ish) label
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
+
+ // 5 labels
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
+ }
+
+ // 3 labels
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight + interval, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
+ }
+ }
+
+ // 10 Y labels
+ if (numYLabels == 10) {
+
+ interval = (this.grapharea / numYLabels );
+
+ for (var i=10; i>0; --i) {
+ RGraph.Text(context, font, text_size, xpos,this.gutterTop + ((this.grapharea / numYLabels) * i),'-' + RGraph.number_format(this,((this.scale[4] / numYLabels) * i).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
+ }
+ }
+
+ /**
+ * Show the minimum value if its not zero
+ */
+ if (this.Get('chart.ymin') != 0) {
+ RGraph.Text(context,
+ font,
+ text_size,
+ xpos,
+ this.gutterTop,
+ '-' + RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post),
+ 'center',
+ align,
+ boxed);
+ }
+
+ }
+
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ /**
+ * Draws the X axis in the middle
+ */
+ RGraph.Bar.prototype.Drawlabels_center = function ()
+ {
+ var font = this.Get('chart.text.font');
+ var numYLabels = this.Get('chart.ylabels.count');
+
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ if (this.Get('chart.xaxispos') == 'center') {
+
+ /**
+ * Draw the top labels
+ */
+ var interval = (this.grapharea * (1/10) );
+ var text_size = this.Get('chart.text.size');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var context = this.context;
+ var align = '';
+ var xpos = 0;
+ var boxed = false;
+
+ this.context.fillStyle = this.Get('chart.text.color');
+ this.context.strokeStyle = 'black';
+
+ if (this.Get('chart.ylabels.inside') == true) {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right';
+ var boxed = true;
+ } else {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
+ var boxed = false;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * Draw specific Y labels here so that the local variables can be reused
+ */
+ if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) {
+
+ var labels = this.Get('chart.ylabels.specific');
+ var grapharea = this.canvas.height - this.gutterTop - this.gutterBottom;
+
+ // Draw the top halves labels
+ for (var i=0; i<labels.length; ++i) {
+
+ var y = this.gutterTop + ((grapharea / 2) / labels.length) * i;
+
+ RGraph.Text(context, font, text_size, xpos, y, String(labels[i]), 'center', align, boxed);
+ }
+
+ // Draw the bottom halves labels
+ for (var i=labels.length-1; i>=0; --i) {
+ var y = this.gutterTop + (grapharea * ( (i+1) / (labels.length * 2) )) + (grapharea / 2);
+
+ RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed);
+ }
+
+ return;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
+ }
+
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
+ }
+ } else if (numYLabels == 10) {
+ // 10Y labels
+ interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context, font, text_size, xpos,this.gutterTop + ((this.grapharea / (numYLabels * 2)) * i),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
+ }
+ }
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Draw the bottom (X axis) labels
+ */
+ var interval = (this.grapharea) / 10;
+
+ if (numYLabels == 3 || numYLabels == 5) {
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
+ }
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
+ }
+
+ RGraph.Text(context, font, text_size, xpos, this.grapharea + this.gutterTop + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
+
+ } else if (numYLabels == 10) {
+
+ // Arbitrary number of Y labels
+ interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context, font, text_size, xpos,this.gutterTop + (this.grapharea / 2) + ((this.grapharea / (numYLabels * 2)) * i) + (this.grapharea / (numYLabels * 2)),RGraph.number_format(this, ((this.scale[4] / numYLabels) * (i+1)).toFixed((this.Get('chart.scale.decimals'))), '-' + units_pre, units_post),'center', align, boxed);
+ }
+ }
+
+
+
+ /**
+ * Show the minimum value if its not zero
+ */
+ if (this.Get('chart.ymin') != 0) {
+ RGraph.Text(context,
+ font,
+ text_size,
+ xpos,
+ this.gutterTop + (this.grapharea / 2),
+ RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post),
+ 'center',
+ align,
+ boxed);
+ }
+ }
+ }
+
+ /**
+ * Draws the X axdis at the bottom (the default)
+ */
+ RGraph.Bar.prototype.Drawlabels_bottom = function ()
+ {
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+ this.context.strokeStyle = 'black';
+
+ if (this.Get('chart.xaxispos') != 'center' && this.Get('chart.xaxispos') != 'top') {
+
+ var interval = (this.grapharea * (1/5) );
+ var text_size = this.Get('chart.text.size');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var context = this.context;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
+ var font = this.Get('chart.text.font');
+ var numYLabels = this.Get('chart.ylabels.count');
+
+ if (this.Get('chart.ylabels.inside') == true) {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft + 5 : RGraph.GetWidth(this) - this.gutterRight - 5;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'left' : 'right';
+ var boxed = true;
+ } else {
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
+ var boxed = false;
+ }
+
+ /**
+ * Draw specific Y labels here so that the local variables can be reused
+ */
+ if (this.Get('chart.ylabels.specific') && typeof(this.Get('chart.ylabels.specific')) == 'object') {
+
+ var labels = this.Get('chart.ylabels.specific');
+ var grapharea = this.canvas.height - this.gutterTop - this.gutterBottom;
+
+ for (var i=0; i<labels.length; ++i) {
+ var y = this.gutterTop + (grapharea * (i / labels.length));
+
+ RGraph.Text(context, font, text_size, xpos, y, labels[i], 'center', align, boxed);
+ }
+
+ return;
+ }
+
+ // 1 label
+ if (numYLabels == 3 || numYLabels == 5) {
+
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed);
+
+ // 5 labels
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed);
+ }
+
+ // 3 labels
+ if (numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed);
+ RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed);
+ }
+ }
+
+ // 10 Y labels
+ if (numYLabels == 10) {
+
+ interval = (this.grapharea / numYLabels );
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ((this.grapharea / numYLabels) * i), RGraph.number_format(this,((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
+ }
+ }
+
+ /**
+ * Show the minimum value if its not zero
+ */
+ if (this.Get('chart.ymin') != 0) {
+ RGraph.Text(context,
+ font,
+ text_size,
+ xpos,
+ this.canvas.height - this.gutterBottom,
+ RGraph.number_format(this,(this.min.toFixed((this.Get('chart.scale.decimals')))), units_pre, units_post),
+ 'center',
+ align,
+ boxed);
+ }
+ }
+
+ this.context.fill();
+ this.context.stroke();
+ }
+
+
+ /**
+ * This function is used by MSIE only to manually draw the shadow
+ *
+ * @param array coords The coords for the bar
+ */
+ RGraph.Bar.prototype.DrawIEShadow = function (coords)
+ {
+ var prevFillStyle = this.context.fillStyle;
+ var offsetx = this.Get('chart.shadow.offsetx');
+ var offsety = this.Get('chart.shadow.offsety');
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.fillStyle = this.Get('chart.shadow.color');
+ this.context.beginPath();
+
+ // Draw shadow here
+ this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);
+
+ this.context.fill();
+
+ // Change the fillstyle back to what it was
+ this.context.fillStyle = prevFillStyle;
+ }
+
+
+ /**
+ * Not used by the class during creating the graph, but is used by event handlers
+ * to get the coordinates (if any) of the selected bar
+ *
+ * @param object e The event object
+ * @param object OPTIONAL You can pass in the bar object instead of the
+ * function getting it from the canvas
+ */
+ RGraph.Bar.prototype.getBar = function (e)
+ {
+ var canvas = e.target;
+ var obj = e.target.__object__;
+
+ if (obj.__bar__) {
+ obj = obj.__bar__;
+ }
+
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ // This facilitates you being able to pass in the bar object as a parameter instead of
+ // the function getting it from the object
+ if (arguments[1]) {
+ obj = arguments[1];
+ }
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+
+ if ( mouseX >= left
+ && mouseX <= left + width
+ && mouseY >= top
+ && mouseY <= top + height) {
+
+ return [obj, left, top, width, height, i];
+ }
+ }
+
+ return null;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.bipolar.js b/schall/static/RGraph/libraries/RGraph.bipolar.js
new file mode 100644
index 0000000..1d6883b
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.bipolar.js
@@ -0,0 +1,787 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The bi-polar/age frequency constructor.
+ *
+ * @param string id The id of the canvas
+ * @param array left The left set of data points
+ * @param array right The right set of data points
+ */
+ RGraph.Bipolar = function (id, left, right)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.canvas.__object__ = this;
+ this.type = 'bipolar';
+ this.coords = [];
+ this.max = 0;
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // The left and right data respectively
+ this.left = left;
+ this.right = right;
+ this.data = [left, right];
+
+ this.properties = {
+ 'chart.margin': 2,
+ 'chart.xtickinterval': null,
+ 'chart.labels': [],
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.title.left': '',
+ 'chart.title.right': '',
+ 'chart.gutter.center': 60,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.colors': ['#0f0'],
+ 'chart.contextmenu': null,
+ 'chart.tooltips': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.xmax': null,
+ 'chart.scale.decimals': null,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.axis.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.strokestyle': '#333'
+ }
+
+ // Pad the arrays so they're the same size
+ while (this.left.length < this.right.length) this.left.push(0);
+ while (this.left.length > this.right.length) this.right.push(0);
+ }
+
+
+ /**
+ * The setter
+ *
+ * @param name string The name of the parameter to set
+ * @param value mixed The value of the paraneter
+ */
+ RGraph.Bipolar.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * The getter
+ *
+ * @param name string The name of the parameter to get
+ */
+ RGraph.Bipolar.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * Draws the graph
+ */
+ RGraph.Bipolar.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+
+
+ // Reset the data to what was initially supplied
+ this.left = this.data[0];
+ this.right = this.data[1];
+
+
+ /**
+ * Reset the coords array
+ */
+ this.coords = [];
+
+ this.GetMax();
+ this.DrawAxes();
+ this.DrawTicks();
+ this.DrawLeftBars();
+ this.DrawRightBars();
+
+ if (this.Get('chart.axis.color') != 'black') {
+ this.DrawAxes(); // Draw the axes again (if the axes color is not black)
+ }
+
+ this.DrawLabels();
+ this.DrawTitles();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+
+ /**
+ * Install the on* event handlers
+ */
+ if (this.Get('chart.tooltips')) {
+
+
+ // Register the object so that it gets redrawn
+ RGraph.Register(this);
+
+
+ /**
+ * Install the window onclick handler
+ */
+
+ /**
+ * Install the window event handler
+ */
+ var eventHandler_window_click = function ()
+ {
+ RGraph.Redraw();
+ }
+ window.addEventListener('click', eventHandler_window_click, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', eventHandler_window_click);
+
+
+
+ /**
+ * If the cursor is over a hotspot, change the cursor to a hand
+ */
+ var eventHandler_canvas_mousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+
+ /**
+ * Get the mouse X/Y coordinates
+ */
+ var mouseCoords = RGraph.getMouseXY(e);
+ var bar = obj.getBar(e);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseX = mouseCoords[0]; // In relation to the canvas
+ var mouseY = mouseCoords[1]; // In relation to the canvas
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+
+ if (mouseX >= left && mouseX <= (left + width ) && mouseY >= top && mouseY <= (top + height) ) {
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = obj.Get('chart.tooltips')(i);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[i]) == 'function') {
+ var text = obj.Get('chart.tooltips')[i](i);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
+ var text = obj.Get('chart.tooltips')[i];
+
+ } else {
+ var text = '';
+ }
+
+ if (text) {
+ canvas.style.cursor = 'pointer';
+ }
+ return;
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', eventHandler_canvas_mousemove, false);
+ RGraph.AddEventListener(this.id, 'mouseover', eventHandler_canvas_mousemove);
+
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ var eventHandler_canvas_click = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id)
+ var obj = canvas.__object__;
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Clear(canvas);
+ obj.Draw();
+
+ /**
+ * Get the mouse X/Y coordinates
+ */
+ var mouseCoords = RGraph.getMouseXY(e);
+ var bar = obj.getBar(e);
+
+ /**
+ * This bit shows the tooltip (if required)
+ */
+ if (bar) {
+
+ var mouseX = mouseCoords[0]; // In relation to the canvas
+ var mouseY = mouseCoords[1]; // In relation to the canvas
+ var left = bar[0];
+ var top = bar[1];
+ var width = bar[2];
+ var height = bar[3];
+ var index = bar[4];
+
+ /**
+ * Show a tooltip if it's defined
+ */
+ if (obj.Get('chart.tooltips')) {
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = obj.Get('chart.tooltips')(index);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[index]) == 'function') {
+ var text = obj.Get('chart.tooltips')[index](index);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
+ var text = obj.Get('chart.tooltips')[index];
+
+ } else {
+ var text = '';
+ }
+
+ // Only now show a tooltip if one has been set
+ if (text) {
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(left, top, width, height);
+ obj.context.fillRect(left, top, width, height);
+ obj.context.stroke();
+ obj.context.fill();
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, index);
+ }
+ }
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+
+ return false;
+ }
+ this.canvas.addEventListener('click', eventHandler_canvas_click, false);
+ RGraph.AddEventListener(this.id, 'click', eventHandler_canvas_click);
+
+ // This resets the bipolar graph
+ if (RGraph.Registry.Get('chart.tooltip')) {
+ RGraph.Registry.Get('chart.tooltip').style.display = 'none';
+ RGraph.Registry.Set('chart.tooltip', null)
+ }
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Add the common .getShape() method
+ */
+ this.getShape = this.getBar;
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the axes
+ */
+ RGraph.Bipolar.prototype.DrawAxes = function ()
+ {
+ // Draw the left set of axes
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.axis.color');
+
+ this.axisWidth = (RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2;
+ this.axisHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft + this.axisWidth, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft + this.axisWidth, this.gutterTop);
+
+ this.context.stroke();
+
+ // Draw the right set of axes
+ this.context.beginPath();
+
+ var x = this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center');
+
+ this.context.moveTo(x, this.gutterTop);
+ this.context.lineTo(x, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draws the tick marks on the axes
+ */
+ RGraph.Bipolar.prototype.DrawTicks = function ()
+ {
+ var numDataPoints = this.left.length;
+ var barHeight = ( (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom)- (this.left.length * (this.Get('chart.margin') * 2) )) / numDataPoints;
+
+ // Draw the left Y tick marks
+ for (var i = RGraph.GetHeight(this) - this.gutterBottom; i >= this.gutterTop; i -= (barHeight + ( this.Get('chart.margin') * 2)) ) {
+ if (i < (RGraph.GetHeight(this) - this.gutterBottom) ) {
+ this.context.beginPath();
+ this.context.moveTo(this.gutterLeft + this.axisWidth, i);
+ this.context.lineTo(this.gutterLeft + this.axisWidth + 3, i);
+ this.context.stroke();
+ }
+ }
+
+ //Draw the right axis Y tick marks
+ for (var i = RGraph.GetHeight(this) - this.gutterBottom; i >= this.gutterTop; i -= (barHeight + ( this.Get('chart.margin') * 2)) ) {
+ if (i < (RGraph.GetHeight(this) - this.gutterBottom) ) {
+ this.context.beginPath();
+ this.context.moveTo(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center'), i);
+ this.context.lineTo(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center') - 3, i);
+ this.context.stroke();
+ }
+ }
+
+ var xInterval = this.axisWidth / 10;
+
+ // Is chart.xtickinterval specified ? If so, use that.
+ if (typeof(this.Get('chart.xtickinterval')) == 'number') {
+ xInterval = this.Get('chart.xtickinterval');
+ }
+
+
+ // Draw the left sides X tick marks
+ for (i=this.gutterLeft; i<(this.gutterLeft + this.axisWidth); i+=xInterval) {
+ this.context.beginPath();
+ this.context.moveTo(i, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(i, (RGraph.GetHeight(this) - this.gutterBottom) + 4);
+ this.context.closePath();
+
+ this.context.stroke();
+ }
+
+ // Draw the right sides X tick marks
+ var stoppingPoint = RGraph.GetWidth(this) - this.gutterRight;
+
+ for (i=(this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center') + xInterval); i<=stoppingPoint; i+=xInterval) {
+ this.context.beginPath();
+ this.context.moveTo(i, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(i, (RGraph.GetHeight(this) - this.gutterBottom) + 4);
+ this.context.closePath();
+
+ this.context.stroke();
+ }
+
+ // Store this for later
+ this.barHeight = barHeight;
+ }
+
+
+ /**
+ * Figures out the maximum value, or if defined, uses xmax
+ */
+ RGraph.Bipolar.prototype.GetMax = function()
+ {
+ var max = 0;
+ var dec = this.Get('chart.scale.decimals');
+
+ // chart.xmax defined
+ if (this.Get('chart.xmax')) {
+
+ max = this.Get('chart.xmax');
+
+ this.scale = [];
+ this.scale[0] = Number((max / 5) * 1).toFixed(dec);
+ this.scale[1] = Number((max / 5) * 2).toFixed(dec);
+ this.scale[2] = Number((max / 5) * 3).toFixed(dec);
+ this.scale[3] = Number((max / 5) * 4).toFixed(dec);
+ this.scale[4] = Number(max).toFixed(dec);
+
+ this.max = max;
+
+
+ // Generate the scale ourselves
+ } else {
+ this.leftmax = RGraph.array_max(this.left);
+ this.rightmax = RGraph.array_max(this.right);
+ max = Math.max(this.leftmax, this.rightmax);
+
+ this.scale = RGraph.getScale(max, this);
+ this.scale[0] = Number(this.scale[0]).toFixed(dec);
+ this.scale[1] = Number(this.scale[1]).toFixed(dec);
+ this.scale[2] = Number(this.scale[2]).toFixed(dec);
+ this.scale[3] = Number(this.scale[3]).toFixed(dec);
+ this.scale[4] = Number(this.scale[4]).toFixed(dec);
+
+ this.max = this.scale[4];
+ }
+
+ // Don't need to return it as it is stored in this.max
+ }
+
+
+ /**
+ * Function to draw the left hand bars
+ */
+ RGraph.Bipolar.prototype.DrawLeftBars = function ()
+ {
+ // Set the stroke colour
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ for (i=0; i<this.left.length; ++i) {
+
+ /**
+ * Turn on a shadow if requested
+ */
+ if (this.Get('chart.shadow')) {
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+ }
+
+ this.context.beginPath();
+
+ // Set the colour
+ if (this.Get('chart.colors')[i]) {
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ }
+
+ /**
+ * Work out the coordinates
+ */
+ var width = ( (this.left[i] / this.max) * this.axisWidth);
+ var coords = [
+ this.gutterLeft + this.axisWidth - width,
+ this.gutterTop + (i * ( this.axisHeight / this.left.length)) + this.Get('chart.margin'),
+ width,
+ this.barHeight
+ ];
+
+ // Draw the IE shadow if necessary
+ if (RGraph.isIE8() && this.Get('chart.shadow')) {
+ this.DrawIEShadow(coords);
+ }
+
+
+ this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]);
+ this.context.fillRect(coords[0], coords[1], coords[2], coords[3]);
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Add the coordinates to the coords array
+ */
+ this.coords.push([
+ coords[0],
+ coords[1],
+ coords[2],
+ coords[3]
+ ]);
+ }
+
+ /**
+ * Turn off any shadow
+ */
+ RGraph.NoShadow(this);
+ }
+
+
+ /**
+ * Function to draw the right hand bars
+ */
+ RGraph.Bipolar.prototype.DrawRightBars = function ()
+ {
+ // Set the stroke colour
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ /**
+ * Turn on a shadow if requested
+ */
+ if (this.Get('chart.shadow')) {
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+ }
+
+ for (var i=0; i<this.right.length; ++i) {
+ this.context.beginPath();
+
+ // Set the colour
+ if (this.Get('chart.colors')[i]) {
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ }
+
+
+ var width = ( (this.right[i] / this.max) * this.axisWidth);
+ var coords = [
+ this.gutterLeft + this.axisWidth + this.Get('chart.gutter.center'),
+ this.Get('chart.margin') + (i * (this.axisHeight / this.right.length)) + this.gutterTop,
+ width,
+ this.barHeight
+ ];
+
+ // Draw the IE shadow if necessary
+ if (RGraph.isIE8() && this.Get('chart.shadow')) {
+ this.DrawIEShadow(coords);
+ }
+ this.context.strokeRect(coords[0], coords[1], coords[2], coords[3]);
+ this.context.fillRect(coords[0], coords[1], coords[2], coords[3]);
+
+ this.context.closePath();
+
+ /**
+ * Add the coordinates to the coords array
+ */
+ this.coords.push([
+ coords[0],
+ coords[1],
+ coords[2],
+ coords[3]
+ ]);
+ }
+
+ this.context.stroke();
+
+ /**
+ * Turn off any shadow
+ */
+ RGraph.NoShadow(this);
+ }
+
+
+ /**
+ * Draws the titles
+ */
+ RGraph.Bipolar.prototype.DrawLabels = function ()
+ {
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ var labelPoints = new Array();
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+
+ var max = Math.max(this.left.length, this.right.length);
+
+ for (i=0; i<max; ++i) {
+ var barAreaHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+ var barHeight = barAreaHeight / this.left.length;
+ var yPos = (i * barAreaHeight) + this.gutterTop;
+
+ labelPoints.push(this.gutterTop + (i * barHeight) + (barHeight / 2) + 5);
+ }
+
+ for (i=0; i<labelPoints.length; ++i) {
+ RGraph.Text(this.context,
+ this.Get('chart.text.font'),
+ this.Get('chart.text.size'),
+ this.gutterLeft + this.axisWidth + (this.Get('chart.gutter.center') / 2),
+ labelPoints[i],
+ String(this.Get('chart.labels')[i] ? this.Get('chart.labels')[i] : ''),
+ null,
+ 'center');
+ }
+
+ // Now draw the X labels for the left hand side
+ RGraph.Text(this.context,font,size,this.gutterLeft,RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')),null,'center');
+ RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (1/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (2/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (3/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, this.gutterLeft + ((RGraph.GetWidth(this) - this.Get('chart.gutter.center') - this.gutterLeft - this.gutterRight) / 2) * (4/5), RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+
+ // Now draw the X labels for the right hand side
+ RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom + 14, RGraph.number_format(this, this.scale[4], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.2), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[3], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.4), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[2], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.6), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[1], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ RGraph.Text(this.context, font, size, RGraph.GetWidth(this) - this.gutterRight - (this.axisWidth * 0.8), RGraph.GetHeight(this) - this.gutterBottom + 14,RGraph.number_format(this, this.scale[0], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');
+ }
+
+ /**
+ * Draws the titles
+ */
+ RGraph.Bipolar.prototype.DrawTitles = function ()
+ {
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.gutterLeft + 5, (this.gutterTop / 2) + 5, String(this.Get('chart.title.left')), 'center');
+ RGraph.Text(this.context,this.Get('chart.text.font'), this.Get('chart.text.size'), RGraph.GetWidth(this) - this.gutterRight - 5, (this.gutterTop / 2) + 5, String(this.Get('chart.title.right')), 'center', 'right');
+
+ // Draw the main title for the whole chart
+ RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : null);
+ }
+
+
+ /**
+ * This function is used by MSIE only to manually draw the shadow
+ *
+ * @param array coords The coords for the bar
+ */
+ RGraph.Bipolar.prototype.DrawIEShadow = function (coords)
+ {
+ var prevFillStyle = this.context.fillStyle;
+ var offsetx = this.Get('chart.shadow.offsetx');
+ var offsety = this.Get('chart.shadow.offsety');
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.fillStyle = this.Get('chart.shadow.color');
+ this.context.beginPath();
+
+ // Draw shadow here
+ this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2],coords[3]);
+
+ this.context.fill();
+
+ // Change the fillstyle back to what it was
+ this.context.fillStyle = prevFillStyle;
+ }
+
+
+ /**
+ * Returns the appropriate focussed bar coordinates
+ *
+ * @param e object The event object
+ */
+ RGraph.Bipolar.prototype.getBar = function (e)
+ {
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+
+ if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
+ return [left,top,width,height,i];
+ }
+ }
+
+ return null;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.adjusting.js b/schall/static/RGraph/libraries/RGraph.common.adjusting.js
new file mode 100644
index 0000000..6935b0e
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.adjusting.js
@@ -0,0 +1,1167 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+ RGraph.AllowAdjusting = function (obj)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ RGraph.Register(obj);
+
+ if (obj.type == 'thermometer') {
+ var canvas_onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = obj.coords;
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ if ( mouseX > obj.coords[0]
+ && mouseX < (obj.coords[0] + obj.coords[2])
+ ) {
+
+ RGraph.Registry.Set('chart.thermometer.' + id + '.resizing', true);
+ obj.canvas.style.cursor = 'ns-resize';
+
+ /**
+ * Fire the RGraph event
+ */
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin');
+
+ // Fire the onmousemove handler
+ canvas_onmousemove(e);
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+
+ var canvas_onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = obj.coords;
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ if (RGraph.Registry.Get('chart.thermometer.' + id + '.resizing')) {
+
+ var capRadius = (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight) / 2;
+ var top = obj.gutterTop + capRadius;
+ var bottom = coords[1] + coords[3];
+ var newvalue = obj.max - ((mouseY - top) / (bottom - obj.gutterTop - capRadius)) * (obj.max - obj.min);
+
+ obj.value = Math.round(newvalue);
+
+ /**
+ * Bounds checking
+ */
+ if (obj.value > obj.max) {
+ obj.value = obj.max;
+ } else if (obj.value < obj.min) {
+ obj.value = obj.min;
+ }
+
+ RGraph.Clear(canvas);
+ obj.Draw();
+
+ /**
+ * Fire the RGraph event
+ */
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjust');
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+
+ var canvas_onmouseup = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = obj.coords;
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ RGraph.Registry.Set('chart.thermometer.' + id + '.resizing', false);
+ obj.canvas.style.cursor = 'default';
+
+ /**
+ * Fire the RGraph event
+ */
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } else if (obj.type == 'line') {
+ var canvas_onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = obj.coords;
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ RGraph.Redraw();
+
+ for (var i=0; i<coords.length; ++i) {
+
+ if ( mouseCoords[0] > coords[i][0] - 5
+ && mouseCoords[1] > coords[i][1] - 5
+ && mouseCoords[0] < coords[i][0] + 5
+ && mouseCoords[1] < coords[i][1] + 5
+ ) {
+
+ var numDataSeries = obj.original_data.length;
+ var numDataPoints = obj.original_data[0].length;
+ var data_series = i / numDataPoints;
+ data_series = Math.floor(data_series);
+
+
+
+ canvas.style.cursor = 'ns-resize';
+ RGraph.FireCustomEvent(obj, 'onadjustbegin');
+ RGraph.Registry.Set('chart.adjusting.line.' + id, [obj, i, [coords[i][0], coords[i][1]], data_series]);
+
+ return;
+ }
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+ var id = e.target.__object__.id;
+
+ var state = RGraph.Registry.Get('chart.adjusting.line.' + id);
+
+ if (state) {
+ var obj = state[0];
+ var idx = state[1];
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var data_series = state[3];
+ var points = obj.original_data[data_series];
+ var mouseCoords = RGraph.getMouseXY(e);
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+ var h = RGraph.GetHeight(obj);
+
+
+ // Don't allow adjusting to the gutter if out-of-bounds is NOT specified
+ if (!obj.Get('chart.outofbounds')) {
+ if (y >= (h - obj.gutterBottom)) {
+ y = h - obj.gutterBottom;
+ } else if (y <= obj.gutterTop) {
+ y = obj.gutterTop;
+ }
+ }
+
+ // This accounts for a center X axis
+ if (obj.Get('chart.xaxispos') == 'center') {
+ y *= 2;
+ y -= (obj.gutterTop / 2);
+ } else if (obj.Get('chart.xaxispos') == 'top') {
+ y = RGraph.GetHeight(obj) - y;
+ }
+
+ var pos = h - obj.gutterTop - obj.gutterBottom;
+ pos = pos - (y - obj.gutterTop);
+ var value = (obj.max / (h - obj.gutterTop - obj.gutterBottom)) * pos;
+
+ if (obj.Get('chart.ylabels.invert')) {
+ value = obj.max - value;
+ }
+
+
+
+ // Adjust the index so that it's applicable to the correct data series
+ for (var i=0; i<data_series; ++i) {
+ idx -= obj.original_data[0].length;
+ }
+
+ obj.original_data[data_series][idx] = value;
+
+ obj.Set('chart.ymax', obj.max);
+ canvas.style.cursor = 'ns-resize';
+ RGraph.Redraw();
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+
+ return;
+
+ } else {
+
+ var canvas = e.target;
+ var context = canvas.__object__.context;
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ if ( x > obj.coords[i][0] - 5
+ && y > obj.coords[i][1] - 5
+ && x < obj.coords[i][0] + 5
+ && y < obj.coords[i][1] + 5
+ ) {
+
+ canvas.style.cursor = 'ns-resize';
+ return;
+ }
+ }
+ }
+
+ e.target.style.cursor = null;
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ if (RGraph.Registry.Get('chart.adjusting.line.' + id)) {
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+
+ RGraph.Registry.Set('chart.adjusting.line.' + id, null);
+ e.target.style.cursor = null;
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+ /**
+ * HProgress bar
+ */
+ } else if (obj.type == 'hprogress') {
+
+
+ var canvas_onmousedown = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ RGraph.Registry.Set('chart.adjusting.hprogress.' + id, [true]);
+
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin');
+
+ canvas_onmousemove(e);
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ var id = e.target.__object__.id;
+ var state = RGraph.Registry.Get('chart.adjusting.hprogress.' + id);
+
+ if (state && state.length) {
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ if (obj.type == 'hprogress') {
+
+ var coords = RGraph.getMouseXY(e);
+ coords[0] = Math.max(0, coords[0] - obj.gutterLeft);
+ var barWidth = canvas.width - obj.gutterLeft - obj.gutterRight;
+
+ // Work out the new value
+ var value = (coords[0] / barWidth) * (obj.max - obj.Get('chart.min'));
+ value += obj.Get('chart.min');
+
+ obj.value = Math.max(0, value.toFixed());
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+/*
+ } else if (obj.type == 'vprogress') {
+
+ var coords = RGraph.getMouseXY(e);
+ coords[1] = Math.max(0, coords[1] - obj.Get('chart.gutter'));
+ var barHeight = canvas.height - (2 * obj.Get('chart.gutter'));
+
+ // Work out the new value
+ var value = ( (barHeight - coords[1]) / barHeight) * obj.max;
+
+ obj.value = Math.max(0, value.toFixed());
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+*/
+ }
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ if (RGraph.Registry.Get('chart.adjusting.hprogress.' + id)) {
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+
+ RGraph.Registry.Set('chart.adjusting.hprogress.' + id, null);
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+ /**
+ * VProgress bar
+ */
+ } else if (obj.type == 'vprogress') {
+
+
+ var canvas_onmousedown = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ RGraph.Registry.Set('chart.adjusting.vprogress.' + id, [true]);
+
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin');
+
+ canvas_onmousemove(e);
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ var id = e.target.__object__.id;
+ var state = RGraph.Registry.Get('chart.adjusting.vprogress.' + id);
+
+ if (state && state.length) {
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ if (obj.type == 'hprogress') {
+
+ var coords = RGraph.getMouseXY(e);
+ coords[0] = Math.max(0, coords[0] - obj.gutterLeft);
+ var barWidth = canvas.width - obj.gutterLeft - obj.gutterRight;
+
+ // Work out the new value
+ var value = (coords[0] / barWidth) * (obj.max - obj.Get('chart.min'));
+ value += obj.Get('chart.min');
+
+ obj.value = Math.max(0, value.toFixed(obj.Get('chart.scale.decimals')));
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ } else if (obj.type == 'vprogress') {
+
+ var coords = RGraph.getMouseXY(e);
+ coords[1] = Math.max(0, coords[1] - obj.gutterTop);
+ var barHeight = canvas.height - obj.gutterTop - obj.gutterBottom;
+
+ // Work out the new value
+ var value = ( (barHeight - coords[1]) / barHeight) * obj.max;
+
+ obj.value = Math.max(0, value.toFixed(obj.Get('chart.scale.decimals')));
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ if (RGraph.Registry.Get('chart.adjusting.vprogress.' + id)) {
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+
+ RGraph.Registry.Set('chart.adjusting.vprogress.' + id, null);
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+ /**
+ * Rose chart
+ */
+ } else if (obj.type == 'rose') {
+
+
+ obj.Set('chart.ymax', obj.max);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = RGraph.getMouseXY(e);
+ var segment = RGraph.Registry.Get('chart.adjusting.rose.' + id);
+ var x = Math.abs(coords[0] - obj.centerx);
+ var y = Math.abs(coords[1] - obj.centery);
+ var theta = Math.atan(y / x) * (180 / Math.PI); // theta is now in DEGREES
+
+
+ // Account for the correct quadrant
+ if (coords[0] >= obj.centerx && coords[1] < obj.centery) {
+ theta = 90 - theta;
+ } else if (coords[0] >= obj.centerx && coords[1] >= obj.centery) {
+ theta += 90;
+ } else if (coords[0] < obj.centerx && coords[1] >= obj.centery) {
+ theta = 90 - theta;
+ theta = 180 + theta;
+
+ } else if (coords[0] < obj.centerx && coords[1] < obj.centery) {
+ theta = theta + 270;
+ }
+
+ var Opp = y;
+ var Adj = x;
+ var Hyp = Math.abs(Adj / Math.sin(theta / (180 / Math.PI)));
+
+ for (var i=0; i<obj.angles.length; ++i) {
+ if (
+ theta > obj.angles[i][0]
+ && theta < obj.angles[i][1]) {
+
+ if (RGraph.Registry.Get('chart.adjusting.rose.' + id) && i == segment[6]) {
+ var newvalue = (Hyp / (obj.radius - 25) ) * obj.max;
+ obj.data[i] = Math.min(newvalue, obj.max);
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+ }
+
+ if (Hyp <= (obj.angles[i][2] + 5) && Hyp >= (obj.angles[i][2] - 5) ) {
+ canvas.style.cursor = 'move';
+ return false;
+
+ } else if (obj.Get('chart.tooltips') && Hyp <= (obj.angles[i][2] - 5) ) {
+ canvas.style.cursor = 'pointer';
+ return false;
+ }
+
+ }
+ }
+
+ canvas.style.cursor = 'default';
+
+ return;
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+ var canvas_onmousedown = function (e)
+ {
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = RGraph.getMouseXY(e);
+ var segment = obj.getSegment(e, 5);
+
+ if (segment && segment.length && !RGraph.Registry.Get('chart.adjusting.rose.' + id)) {
+ var x = Math.abs(coords[0] - obj.centerx);
+ var y = Math.abs(coords[1] - obj.centery);
+ var a = Math.atan(y / x) * (180 / Math.PI); // a is now in DEGREES
+
+ // Account for the correct quadrant
+ if (coords[0] >= obj.centerx && coords[1] < obj.centery) {
+ a = 90 - a;
+ a += 270;
+ } else if (coords[0] >= obj.centerx && coords[1] >= obj.centery) {
+ // Nada
+ } else if (coords[0] < obj.centerx && coords[1] >= obj.centery) {
+ a = 90 - a;
+ a += 90;
+ } else if (coords[0] < obj.centerx && coords[1] < obj.centery) {
+ a += 180;
+ }
+
+ var hyp = Math.abs(y / Math.sin(a / 57.3));
+
+ if (hyp >= (segment[2] - 10) ) {
+
+ /**
+ * Hide any currently shown tooltip
+ */
+ if (RGraph.Registry.Get('chart.tooltip')) {
+ RGraph.Registry.Get('chart.tooltip').style.display = 'none';
+ RGraph.Registry.Set('chart.tooltip', null);
+ }
+
+ RGraph.Registry.Set('chart.adjusting.rose.' + id, segment);
+
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustbegin');
+
+ e.stopPropagation();
+ }
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var obj = e.target.__object__;
+ var id = obj.id;
+
+ if (RGraph.Registry.Get('chart.adjusting.rose.' + id)) {
+
+
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ RGraph.Registry.Set('chart.adjusting.rose.' + id, null);
+ e.stopPropagation();
+
+ return false;
+ }
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * Bar chart
+ */
+ } else if (obj.type == 'bar') {
+
+ // Stacked bar charts not supported
+ if (obj.Get('chart.grouping') == 'stacked') {
+ alert('[BAR] Adjusting stacked bar charts is not supported');
+ return;
+ }
+
+
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+
+ var canvas_onmousemove = function (e)
+ {
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouse = RGraph.getMouseXY(e);
+ var mousex = mouse[0];
+ var mousey = mouse[1]; // mousey, mousey...
+
+
+ // Loop through the coords to see if the mouse position is at the top of a bar
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ var barX = obj.coords[i][0];
+ var barY = obj.coords[i][1];
+ var barW = obj.coords[i][2];
+ var barH = obj.coords[i][3];
+
+ if (mousex > barX && mousex < (barX + barW)) {
+
+ /**
+ * Change the mouse pointer
+ */
+ //if ( (obj.Get('chart.xaxispos') == 'bottom' && (mousey > (barY - 5) && mousey < (barY + 5)))
+ // || (((obj.Get('chart.xaxispos') == 'center' || obj.Get('chart.xaxispos') == 'top') && mousey > (barY + barH - 5) && mousey < (barY + barH + 5)))
+ // || ((obj.Get('chart.xaxispos') == 'center' && barX < (obj.canvas.height / 2) && mousey < (barY + 5) && mousey > barY))
+ // ) {
+ // canvas.style.cursor = 'ns-resize';
+
+ //} else {
+ // canvas.style.cursor = 'default';
+ //}
+
+
+
+ var idx = RGraph.Registry.Get('chart.adjusting.bar.' + id)
+
+ if (typeof(idx) == 'number') {
+
+ // This accounts for a center X axis
+ if (obj.Get('chart.xaxispos') == 'center') {
+ obj.grapharea /= 2;
+ }
+
+ var newheight = obj.grapharea - (mousey - obj.gutterTop);
+ var newvalue = (newheight / obj.grapharea) * obj.max;
+
+ // Account for X axis at the top
+ if (obj.Get('chart.xaxispos') == 'top') {
+ newvalue = obj.max - newvalue;
+ }
+
+ // Top and bottom boundaries
+ if (newvalue > obj.max) newvalue = obj.max;
+
+ if (obj.Get('chart.xaxispos') == 'center') {
+ if (newvalue < (-1 * obj.max)) newvalue = (-1 * obj.max);
+
+ } else {
+ if (newvalue < 0) newvalue = 0;
+ }
+
+ ///////////////// This was fun to work out... /////////////////
+
+ var j, index;
+
+ for (var j=0, index=0; j<obj.data.length; ++j,++index) {
+
+ if (typeof(obj.data[j]) == 'object') {
+
+ for (var k=0; k<obj.data[j].length && index <= idx; ++k, ++index) {
+ if (index == idx) {
+
+
+ if (obj.Get('chart.xaxispos') == 'top') {
+ newvalue *= -1;
+ }
+
+ obj.data[j][k] = newvalue;
+ var b = true;
+ break;
+ }
+ }
+
+ --index;
+ } else if (typeof(obj.data[j]) == 'number') {
+
+ if (index == idx) {
+
+ if (obj.Get('chart.xaxispos') == 'top') {
+ newvalue *= -1;
+ }
+
+ obj.data[j] = newvalue;
+
+ // No need to set b
+ break;
+ }
+ }
+
+ if (b) {
+ break;
+ }
+ }
+ ///////////////////////////////////////////////////////////////
+
+ RGraph.Clear(canvas);
+ obj.Draw();
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+ }
+
+ return;
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+
+ var canvas_onmousedown = function (e)
+ {
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouse = RGraph.getMouseXY(e);
+ var mousex = mouse[0];
+ var mousey = mouse[1];
+
+ // Loop through the coords to see if the mouse position is at the top of a bar
+ for (var i=0; i<obj.coords.length; ++i) {
+ if (mousex > obj.coords[i][0] && mousex < (obj.coords[i][0] + obj.coords[i][2])) {
+
+ RGraph.FireCustomEvent(obj, 'onadjustbegin');
+
+ obj.Set('chart.ymax', obj.max);
+ RGraph.Registry.Set('chart.adjusting.bar.' + id, i);
+
+ canvas_onmousemove(e);
+ }
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var id = e.target.__object__.id;
+
+ if (typeof(RGraph.Registry.Get('chart.adjusting.bar.' + id)) == 'number') {
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+
+ RGraph.Registry.Set('chart.adjusting.bar.' + id, null);
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * The Radar chart
+ */
+ } else if (obj.type == 'radar') {
+
+
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+
+ var canvas_onmousemove = function (e)
+ {
+ var id = e.target.id;
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseDown = RGraph.Registry.Get('chart.adjusting.radar.' + id);
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ if (mouseDown) {
+
+ canvas.style.cursor = 'move';
+
+ var dataset = mouseDown[0];
+ var index = mouseDown[1];
+ var dx = mouseCoords[0] - obj.centerx;
+ var dy = mouseCoords[1] - obj.centery;
+ var hyp = Math.sqrt((dx * dx) + (dy * dy));
+ var newvalue = (hyp / (obj.size / 2)) * obj.max;
+
+ newvalue = Math.min(obj.max, newvalue);
+ newvalue = Math.max(0, newvalue);
+
+ obj.data[mouseDown[0]][mouseDown[1]] = newvalue;
+ RGraph.Clear(canvas);
+ obj.Draw();
+
+ /**
+ * Fire the onadjust event
+ */
+ RGraph.FireCustomEvent(obj, 'onadjust');
+
+ } else {
+
+ // Determine if the mouse is near a point, and if so, change the pointer
+ for (var ds = 0; ds<obj.coords.length; ds++) {
+ for (var i=0; i<obj.coords[ds].length; ++i) {
+
+ var dx = Math.abs(mouseCoords[0] - obj.coords[ds][i][0]);
+ var dy = Math.abs(mouseCoords[1] - obj.coords[ds][i][1]);
+ var a = Math.atan(dy / dx);
+
+
+ var hyp = Math.sqrt((dx * dx) + (dy * dy));
+
+ if (hyp <= 5) {
+ canvas.style.cursor = 'move';
+ return;
+ }
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+ var canvas_onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var id = obj.id;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+
+
+ // Determine if the mouse is near a point
+ for (var j=0; j<obj.coords.length; ++j) {
+ for (var i=0; i<obj.coords[j].length; ++i) {
+
+ var dx = Math.abs(mouseCoords[0] - obj.coords[j][i][0]);
+ var dy = Math.abs(mouseCoords[1] - obj.coords[j][i][1]);
+ var a = Math.atan(dy / dx);
+
+
+ var hyp = Math.sqrt((dx * dx) + (dy * dy));
+
+ if (hyp <= 5) {
+ canvas.style.cursor = 'pointer';
+ RGraph.FireCustomEvent(obj, 'onadjustbegin');
+
+ RGraph.Registry.Set('chart.adjusting.radar.' + id, [j, i, obj.coords[j][i][0] > obj.centerx, obj.coords[j][i][1] > obj.centery]);
+ return;
+ }
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ var canvas_onmouseup = function (e)
+ {
+ var id = e.target.id;
+
+ if (RGraph.Registry.Get('chart.adjusting.radar.' + id)) {
+ RGraph.FireCustomEvent(e.target.__object__, 'onadjustend');
+ }
+
+ RGraph.Registry.Set('chart.adjusting.radar.' + id, null);
+ canvas.style.cursor = 'default';
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+
+ var canvas_onmouseout = function (e)
+ {
+ canvas_onmouseup(e);
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+ /**
+ * Gantt chart
+ */
+ } else if (obj.type == 'gantt') {
+
+
+ /**
+ * The onmousedown event handler
+ */
+ var canvas_onmousedown = function (e)
+ {
+ var canvas = e.target;
+ var id = canvas.id;
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ var coordX = obj.coords[i][0];
+ var coordY = obj.coords[i][1];
+ var coordW = obj.coords[i][2];
+ var coordH = obj.coords[i][3];
+
+ if (mouseX > coordX
+ && mouseX < (coordX + coordW)
+ && mouseY > coordY
+ && mouseY < (coordY + coordH)
+ ) {
+
+ var mode = (mouseX >= (coordX + coordW - 5) ? 'resize' : 'move');
+
+ RGraph.Registry.Set('chart.adjusting.gantt', {'index': i,'object': obj,'mousex': mouseX,'mousey': mouseY,'event_start': obj.Get('chart.events')[i][0],'event_duration': obj.Get('chart.events')[i][1],'mode': mode});
+
+ RGraph.FireCustomEvent(obj, 'onadjustbegin');
+ return;
+ }
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ /**
+ * Change the pointer
+ */
+ var canvas_onmousemove = function (e)
+ {
+ var canvas = e.target;
+ var id = canvas.id;
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ var coordX = obj.coords[i][0];
+ var coordY = obj.coords[i][1];
+ var coordW = obj.coords[i][2];
+ var coordH = obj.coords[i][3];
+
+ if (mouseX > coordX
+ && mouseX < (coordX + coordW)
+ && mouseY > coordY
+ && mouseY < (coordY + coordH)
+ ) {
+
+ canvas.style.cursor = 'ew-resize';
+ return;
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+
+
+
+
+
+
+ var window_onmousemove = function (e)
+ {
+ var conf = RGraph.Registry.Get('chart.adjusting.gantt');
+
+ if (conf) {
+
+ var obj = conf['object'];
+ var id = obj.id;
+ var index = conf['index'];
+ var startX = conf['mousex'];
+ var startY = conf['mousey'];
+ var eventStart = conf['event_start'];
+ var duration = conf['event_duration'];
+ var mode = conf['mode'];
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ RGraph.FireCustomEvent(obj, 'onadjust');
+
+ if (mode == 'resize') {
+
+ /**
+ * Account for the right hand gutter. Appears to be a FF bug
+ */
+ if (mouseX > (RGraph.GetWidth(obj) - obj.gutterRight)) {
+ mouseX = RGraph.GetWidth(obj) - obj.gutterRight;
+ }
+
+
+ var diff = ((mouseX - startX) / (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight)) * obj.Get('chart.xmax');
+ diff = Math.round(diff);
+
+ obj.Get('chart.events')[index][1] = duration + diff;
+
+ if (obj.Get('chart.events')[index][1] < 0) {
+ obj.Get('chart.events')[index][1] = 1;
+ }
+
+ } else {
+
+ var diff = ((mouseX - startX) / (RGraph.GetWidth(obj) - obj.gutterLeft - obj.gutterRight)) * obj.Get('chart.xmax');
+ diff = Math.round(diff);
+
+ if ( eventStart + diff > 0
+ && (eventStart + diff + obj.Get('chart.events')[index][1]) < obj.Get('chart.xmax')) {
+
+ obj.Get('chart.events')[index][0] = eventStart + diff;
+
+ } else if (eventStart + diff < 0) {
+ obj.Get('chart.events')[index][0] = 0;
+
+ } else if ((eventStart + diff + obj.Get('chart.events')[index][1]) > obj.Get('chart.xmax')) {
+ obj.Get('chart.events')[index][0] = obj.Get('chart.xmax') - obj.Get('chart.events')[index][1];
+ }
+ }
+
+ RGraph.Redraw();
+ RGraph.FireCustomEvent(obj, 'onadjust');
+ }
+ }
+ window.addEventListener('mousemove', window_onmousemove, false);
+ RGraph.AddEventListener('window_' + canvas.id, 'mousemove', window_onmousemove);
+
+
+
+
+
+
+
+
+
+
+ var window_onmouseup = function (e)
+ {
+ if (RGraph.Registry.Get('chart.adjusting.gantt')) {
+
+ var conf = RGraph.Registry.Get('chart.adjusting.gantt');
+ var obj = conf['object'];
+ var id = obj.id;
+
+ RGraph.FireCustomEvent(obj, 'onadjustend');
+ RGraph.Registry.Set('chart.adjusting.gantt', null);
+ }
+ }
+ window.addEventListener('mouseup', window_onmouseup, false);
+ RGraph.AddEventListener('window_' + canvas.id, 'mouseup', window_onmouseup);
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.annotate.js b/schall/static/RGraph/libraries/RGraph.common.annotate.js
new file mode 100644
index 0000000..450cf06
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.annotate.js
@@ -0,0 +1,340 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+
+ /**
+ * The function which controls the annotate feature
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Annotate = function (obj)
+ {
+ /**
+ * This installs some event handlers
+ */
+ if (obj.Get('chart.annotatable')) {
+
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ /**
+ * Capture the mouse events so we can set whther the mouse is down or not
+ */
+ var canvas_onmousedown = function (e)
+ {
+ if (e.button == 0) {
+
+ e.target.__object__.Set('chart.mousedown', true);
+
+ // Get the context
+ var obj = e.target.__object__;
+ var context = obj.context;
+
+ // Don't want any "joining" lines or colour "bleeding"
+ context.beginPath();
+
+ // Accommodate Chrome
+ var coords = RGraph.getMouseXY(e);
+ var x = coords[0];
+ var y = coords[1];
+
+ // Clear the annotation recording
+ RGraph.Registry.Set('annotate.actions', [obj.Get('chart.annotate.color')]);
+
+ context.strokeStyle = obj.Get('chart.annotate.color');
+
+ context.moveTo(x, y);
+
+ // Set the lineWidth
+ context.lineWidth = 1;
+
+ RGraph.Registry.Set('started.annotating', false);
+ RGraph.Registry.Set('chart.annotating', obj);
+
+ /**
+ * Fire the onannotatebegin event. It also fires an event called ononnotatestart for BC purposes
+ */
+ RGraph.FireCustomEvent(obj, 'onannotatestart');
+ RGraph.FireCustomEvent(obj, 'onannotatebegin');
+ }
+
+ return false;
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+ /**
+ * This cancels annotating for ALL canvases
+ */
+ var window_onmouseup = function (e)
+ {
+ var obj = RGraph.Registry.Get('chart.annotating');
+ var tags = document.getElementsByTagName('canvas');
+
+ for (var i=0; i<tags.length; ++i) {
+ if (tags[i].__object__) {
+ tags[i].__object__.Set('chart.mousedown', false);
+ }
+ }
+
+ if (e.button != 0 || !obj) {
+ return;
+ }
+
+ // Store the annotations in browser storage if it's available
+ if (RGraph.Registry.Get('annotate.actions') && RGraph.Registry.Get('annotate.actions').length > 0 && window.localStorage) {
+
+ var id = '__rgraph_annotations_' + e.target.id + '__';
+ var annotations = window.localStorage[id] ? window.localStorage[id] + '|' : '';
+ annotations += RGraph.Registry.Get('annotate.actions');
+
+ // Store the annotations information in HTML5 browser storage here
+ window.localStorage[id] = annotations;
+ }
+
+ // Clear the recorded annotations
+ RGraph.Registry.Set('annotate.actions', []);
+
+ /**
+ * Fire the annotate event
+ */
+
+ RGraph.FireCustomEvent(obj, 'onannotateend');
+ }
+ window.addEventListener('mouseup', window_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'window_mouseup', window_onmouseup);
+
+ /**
+ * Canvas onmouseup event
+ */
+ var canvas_onmouseup = function (e)
+ {
+ var obj = e.target.__object__;
+
+ RGraph.Registry.Set('chart.mousedown', false);
+ }
+ canvas.addEventListener('mouseup', canvas_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'mouseup', canvas_onmouseup);
+
+ /**
+ * The canvas onmousemove function
+ */
+ var canvas_onmousemove = function (e)
+ {
+ var e = RGraph.FixEventObject(e);
+ var obj = e.target.__object__;
+ var coords = RGraph.getMouseXY(e);
+ var x = coords[0];
+ var y = coords[1];
+ var width = canvas.width;
+ var height = canvas.height;
+
+ obj.context.lineWidth = 1;
+
+ // Don't allow annotating in the gutter
+ //
+ // CHANGED 20TH DECEMBER 2010 TO ALLOW ANNOTATING IN THE GUTTER
+ if (true) {
+
+ canvas.style.cursor = 'crosshair';
+
+ if (obj.Get('chart.mousedown')) {
+
+ // Special case for HBars and Gantts with their extra wide left gutter
+ if ( (obj.type != 'hbar' && obj.type != 'gantt') || x > obj.gutterLeft) {
+
+ /**
+ * This is here to stop annotating in the gutter
+ */
+ if (RGraph.Registry.Get('started.annotating') == false) {
+ context.moveTo(x, y);
+ RGraph.Registry.Set('started.annotating', true)
+ }
+
+ context.lineTo(x, y);
+
+ RGraph.Registry.Set('annotate.actions', RGraph.Registry.Get('annotate.actions') + '|' + x + ',' + y);
+
+ context.stroke();
+
+ /**
+ * Fire the annotate event
+ */
+ RGraph.FireCustomEvent(obj, 'onannotate');
+ }
+ }
+
+ } else {
+ canvas.style.cursor = 'default';
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+ RGraph.ReplayAnnotations(obj);
+ }
+ }
+
+
+ /**
+ * Shows the mini palette used for annotations
+ *
+ * @param object e The event object
+ */
+ RGraph.Showpalette = function (e)
+ {
+ var isSafari = navigator.userAgent.indexOf('Safari') ? true : false;
+
+ e = RGraph.FixEventObject(e);
+
+ var canvas = e.target.parentNode.__canvas__;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var div = document.createElement('DIV');
+ var coords = RGraph.getMouseXY(e);
+
+ div.__object__ = obj; // The graph object
+ div.className = 'RGraph_palette';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = 'white';
+ div.style.border = '1px solid black';
+ div.style.left = 0;
+ div.style.top = 0;
+ div.style.padding = '3px';
+ div.style.paddingBottom = 0;
+ div.style.paddingRight = 0;
+ div.style.opacity = 0;
+ div.style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';
+ div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';
+ div.style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';
+ div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)';
+
+ var common_css = 'padding: 1px; display: inline; display: inline-block; width: 15px; height: 15px; margin-right: 3px; cursor: pointer;' + (isSafari ? 'margin-bottom: 3px' : '');
+ var common_mouseover = ' onmouseover="this.style.border = \'1px black solid\'; this.style.padding = 0"';
+ var common_mouseout = ' onmouseout="this.style.border = 0; this.style.padding = \'1px\'" ';
+
+ var str = '';
+
+ var colors = ['red', 'blue', 'green', 'black', 'yellow', 'magenta', 'pink', 'cyan', 'purple', '#ddf', 'gray', '#36905c'];
+
+ for (i=0; i<colors.length; ++i) {
+ str = str + '<span ' + common_mouseover + common_mouseout + ' style="background-color: ' + colors[i] + '; ' + common_css + '" onclick="this.parentNode.__object__.Set(\'chart.annotate.color\', this.style.backgroundColor); this.parentNode.style.display = \'none\'; RGraph.FireCustomEvent(this.parentNode.__object__, \'onannotatecolor\')">&nbsp;</span>';
+
+ // This makes the colours go across two levels
+ if (i == 5) {
+ str += '<br />';
+ }
+ }
+
+ div.innerHTML = str;
+ document.body.appendChild(div);
+
+ /**
+ * Now the div has been added to the document, move it up and left and set the width and height
+ */
+ div.style.width = (div.offsetWidth) + 'px';
+ div.style.height = (div.offsetHeight - (RGraph.isIE9up() ? 5 : 5)) + 'px';
+ div.style.left = Math.max(0, e.pageX - div.offsetWidth - 2) + 'px';
+ div.style.top = (e.pageY - div.offsetHeight - 2) + 'px';
+
+ /**
+ * Store the palette div in the registry
+ */
+ RGraph.Registry.Set('palette', div);
+
+ setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.2", 50);
+ setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.4", 100);
+ setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.6", 150);
+ setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.8", 200);
+ setTimeout("RGraph.Registry.Get('palette').style.opacity = 1", 250);
+
+ RGraph.HideContext();
+
+ window.onclick = function ()
+ {
+ RGraph.HidePalette();
+ }
+
+ // Should this be here? Yes. This function is being used as an event handler.
+ e.stopPropagation();
+ return false;
+ }
+
+
+ /**
+ * Clears any annotation data from global storage
+ *
+ * @param string id The ID of the canvas
+ */
+ RGraph.ClearAnnotations = function (id)
+ {
+ var canvas = document.getElementById(id);
+ var obj = canvas.__object__;
+
+ if (window.localStorage && window.localStorage['__rgraph_annotations_' + id + '__'] && window.localStorage['__rgraph_annotations_' + id + '__'].length) {
+ window.localStorage['__rgraph_annotations_' + id + '__'] = [];
+
+ RGraph.FireCustomEvent(obj, 'onannotateclear');
+ }
+ }
+
+
+ /**
+ * Replays stored annotations
+ *
+ * @param object obj The graph object
+ */
+ RGraph.ReplayAnnotations = function (obj)
+ {
+ // Check for support
+ if (!window.localStorage) {
+ return;
+ }
+
+ var context = obj.context;
+ var annotations = window.localStorage['__rgraph_annotations_' + obj.id + '__'];
+ var i, len, move, coords;
+
+ context.beginPath();
+ context.lineWidth = 2;
+
+ if (annotations && annotations.length) {
+ annotations = annotations.split('|');
+ } else {
+ return;
+ }
+
+ for (i=0, len=annotations.length; i<len; ++i) {
+ if (!annotations[i].match(/^[0-9]+,[0-9]+$/)) {
+ context.stroke();
+ context.beginPath();
+ context.strokeStyle = annotations[i];
+ move = true;
+ continue;
+ }
+
+ coords = annotations[i].split(',');
+
+ if (move) {
+ context.moveTo(coords[0], coords[1]);
+ move = false;
+ } else {
+ context.lineTo(coords[0], coords[1]);
+ }
+ }
+
+ context.stroke();
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.context.js b/schall/static/RGraph/libraries/RGraph.common.context.js
new file mode 100644
index 0000000..ee6c6ae
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.context.js
@@ -0,0 +1,579 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+
+ /**
+ * This gunction shows a context menu containing the parameters
+ * provided to it
+ *
+ * @param object canvas The canvas object
+ * @param array menuitems The context menu menuitems
+ * @param object e The event object
+ */
+ RGraph.Contextmenu = function (canvas, menuitems, e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ /**
+ * Fire the custom RGraph event onbeforecontextmenu
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'onbeforecontextmenu');
+
+ /**
+ * Hide any existing menu
+ */
+ if (RGraph.Registry.Get('chart.contextmenu')) {
+ RGraph.HideContext();
+ }
+
+ // Hide any zoomed canvas
+ RGraph.HideZoomedCanvas();
+
+ /**
+ * Hide the palette if necessary
+ */
+ RGraph.HidePalette();
+
+ /**
+ * This is here to ensure annotating is OFF
+ */
+ canvas.__object__.Set('chart.mousedown', false);
+
+ var x = e.pageX;
+ var y = e.pageY;
+ var div = document.createElement('div');
+ var bg = document.createElement('div');
+
+ div.className = 'RGraph_contextmenu';
+ div.__canvas__ = canvas; /* Store a reference to the canvas on the contextmenu object */
+ div.style.position = 'absolute';
+ div.style.left = 0;
+ div.style.top = 0;
+ div.style.border = '1px solid black';
+ div.style.backgroundColor = 'white';
+ div.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ div.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ div.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)';
+ div.style.opacity = 0;
+
+ bg.className = 'RGraph_contextmenu_background';
+ bg.style.position = 'absolute';
+ bg.style.backgroundColor = '#ccc';
+ bg.style.borderRight = '1px solid #aaa';
+ bg.style.top = 0;
+ bg.style.left = 0;
+ bg.style.width = '18px';
+ bg.style.height = '100%';
+ bg.style.opacity = 0;
+
+
+ div = document.body.appendChild(div);
+ bg = div.appendChild(bg);
+
+
+ /**
+ * Now add the context menu items
+ */
+ for (i=0; i<menuitems.length; ++i) {
+
+ var menuitem = document.createElement('div');
+
+ menuitem.__canvas__ = canvas;
+ menuitem.__contextmenu__ = div;
+ menuitem.className = 'RGraph_contextmenu_item';
+
+ if (menuitems[i]) {
+ menuitem.style.padding = '2px 5px 2px 23px';
+ menuitem.style.fontFamily = 'Arial';
+ menuitem.style.fontSize = '10pt';
+ menuitem.style.fontWeight = 'normal';
+ menuitem.innerHTML = menuitems[i][0];
+
+ if (RGraph.is_array(menuitems[i][1])) {
+ menuitem.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAQUlEQVQImY3NoQ2AMABE0ZewABMyGQ6mqWODzlAclBSFO8HZl8uf0FFxCHtwYkt4Y6ChYE44cGH9/fyae2p2LAleW9oVTQuVf6gAAAAASUVORK5CYII=)';
+ menuitem.style.backgroundRepeat = 'no-repeat';
+ menuitem.style.backgroundPosition = '97% center';
+ }
+
+ // Add the mouseover event
+ if (menuitems[i][1]) {
+ if (menuitem.addEventListener) {
+ menuitem.addEventListener("mouseover", function (e) {RGraph.HideContextSubmenu(); e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false);
+ menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false);
+ } else {
+ menuitem.attachEvent("onmouseover", function () {RGraph.HideContextSubmenu();event.srcElement.style.backgroundColor = '#eee';event.srcElement.style.cursor = 'pointer';}
+ , false);
+ menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false);
+ }
+ } else {
+ if (menuitem.addEventListener) {
+ menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false);
+ menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false);
+ } else {
+ menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false);
+ menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false);
+ }
+ }
+
+ } else {
+ menuitem.style.borderBottom = '1px solid #ddd';
+ menuitem.style.marginLeft = '25px';
+ }
+
+ div.appendChild(menuitem);
+
+ /**
+ * Install the event handler that calls the menuitem
+ */
+ if (menuitems[i] && menuitems[i][1] && typeof(menuitems[i][1]) == 'function') {
+ if (document.all && 0) { // MSIE bit no longer necessary
+ menuitem.attachEvent('onclick', menuitems[i][1]);
+ menuitem.attachEvent('onclick', function () {RGraph.HideContext();});
+ } else {
+ menuitem.addEventListener('click', menuitems[i][1], false);
+ }
+
+ // Submenu
+ } else if (menuitems[i] && menuitems[i][1] && RGraph.is_array(menuitems[i][1])) {
+ (function ()
+ {
+ var tmp = menuitems[i][1]; // This is here because of "references vs primitives" and how they're passed around in Javascript
+ menuitem.addEventListener('mouseover', function (e) {RGraph.Contextmenu_submenu(canvas.__object__, tmp, e.target);}, false);
+ })();
+ }
+ }
+
+ /**
+ * Now all the menu items have been added, set the shadow width
+ * Shadow now handled by CSS3?
+ */
+ div.style.width = (div.offsetWidth + 10) + 'px';
+ div.style.height = (div.offsetHeight - 2) + 'px';
+
+ /**
+ * Set the background (the left bar) width if it's MSIE
+ */
+ //if (document.all) {
+ // bg.style.height = (div.offsetHeight - 2) + 'px';
+ //}
+
+ // Show the menu to the left or the right (normal) of the cursor?
+ if (x + div.offsetWidth > document.body.offsetWidth) {
+ x -= div.offsetWidth;
+ }
+
+ // Reposition the menu (now we have the real offsetWidth)
+ div.style.left = x + 'px';
+ div.style.top = y + 'px';
+
+ /**
+ * Do a little fade in effect
+ */
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.2", 50);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.4", 100);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.6", 150);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.8", 200);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 1", 250);
+
+ // The fade in effect on the left gray bar
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.2", 50);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.4", 100);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.6", 150);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.8", 200);
+ setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 1", 250);
+
+ // Store the context menu in the registry
+ RGraph.Registry.Set('chart.contextmenu', div);
+ RGraph.Registry.Set('chart.contextmenu.bg', bg);
+ RGraph.Registry.Get('chart.contextmenu').oncontextmenu = function () {return false;};
+ RGraph.Registry.Get('chart.contextmenu.bg').oncontextmenu = function () {return false;};
+
+ /**
+ * Install the event handlers that hide the context menu
+ */
+ canvas.addEventListener('click', function () {RGraph.HideContext();}, false);
+
+ window.onclick = function (e)
+ {
+ RGraph.HideContext();
+
+ // Removed on 3/7/10 - stops a bug in conjunction with annotating which presents itself on the Rscatter
+ //RGraph.Redraw();
+
+ // Fire the onclick event again
+ if (e.target.onclick && e.button == 0) {
+ e.target.onclick(e);
+ }
+ }
+
+ window.onresize = function () {RGraph.HideContext();}
+
+ /**
+ * Add the __shape__ object to the context menu
+ */
+
+ /**
+ * Set the shape coords from the .getShape() method
+ */
+ if (typeof(canvas.__object__.getShape) == 'function') {
+ RGraph.Registry.Get('chart.contextmenu').__shape__ = canvas.__object__.getShape(e);
+ }
+
+
+ e.stopPropagation();
+
+ /**
+ * Fire the (RGraph) oncontextmenu event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'oncontextmenu');
+
+ return false;
+ }
+
+
+ /**
+ * Hides the context menu if it's currently visible
+ */
+ RGraph.HideContext = function ()
+ {
+ var cm = RGraph.Registry.Get('chart.contextmenu');
+ var cmbg = RGraph.Registry.Get('chart.contextmenu.bg');
+
+ //Hide any submenu currently being displayed
+ RGraph.HideContextSubmenu();
+
+ if (cm) {
+ cm.parentNode.removeChild(cm);
+ cmbg.parentNode.removeChild(cmbg);
+
+ cm.style.visibility = 'hidden';
+ cm.style.display = 'none';
+ RGraph.Registry.Set('chart.contextmenu', null);
+
+ cmbg.style.visibility = 'hidden';
+ cmbg.style.display = 'none';
+ RGraph.Registry.Set('chart.contextmenu.bg', null);
+ }
+ }
+
+
+ /**
+ * Hides the context menus SUBMENU if it's currently visible
+ */
+ RGraph.HideContextSubmenu = function ()
+ {
+ var sub = RGraph.Registry.Get('chart.contextmenu.submenu');
+
+ if (sub) {
+ sub.style.visibility = 'none';
+ sub.style.display = 'none';
+ RGraph.Registry.Set('chart.contextmenu.submenu', null);
+ }
+ }
+
+
+ /**
+ * Shows the context menu after making a few checks - not opera (doesn't support oncontextmenu,
+ * not safari (tempermentality), not chrome (hmmm)
+ */
+ RGraph.ShowContext = function (obj)
+ {
+ RGraph.HidePalette();
+
+ if (obj.Get('chart.contextmenu') && obj.Get('chart.contextmenu').length) {
+
+ var isOpera = navigator.userAgent.indexOf('Opera') >= 0;
+ var isSafari = navigator.userAgent.indexOf('Safari') >= 0;
+ var isChrome = navigator.userAgent.indexOf('Chrome') >= 0;
+ var isMacFirefox = navigator.userAgent.indexOf('Firefox') > 0 && navigator.userAgent.indexOf('Mac') > 0;
+ var isIE9 = navigator.userAgent.indexOf('MSIE 9') >= 0;
+
+ if (((!isOpera && !isSafari) || isChrome) && !isMacFirefox) {
+
+ obj.canvas.oncontextmenu = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ if (e.ctrlKey) return true;
+
+ RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e);
+
+ return false;
+ }
+
+ // Accomodate Opera and Safari - use double click event
+ } else {
+
+ obj.canvas.addEventListener('dblclick', function (e)
+ {
+ if (e.ctrlKey) return true;
+
+ if (!RGraph.Registry.Get('chart.contextmenu')) {
+ RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e);
+ }
+ }, false);
+ }
+ }
+ }
+
+
+ /**
+ * This draws a submenu should it be necessary
+ *
+ * @param object obj The graph object
+ * @param object menu The context menu
+ */
+ RGraph.Contextmenu_submenu = function (obj, menuitems, parentMenuItem)
+ {
+ RGraph.HideContextSubmenu();
+
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var menu = parentMenuItem.parentNode;
+
+ var subMenu = document.createElement('DIV');
+ subMenu.style.position = 'absolute';
+ subMenu.style.width = '100px';
+ subMenu.style.top = menu.offsetTop + parentMenuItem.offsetTop + 'px';
+ subMenu.style.left = (menu.offsetLeft + menu.offsetWidth - (RGraph.isIE8() ? 9 : 0)) + 'px';
+ subMenu.style.backgroundColor = 'white';
+ subMenu.style.border = '1px solid black';
+ subMenu.className = 'RGraph_contextmenu';
+ subMenu.__contextmenu__ = menu;
+ subMenu.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ subMenu.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ subMenu.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ subMenu.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)';
+ document.body.appendChild(subMenu);
+
+ for (var i=0; i<menuitems.length; ++i) {
+
+ var menuitem = document.createElement('DIV');
+
+ menuitem.__canvas__ = canvas;
+ menuitem.__contextmenu__ = menu;
+ menuitem.className = 'RGraph_contextmenu_item';
+
+ if (menuitems[i]) {
+ menuitem.style.padding = '2px 5px 2px 23px';
+ menuitem.style.fontFamily = 'Arial';
+ menuitem.style.fontSize = '10pt';
+ menuitem.style.fontWeight = 'normal';
+ menuitem.innerHTML = menuitems[i][0];
+
+ if (menuitems[i][1]) {
+ if (menuitem.addEventListener) {
+ menuitem.addEventListener("mouseover", function (e) {e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false);
+ menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false);
+ } else {
+ menuitem.attachEvent("onmouseover", function () {event.srcElement.style.backgroundColor = 'rgba(0,0,0,0.2)'; event.srcElement.style.cursor = 'pointer'}, false);
+ menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false);
+ }
+ } else {
+ if (menuitem.addEventListener) {
+ menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false);
+ menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false);
+ } else {
+ menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false);
+ menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false);
+ }
+ }
+ } else {
+ menuitem.style.borderBottom = '1px solid #ddd';
+ menuitem.style.marginLeft = '25px';
+ }
+
+ subMenu.appendChild(menuitem);
+
+ if (menuitems[i] && menuitems[i][1]) {
+ if (document.all) {
+ menuitem.attachEvent('onclick', menuitems[i][1]);
+ } else {
+ menuitem.addEventListener('click', menuitems[i][1], false);
+ }
+ }
+ }
+
+
+ var bg = document.createElement('DIV');
+ bg.className = 'RGraph_contextmenu_background';
+ bg.style.position = 'absolute';
+ bg.style.backgroundColor = '#ccc';
+ bg.style.borderRight = '1px solid #aaa';
+ bg.style.top = 0;
+ bg.style.left = 0;
+ bg.style.width = '18px';
+ bg.style.height = '100%';
+
+ bg = subMenu.appendChild(bg);
+
+ RGraph.Registry.Set('chart.contextmenu.submenu', subMenu);
+ }
+
+
+ /**
+ * A function designed to be used in conjunction with thed context menu
+ * to allow people to get image versions of canvases.
+ *
+ * @param canvas Optionally you can pass in the canvas, which will be used
+ */
+ RGraph.showPNG = function ()
+ {
+ if (RGraph.isIE8()) {
+ alert('[RGRAPH PNG] Sorry, showing a PNG is not supported on MSIE8.');
+ return;
+ }
+
+ if (arguments[0] && arguments[0].id) {
+ var canvas = arguments[0];
+ var event = arguments[1];
+
+ } else if (RGraph.Registry.Get('chart.contextmenu')) {
+ var canvas = RGraph.Registry.Get('chart.contextmenu').__canvas__;
+
+ } else {
+ alert('[RGRAPH SHOWPNG] Could not find canvas!');
+ }
+
+ var obj = canvas.__object__;
+
+ /**
+ * Create the gray background DIV to cover the page
+ */
+ var bg = document.createElement('DIV');
+ bg.id = '__rgraph_image_bg__';
+ bg.style.position = 'fixed';
+ bg.style.top = '-10px';
+ bg.style.left = '-10px';
+ bg.style.width = '5000px';
+ bg.style.height = '5000px';
+ bg.style.backgroundColor = 'rgb(204,204,204)';
+ bg.style.opacity = 0;
+ document.body.appendChild(bg);
+
+
+ /**
+ * Create the div that the graph sits in
+ */
+ var div = document.createElement('DIV');
+ div.style.backgroundColor = 'white';
+ div.style.opacity = 0;
+ div.style.border = '1px solid black';
+ div.style.position = 'fixed';
+ div.style.top = '20%';
+ div.style.width = canvas.width + 'px';
+ div.style.height = canvas.height + 35 + 'px';
+ div.style.left = (document.body.clientWidth / 2) - (canvas.width / 2) + 'px';
+ div.style.padding = '5px';
+
+ div.style.borderRadius = '10px';
+ div.style.MozBorderRadius = '10px';
+ div.style.WebkitBorderRadius = '10px';
+
+ div.style.boxShadow = '0 0 15px rgba(96,96,96,0.5)';
+ div.style.MozBoxShadow = '0 0 15px rgba(96,96,96,0.5)';
+ div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
+
+ div.__canvas__ = canvas;
+ div.__object__ = obj;
+ div.id = '__rgraph_image_div__';
+ document.body.appendChild(div);
+
+
+ /**
+ * Add the HTML text inputs
+ */
+ div.innerHTML += '<div style="position: absolute; margin-left: 10px; top: ' + canvas.height + 'px; width: ' + (canvas.width - 50) + 'px; height: 25px"><span style="display: inline; display: inline-block; width: 65px; text-align: right">URL:</span><textarea style="float: right; overflow: hidden; height: 15px; width: ' + (canvas.width - obj.gutterLeft - obj.gutterRight - 80) + 'px" onclick="this.select()" readonly="readonly" id="__rgraph_dataurl__">' + canvas.toDataURL() + '</textarea></div>';
+ div.innerHTML += '<div style="position: absolute; top: ' + (canvas.height + 25) + 'px; left: ' + (obj.gutterLeft - 65 + (canvas.width / 2)) + 'px; width: ' + (canvas.width - obj.gutterRight) + 'px; font-size: 65%">A link using the URL: <a href="' + canvas.toDataURL() + '">View</a></div>'
+
+
+
+ /**
+ * Create the image rendition of the graph
+ */
+ var img = document.createElement('IMG');
+ RGraph.Registry.Set('chart.png', img);
+ img.__canvas__ = canvas;
+ img.__object__ = obj;
+ img.id = '__rgraph_image_img__';
+ img.className = 'RGraph_png';
+
+ img.src = canvas.toDataURL();
+
+ div.appendChild(img);
+
+ setTimeout(function () {document.getElementById("__rgraph_dataurl__").select();}, 50);
+
+ window.addEventListener('resize', function (e){var img = RGraph.Registry.Get('chart.png');img.style.left = (document.body.clientWidth / 2) - (img.width / 2) + 'px';}, false);
+
+ bg.onclick = function (e)
+ {
+ var div = document.getElementById("__rgraph_image_div__");
+ var bg = document.getElementById("__rgraph_image_bg__");
+
+ if (div) {
+ div.style.opacity = 0;
+
+ div.parentNode.removeChild(div);
+
+ div.id = '';
+ div.style.display = 'none';
+ div = null;
+ }
+
+ if (bg) {
+ bg.style.opacity = 0;
+
+ bg.id = '';
+ bg.style.display = 'none';
+ bg = null;
+ }
+ }
+
+ window.addEventListener('resize', function (e) {bg.onclick(e);}, false)
+
+ /**
+ * This sets the image as a global variable, circumventing repeated calls to document.getElementById()
+ */
+ __rgraph_image_bg__ = bg;
+ __rgraph_image_div__ = div;
+
+
+ setTimeout('__rgraph_image_div__.style.opacity = 0.2', 50);
+ setTimeout('__rgraph_image_div__.style.opacity = 0.4', 100);
+ setTimeout('__rgraph_image_div__.style.opacity = 0.6', 150);
+ setTimeout('__rgraph_image_div__.style.opacity = 0.8', 200);
+ setTimeout('__rgraph_image_div__.style.opacity = 1', 250);
+
+ setTimeout('__rgraph_image_bg__.style.opacity = 0.1', 50);
+ setTimeout('__rgraph_image_bg__.style.opacity = 0.2', 100);
+ setTimeout('__rgraph_image_bg__.style.opacity = 0.3', 150);
+ setTimeout('__rgraph_image_bg__.style.opacity = 0.4', 200);
+ setTimeout('__rgraph_image_bg__.style.opacity = 0.5', 250);
+
+
+
+ img.onclick = function (e)
+ {
+ if (e.stopPropagation) e.stopPropagation();
+ else event.cancelBubble = true;
+ }
+
+ if (event && event.stopPropagation) {
+ event.stopPropagation();
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.core.js b/schall/static/RGraph/libraries/RGraph.common.core.js
new file mode 100644
index 0000000..990f0ca
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.core.js
@@ -0,0 +1,2991 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ /**
+ * Initialise the various objects
+ */
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+
+ RGraph.Registry = {};
+ RGraph.Registry.store = [];
+ RGraph.Registry.store['chart.event.handlers'] = [];
+ RGraph.background = {};
+ RGraph.objects = [];
+ RGraph.Resizing = {};
+ RGraph.events = [];
+
+
+
+ /**
+ * Returns five values which are used as a nice scale
+ *
+ * @param max int The maximum value of the graph
+ * @param obj object The graph object
+ * @return array An appropriate scale
+ */
+ RGraph.getScale = function (max, obj)
+ {
+ /**
+ * Special case for 0
+ */
+ if (max == 0) {
+ return ['0.2', '0.4', '0.6', '0.8', '1.0'];
+ }
+
+ var original_max = max;
+
+ /**
+ * Manually do decimals
+ */
+ if (max <= 1) {
+ if (max > 0.5) {
+ return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)];
+
+ } else if (max >= 0.1) {
+ return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5];
+
+ } else {
+
+ var tmp = max;
+ var exp = 0;
+
+ while (tmp < 1.01) {
+ exp += 1;
+ tmp *= 10;
+ }
+
+ var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp];
+
+
+ if (max <= ('5e-' + exp)) {
+ ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp];
+ }
+
+ return ret;
+ }
+ }
+
+ // Take off any decimals
+ if (String(max).indexOf('.') > 0) {
+ max = String(max).replace(/\.\d+$/, '');
+ }
+
+ var interval = Math.pow(10, Number(String(Number(max)).length - 1));
+ var topValue = interval;
+
+ while (topValue < max) {
+ topValue += (interval / 2);
+ }
+
+ // Handles cases where the max is (for example) 50.5
+ if (Number(original_max) > Number(topValue)) {
+ topValue += (interval / 2);
+ }
+
+ // Custom if the max is greater than 5 and less than 10
+ if (max < 10) {
+ topValue = (Number(original_max) <= 5 ? 5 : 10);
+ }
+
+ /**
+ * Added 02/11/2010 to create "nicer" scales
+ */
+ if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) {
+ topValue = 10 * interval;
+ }
+
+ return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue];
+ }
+
+
+ /**
+ * Returns the maximum numeric value which is in an array
+ *
+ * @param array arr The array (can also be a number, in which case it's returned as-is)
+ * @param int Whether to ignore signs (ie negative/positive)
+ * @return int The maximum value in the array
+ */
+ RGraph.array_max = function (arr)
+ {
+ var max = null;
+
+ if (typeof(arr) == 'number') {
+ return arr;
+ }
+
+ for (var i=0; i<arr.length; ++i) {
+ if (typeof(arr[i]) == 'number') {
+
+ var val = arguments[1] ? Math.abs(arr[i]) : arr[i];
+
+ if (typeof(max) == 'number') {
+ max = Math.max(max, val);
+ } else {
+ max = val;
+ }
+ }
+ }
+
+ return max;
+ }
+
+
+ /**
+ * Returns the maximum value which is in an array
+ *
+ * @param array arr The array
+ * @param int len The length to pad the array to
+ * @param mixed The value to use to pad the array (optional)
+ */
+ RGraph.array_pad = function (arr, len)
+ {
+ if (arr.length < len) {
+ var val = arguments[2] ? arguments[2] : null;
+
+ for (var i=arr.length; i<len; ++i) {
+ arr[i] = val;
+ }
+ }
+
+ return arr;
+ }
+
+
+ /**
+ * An array sum function
+ *
+ * @param array arr The array to calculate the total of
+ * @return int The summed total of the arrays elements
+ */
+ RGraph.array_sum = function (arr)
+ {
+ // Allow integers
+ if (typeof(arr) == 'number') {
+ return arr;
+ }
+
+ var i, sum;
+ var len = arr.length;
+
+ for(i=0,sum=0;i<len;sum+=arr[i++]);
+ return sum;
+ }
+
+
+
+ /**
+ * A simple is_array() function
+ *
+ * @param mixed obj The object you want to check
+ * @return bool Whether the object is an array or not
+ */
+ RGraph.is_array = function (obj)
+ {
+ return obj != null && obj.constructor.toString().indexOf('Array') != -1;
+ }
+
+
+ /**
+ * Converts degrees to radians
+ *
+ * @param int degrees The number of degrees
+ * @return float The number of radians
+ */
+ RGraph.degrees2Radians = function (degrees)
+ {
+ return degrees * (Math.PI / 180);
+ }
+
+
+ /**
+ * This function draws an angled line. The angle is cosidered to be clockwise
+ *
+ * @param obj ctxt The context object
+ * @param int x The X position
+ * @param int y The Y position
+ * @param int angle The angle in RADIANS
+ * @param int length The length of the line
+ */
+ RGraph.lineByAngle = function (context, x, y, angle, length)
+ {
+ context.arc(x, y, length, angle, angle, false);
+ context.lineTo(x, y);
+ context.arc(x, y, length, angle, angle, false);
+ }
+
+
+ /**
+ * This is a useful function which is basically a shortcut for drawing left, right, top and bottom alligned text.
+ *
+ * @param object context The context
+ * @param string font The font
+ * @param int size The size of the text
+ * @param int x The X coordinate
+ * @param int y The Y coordinate
+ * @param string text The text to draw
+ * @parm string The vertical alignment. Can be null. "center" gives center aligned text, "top" gives top aligned text.
+ * Anything else produces bottom aligned text. Default is bottom.
+ * @param string The horizontal alignment. Can be null. "center" gives center aligned text, "right" gives right aligned text.
+ * Anything else produces left aligned text. Default is left.
+ * @param bool Whether to show a bounding box around the text. Defaults not to
+ * @param int The angle that the text should be rotate at (IN DEGREES)
+ * @param string Background color for the text
+ * @param bool Whether the text is bold or not
+ * @param bool Whether the bounding box has a placement indicator
+ */
+ RGraph.Text = function (context, font, size, x, y, text)
+ {
+ /**
+ * This calls the text function recursively to accommodate multi-line text
+ */
+ if (typeof(text) == 'string' && text.match(/\r\n/)) {
+
+ var arr = text.split('\r\n');
+
+ text = arr[0];
+ arr = RGraph.array_shift(arr);
+
+ var nextline = arr.join('\r\n')
+
+ RGraph.Text(context, font, size, arguments[9] == -90 ? (x + (size * 1.5)) : x, y + (size * 1.5), nextline, arguments[6] ? arguments[6] : null, 'center', arguments[8], arguments[9], arguments[10], arguments[11], arguments[12]);
+ }
+
+
+ // Accommodate MSIE
+ if (RGraph.isIE8()) {
+ y += 2;
+ }
+
+
+ context.font = (arguments[11] ? 'Bold ': '') + size + 'pt ' + font;
+
+ var i;
+ var origX = x;
+ var origY = y;
+ var originalFillStyle = context.fillStyle;
+ var originalLineWidth = context.lineWidth;
+
+ // Need these now the angle can be specified, ie defaults for the former two args
+ if (typeof(arguments[6]) == null) arguments[6] = 'bottom'; // Vertical alignment. Default to bottom/baseline
+ if (typeof(arguments[7]) == null) arguments[7] = 'left'; // Horizontal alignment. Default to left
+ if (typeof(arguments[8]) == null) arguments[8] = null; // Show a bounding box. Useful for positioning during development. Defaults to false
+ if (typeof(arguments[9]) == null) arguments[9] = 0; // Angle (IN DEGREES) that the text should be drawn at. 0 is middle right, and it goes clockwise
+ if (typeof(arguments[12]) == null) arguments[12] = true; // Whether the bounding box has the placement indicator
+
+ // The alignment is recorded here for purposes of Opera compatibility
+ if (navigator.userAgent.indexOf('Opera') != -1) {
+ context.canvas.__rgraph_valign__ = arguments[6];
+ context.canvas.__rgraph_halign__ = arguments[7];
+ }
+
+ // First, translate to x/y coords
+ context.save();
+
+ context.canvas.__rgraph_originalx__ = x;
+ context.canvas.__rgraph_originaly__ = y;
+
+ context.translate(x, y);
+ x = 0;
+ y = 0;
+
+ // Rotate the canvas if need be
+ if (arguments[9]) {
+ context.rotate(arguments[9] / 57.3);
+ }
+
+ // Vertical alignment - defaults to bottom
+ if (arguments[6]) {
+ var vAlign = arguments[6];
+
+ if (vAlign == 'center') {
+ context.translate(0, size / 2);
+ } else if (vAlign == 'top') {
+ context.translate(0, size);
+ }
+ }
+
+
+ // Hoeizontal alignment - defaults to left
+ if (arguments[7]) {
+ var hAlign = arguments[7];
+ var width = context.measureText(text).width;
+
+ if (hAlign) {
+ if (hAlign == 'center') {
+ context.translate(-1 * (width / 2), 0)
+ } else if (hAlign == 'right') {
+ context.translate(-1 * width, 0)
+ }
+ }
+ }
+
+
+ context.fillStyle = originalFillStyle;
+
+ /**
+ * Draw a bounding box if requested
+ */
+ context.save();
+ context.fillText(text,0,0);
+ context.lineWidth = 0.5;
+
+ if (arguments[8]) {
+
+ var width = context.measureText(text).width;
+ var ieOffset = RGraph.isIE8() ? 2 : 0;
+
+ context.translate(x, y);
+ context.strokeRect(0 - 3, 0 - 3 - size - ieOffset, width + 6, 0 + size + 6);
+
+ /**
+ * If requested, draw a background for the text
+ */
+ if (arguments[10]) {
+
+ var offset = 3;
+ var ieOffset = RGraph.isIE8() ? 2 : 0;
+ var width = context.measureText(text).width
+
+ //context.strokeStyle = 'gray';
+ context.fillStyle = arguments[10];
+ context.fillRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset));
+ //context.strokeRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset));
+ }
+
+ /**
+ * Do the actual drawing of the text
+ */
+ context.fillStyle = originalFillStyle;
+ context.fillText(text,0,0);
+
+ if (arguments[12]) {
+ context.fillRect(
+ arguments[7] == 'left' ? 0 : (arguments[7] == 'center' ? width / 2 : width ) - 2,
+ arguments[6] == 'bottom' ? 0 : (arguments[6] == 'center' ? (0 - size) / 2 : 0 - size) - 2,
+ 4,
+ 4
+ );
+ }
+ }
+ context.restore();
+
+ // Reset the lineWidth
+ context.lineWidth = originalLineWidth;
+
+ context.restore();
+ }
+
+
+ /**
+ * Clears the canvas by setting the width. You can specify a colour if you wish.
+ *
+ * @param object canvas The canvas to clear
+ */
+ RGraph.Clear = function (canvas)
+ {
+ var context = canvas.getContext('2d');
+ var color = arguments[1];
+
+ if (RGraph.isIE8() && !color) {
+ color = 'white';
+ }
+
+ /**
+ * Can now clear the canvas back to fully transparent
+ */
+ if (!color || (color && color == 'transparent')) {
+
+ context.clearRect(0,0,canvas.width, canvas.height);
+ // context.fillStyle = 'rgba(0,0,0,0)';
+ // context.globalCompositeOperation = 'source-in';
+ // context = canvas.getContext('2d');
+ //context.beginPath();
+ //context.fillRect(-1000,-1000,canvas.width + 2000,canvas.height + 2000);
+ //context.fill();
+
+ // Reset the globalCompositeOperation
+ context.globalCompositeOperation = 'source-over';
+
+ } else {
+
+ context.fillStyle = color;
+ context = canvas.getContext('2d');
+ context.beginPath();
+
+ if (RGraph.isIE8()) {
+ context.fillRect(0,0,canvas.width,canvas.height);
+ } else {
+ context.fillRect(-10,-10,canvas.width + 20,canvas.height + 20);
+ }
+
+ context.fill();
+ }
+
+ // Don't do this as it also clears any translation that may have occurred
+ //canvas.width = canvas.width;
+
+ if (RGraph.ClearAnnotations) {
+ RGraph.ClearAnnotations(canvas.id);
+ }
+
+ RGraph.FireCustomEvent(canvas.__object__, 'onclear');
+ }
+
+
+ /**
+ * Draws the title of the graph
+ *
+ * @param object canvas The canvas object
+ * @param string text The title to write
+ * @param integer gutter The size of the gutter
+ * @param integer The center X point (optional - if not given it will be generated from the canvas width)
+ * @param integer Size of the text. If not given it will be 14
+ */
+ RGraph.DrawTitle = function (canvas, text, gutterTop)
+ {
+ var obj = canvas.__object__;
+ var context = canvas.getContext('2d');
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+ var size = arguments[4] ? arguments[4] : 12;
+ var bold = obj.Get('chart.title.bold');
+ var centerx = (arguments[3] ? arguments[3] : ((obj.canvas.width - gutterLeft - gutterRight) / 2) + gutterLeft);
+ var keypos = obj.Get('chart.key.position');
+ var vpos = obj.Get('chart.title.vpos');
+ var hpos = obj.Get('chart.title.hpos');
+ var bgcolor = obj.Get('chart.title.background');
+
+ // Account for 3D effect by faking the key position
+ if (obj.type == 'bar' && obj.Get('chart.variant') == '3d') {
+ keypos = 'gutter';
+ }
+
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.text.color') ? obj.Get('chart.text.color') : 'black';
+
+ /**
+ * Vertically center the text if the key is not present
+ */
+ if (keypos && keypos != 'gutter') {
+ var vCenter = 'center';
+
+ } else if (!keypos) {
+ var vCenter = 'center';
+
+ } else {
+ var vCenter = 'bottom';
+ }
+
+ // if chart.title.vpos does not equal 0.5, use that
+ if (typeof(obj.Get('chart.title.vpos')) == 'number') {
+ vpos = obj.Get('chart.title.vpos') * gutterTop;
+
+ if (obj.Get('chart.xaxispos') == 'top') {
+ vpos = obj.Get('chart.title.vpos') * gutterBottom + gutterTop + (obj.canvas.height - gutterTop - gutterBottom);
+ }
+ } else {
+ vpos = gutterTop - size - 5;
+
+ if (obj.Get('chart.xaxispos') == 'top') {
+ vpos = obj.canvas.height - gutterBottom + size + 5;
+ }
+ }
+
+ // if chart.title.hpos is a number, use that. It's multiplied with the (entire) canvas width
+ if (typeof(hpos) == 'number') {
+ centerx = hpos * canvas.width;
+ }
+
+ // Set the colour
+ if (typeof(obj.Get('chart.title.color') != null)) {
+ var oldColor = context.fillStyle
+ var newColor = obj.Get('chart.title.color')
+ context.fillStyle = newColor ? newColor : 'black';
+ }
+
+ /**
+ * Default font is Verdana
+ */
+ var font = obj.Get('chart.text.font');
+
+ /**
+ * Override the default font with chart.title.font
+ */
+ if (typeof(obj.Get('chart.title.font')) == 'string') {
+ font = obj.Get('chart.title.font');
+ }
+
+ /**
+ * Draw the title itself
+ */
+ RGraph.Text(context, font, size, centerx, vpos, text, vCenter, 'center', bgcolor != null, null, bgcolor, bold);
+
+ // Reset the fill colour
+ context.fillStyle = oldColor;
+ }
+
+
+ /**
+ * This function returns the mouse position in relation to the canvas
+ *
+ * @param object e The event object.
+ */
+ RGraph.getMouseXY = function (e)
+ {
+ var obj = (RGraph.isIE8() ? event.srcElement : e.target);
+ var x;
+ var y;
+
+ if (RGraph.isIE8()) e = event;
+
+ // Browser with offsetX and offsetY
+ if (typeof(e.offsetX) == 'number' && typeof(e.offsetY) == 'number') {
+ x = e.offsetX;
+ y = e.offsetY;
+
+ // FF and other
+ } else {
+ x = 0;
+ y = 0;
+
+ while (obj != document.body && obj) {
+ x += obj.offsetLeft;
+ y += obj.offsetTop;
+
+ obj = obj.offsetParent;
+ }
+
+ x = e.pageX - x;
+ y = e.pageY - y;
+ }
+
+ return [x, y];
+ }
+
+
+ /**
+ * This function returns a two element array of the canvas x/y position in
+ * relation to the page
+ *
+ * @param object canvas
+ */
+ RGraph.getCanvasXY = function (canvas)
+ {
+ var x = 0;
+ var y = 0;
+ var obj = canvas;
+
+ do {
+
+ x += obj.offsetLeft;
+ y += obj.offsetTop;
+
+ obj = obj.offsetParent;
+
+ } while (obj && obj.tagName.toLowerCase() != 'body');
+
+ return [x, y];
+ }
+
+
+ /**
+ * Registers a graph object (used when the canvas is redrawn)
+ *
+ * @param object obj The object to be registered
+ */
+ RGraph.Register = function (obj)
+ {
+ var key = obj.id + '_' + obj.type;
+
+ RGraph.objects[key] = obj;
+ }
+
+
+ /**
+ * Causes all registered objects to be redrawn
+ *
+ * @param string An optional string indicating which canvas is not to be redrawn
+ * @param string An optional color to use to clear the canvas
+ */
+ RGraph.Redraw = function ()
+ {
+ for (i in RGraph.objects) {
+ // TODO FIXME Maybe include more intense checking for whether the object is an RGraph object, eg obj.isRGraph == true ...?
+ if (
+ typeof(i) == 'string'
+ && typeof(RGraph.objects[i]) == 'object'
+ && typeof(RGraph.objects[i].type) == 'string'
+ && RGraph.objects[i].isRGraph) {
+
+ if (!arguments[0] || arguments[0] != RGraph.objects[i].id) {
+ RGraph.Clear(RGraph.objects[i].canvas, arguments[1] ? arguments[1] : null);
+ RGraph.objects[i].Draw();
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Loosly mimicks the PHP function print_r();
+ */
+ RGraph.pr = function (obj)
+ {
+ var str = '';
+ var indent = (arguments[2] ? arguments[2] : '');
+
+ switch (typeof(obj)) {
+ case 'number':
+ if (indent == '') {
+ str+= 'Number: '
+ }
+ str += String(obj);
+ break;
+
+ case 'string':
+ if (indent == '') {
+ str+= 'String (' + obj.length + '):'
+ }
+ str += '"' + String(obj) + '"';
+ break;
+
+ case 'object':
+ // In case of null
+ if (obj == null) {
+ str += 'null';
+ break;
+ }
+
+ str += 'Object\n' + indent + '(\n';
+ for (var i in obj) {
+ if (typeof(i) == 'string' || typeof(i) == 'number') {
+ str += indent + ' ' + i + ' => ' + RGraph.pr(obj[i], true, indent + ' ') + '\n';
+ }
+ }
+
+ var str = str + indent + ')';
+ break;
+
+ case 'function':
+ str += obj;
+ break;
+
+ case 'boolean':
+ str += 'Boolean: ' + (obj ? 'true' : 'false');
+ break;
+ }
+
+ /**
+ * Finished, now either return if we're in a recursed call, or alert()
+ * if we're not.
+ */
+ if (arguments[1]) {
+ return str;
+ } else {
+ alert(str);
+ }
+ }
+
+
+ /**
+ * The RGraph registry Set() function
+ *
+ * @param string name The name of the key
+ * @param mixed value The value to set
+ * @return mixed Returns the same value as you pass it
+ */
+ RGraph.Registry.Set = function (name, value)
+ {
+ // Store the setting
+ RGraph.Registry.store[name] = value;
+
+ // Don't really need to do this, but ho-hum
+ return value;
+ }
+
+
+ /**
+ * The RGraph registry Get() function
+ *
+ * @param string name The name of the particular setting to fetch
+ * @return mixed The value if exists, null otherwise
+ */
+ RGraph.Registry.Get = function (name)
+ {
+ //return RGraph.Registry.store[name] == null ? null : RGraph.Registry.store[name];
+ return RGraph.Registry.store[name];
+ }
+
+
+ /**
+ * This function draws the background for the bar chart, line chart and scatter chart.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.background.Draw = function (obj)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var height = 0;
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+ var variant = obj.Get('chart.variant');
+
+ context.fillStyle = obj.Get('chart.text.color');
+
+ // If it's a bar and 3D variant, translate
+ if (variant == '3d') {
+ context.save();
+ context.translate(10, -5);
+ }
+
+ // X axis title
+ if (typeof(obj.Get('chart.title.xaxis')) == 'string' && obj.Get('chart.title.xaxis').length) {
+
+ var size = obj.Get('chart.text.size') + 2;
+ var font = obj.Get('chart.text.font');
+ var bold = obj.Get('chart.title.xaxis.bold');
+
+ if (typeof(obj.Get('chart.title.xaxis.size')) == 'number') {
+ size = obj.Get('chart.title.xaxis.size');
+ }
+
+ if (typeof(obj.Get('chart.title.xaxis.font')) == 'string') {
+ font = obj.Get('chart.title.xaxis.font');
+ }
+
+ var hpos = ((obj.canvas.width - obj.gutterLeft - obj.gutterRight) / 2) + obj.gutterLeft;
+ var vpos = obj.canvas.height - obj.Get('chart.gutter.bottom') + 25;
+
+ if (typeof(obj.Get('chart.title.xaxis.pos')) == 'number') {
+ vpos = obj.canvas.height - (gutterBottom * obj.Get('chart.title.xaxis.pos'));
+ }
+
+ context.beginPath();
+ RGraph.Text(context,
+ font,
+ size,
+ hpos,
+ vpos,
+ obj.Get('chart.title.xaxis'),
+ 'center',
+ 'center',
+ false,
+ false,
+ false,
+ bold);
+ context.fill();
+ }
+
+ // Y axis title
+ if (typeof(obj.Get('chart.title.yaxis')) == 'string' && obj.Get('chart.title.yaxis').length) {
+
+ var size = obj.Get('chart.text.size') + 2;
+ var font = obj.Get('chart.text.font');
+ var angle = 270;
+ var bold = obj.Get('chart.title.yaxis.bold');
+
+ if (typeof(obj.Get('chart.title.yaxis.pos')) == 'number') {
+ var yaxis_title_pos = obj.Get('chart.title.yaxis.pos') * obj.Get('chart.gutter.left');
+ } else {
+ var yaxis_title_pos = ((obj.Get('chart.gutter.left') - 25) / obj.Get('chart.gutter.left')) * obj.Get('chart.gutter.left');
+ }
+
+ if (typeof(obj.Get('chart.title.yaxis.size')) == 'number') {
+ size = obj.Get('chart.title.yaxis.size');
+ }
+
+ if (typeof(obj.Get('chart.title.yaxis.font')) == 'string') {
+ font = obj.Get('chart.title.yaxis.font');
+ }
+
+ if (obj.Get('chart.title.yaxis.align') == 'right' || obj.Get('chart.title.yaxis.position') == 'right') {
+ angle = 90;
+ yaxis_title_pos = obj.canvas.width - yaxis_title_pos;
+ } else {
+ yaxis_title_pos = yaxis_title_pos;
+ }
+
+
+ context.beginPath();
+ RGraph.Text(context,
+ font,
+ size,
+ yaxis_title_pos,
+ ((obj.canvas.height - obj.gutterTop - obj.gutterBottom) / 2) + obj.gutterTop,
+ obj.Get('chart.title.yaxis'),
+ 'center',
+ 'center',
+ false,
+ angle,
+ false,
+ bold);
+ context.fill();
+ }
+
+ obj.context.beginPath();
+
+ // Draw the horizontal bars
+ context.fillStyle = obj.Get('chart.background.barcolor1');
+ height = (RGraph.GetHeight(obj) - gutterBottom);
+
+ for (var i=gutterTop; i < height ; i+=80) {
+ obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, Math.min(40, RGraph.GetHeight(obj) - gutterBottom - i) );
+ }
+
+ context.fillStyle = obj.Get('chart.background.barcolor2');
+ height = (RGraph.GetHeight(obj) - gutterBottom);
+
+ for (var i= (40 + gutterTop); i < height; i+=80) {
+ obj.context.fillRect(gutterLeft, i, RGraph.GetWidth(obj) - gutterLeft - gutterRight, i + 40 > (RGraph.GetHeight(obj) - gutterBottom) ? RGraph.GetHeight(obj) - (gutterBottom + i) : 40);
+ }
+
+ context.stroke();
+
+
+ // Draw the background grid
+ if (obj.Get('chart.background.grid')) {
+
+ // If autofit is specified, use the .numhlines and .numvlines along with the width to work
+ // out the hsize and vsize
+ if (obj.Get('chart.background.grid.autofit')) {
+
+ /**
+ * Align the grid to the tickmarks
+ */
+ if (obj.Get('chart.background.grid.autofit.align')) {
+ // Align the horizontal lines
+ obj.Set('chart.background.grid.autofit.numhlines', obj.Get('chart.ylabels.count'));
+
+ // Align the vertical lines for the line
+ if (obj.type == 'line') {
+ if (obj.Get('chart.labels') && obj.Get('chart.labels').length) {
+ obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length - 1);
+ } else {
+ obj.Set('chart.background.grid.autofit.numvlines', obj.data[0].length - 1);
+ }
+
+ // Align the vertical lines for the bar
+ } else if (obj.type == 'bar' && obj.Get('chart.labels') && obj.Get('chart.labels').length) {
+ obj.Set('chart.background.grid.autofit.numvlines', obj.Get('chart.labels').length);
+ }
+ }
+
+ var vsize = ((RGraph.GetWidth(obj) - gutterLeft - gutterRight)) / obj.Get('chart.background.grid.autofit.numvlines');
+ var hsize = (RGraph.GetHeight(obj) - gutterTop - gutterBottom) / obj.Get('chart.background.grid.autofit.numhlines');
+
+ obj.Set('chart.background.grid.vsize', vsize);
+ obj.Set('chart.background.grid.hsize', hsize);
+ }
+
+ context.beginPath();
+ context.lineWidth = obj.Get('chart.background.grid.width') ? obj.Get('chart.background.grid.width') : 1;
+ context.strokeStyle = obj.Get('chart.background.grid.color');
+
+ // Draw the horizontal lines
+ if (obj.Get('chart.background.grid.hlines')) {
+ height = (RGraph.GetHeight(obj) - gutterBottom)
+ for (y=gutterTop; y<height; y+=obj.Get('chart.background.grid.hsize')) {
+ context.moveTo(gutterLeft, y);
+ context.lineTo(RGraph.GetWidth(obj) - gutterRight, y);
+ }
+ }
+
+ if (obj.Get('chart.background.grid.vlines')) {
+ // Draw the vertical lines
+ var width = (RGraph.GetWidth(obj) - gutterRight)
+ for (x=gutterLeft; x<=width; x+=obj.Get('chart.background.grid.vsize')) {
+ context.moveTo(x, gutterTop);
+ context.lineTo(x, RGraph.GetHeight(obj) - gutterBottom);
+ }
+ }
+
+ if (obj.Get('chart.background.grid.border')) {
+ // Make sure a rectangle, the same colour as the grid goes around the graph
+ context.strokeStyle = obj.Get('chart.background.grid.color');
+ context.strokeRect(gutterLeft, gutterTop, RGraph.GetWidth(obj) - gutterLeft - gutterRight, RGraph.GetHeight(obj) - gutterTop - gutterBottom);
+ }
+ }
+
+ context.stroke();
+
+ // If it's a bar and 3D variant, translate
+ if (variant == '3d') {
+ context.restore();
+ }
+
+ // Draw the title if one is set
+ if ( typeof(obj.Get('chart.title')) == 'string') {
+
+ if (obj.type == 'gantt') {
+ gutterTop -= 10;
+ }
+
+ RGraph.DrawTitle(canvas,
+ obj.Get('chart.title'),
+ gutterTop,
+ null,
+ obj.Get('chart.title.size') ? obj.Get('chart.title.size') : obj.Get('chart.text.size') + 2);
+ }
+
+ context.stroke();
+ }
+
+
+ /**
+ * Returns the day number for a particular date. Eg 1st February would be 32
+ *
+ * @param object obj A date object
+ * @return int The day number of the given date
+ */
+ RGraph.GetDays = function (obj)
+ {
+ var year = obj.getFullYear();
+ var days = obj.getDate();
+ var month = obj.getMonth();
+
+ if (month == 0) return days;
+ if (month >= 1) days += 31;
+ if (month >= 2) days += 28;
+
+ // Leap years. Crude, but if this code is still being used
+ // when it stops working, then you have my permission to shoot
+ // me. Oh, you won't be able to - I'll be dead...
+ if (year >= 2008 && year % 4 == 0) days += 1;
+
+ if (month >= 3) days += 31;
+ if (month >= 4) days += 30;
+ if (month >= 5) days += 31;
+ if (month >= 6) days += 30;
+ if (month >= 7) days += 31;
+ if (month >= 8) days += 31;
+ if (month >= 9) days += 30;
+ if (month >= 10) days += 31;
+ if (month >= 11) days += 30;
+
+ return days;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * Draws the graph key (used by various graphs)
+ *
+ * @param object obj The graph object
+ * @param array key An array of the texts to be listed in the key
+ * @param colors An array of the colors to be used
+ */
+ RGraph.DrawKey = function (obj, key, colors)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ context.lineWidth = 1;
+
+ context.beginPath();
+
+ /**
+ * Key positioned in the gutter
+ */
+ var keypos = obj.Get('chart.key.position');
+ var textsize = obj.Get('chart.text.size');
+
+ /**
+ * Change the older chart.key.vpos to chart.key.position.y
+ */
+ if (typeof(obj.Get('chart.key.vpos')) == 'number') {
+ obj.Set('chart.key.position.y', obj.Get('chart.key.vpos') * this.Get('chart.gutter.top') );
+ }
+
+ /**
+ * Account for null values in the key
+ */
+ var key_non_null = [];
+ var colors_non_null = [];
+ for (var i=0; i<key.length; ++i) {
+ if (key[i] != null) {
+ colors_non_null.push(colors[i]);
+ key_non_null.push(key[i]);
+ }
+ }
+
+ key = key_non_null;
+ colors = colors_non_null;
+
+
+
+ if (keypos && keypos == 'gutter') {
+
+ RGraph.DrawKey_gutter(obj, key, colors);
+
+
+ /**
+ * In-graph style key
+ */
+ } else if (keypos && keypos == 'graph') {
+
+ RGraph.DrawKey_graph(obj, key, colors);
+
+ } else {
+ alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos);
+ }
+ }
+
+
+
+
+
+ /**
+ * This does the actual drawing of the key when it's in the graph
+ *
+ * @param object obj The graph object
+ * @param array key The key items to draw
+ * @param array colors An aray of colors that the key will use
+ */
+ RGraph.DrawKey_graph = function (obj, key, colors)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size');
+ var text_font = obj.Get('chart.text.font');
+
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+
+ var hpos = obj.Get('chart.yaxispos') == 'right' ? gutterLeft + 10 : RGraph.GetWidth(obj) - gutterRight - 10;
+ var vpos = gutterTop + 10;
+ var title = obj.Get('chart.title');
+ var blob_size = text_size; // The blob of color
+ var hmargin = 8; // This is the size of the gaps between the blob of color and the text
+ var vmargin = 4; // This is the vertical margin of the key
+ var fillstyle = obj.Get('chart.key.background');
+ var strokestyle = '#333';
+ var height = 0;
+ var width = 0;
+
+
+ obj.coordsKey = [];
+
+
+ // Need to set this so that measuring the text works out OK
+ context.font = text_size + 'pt ' + obj.Get('chart.text.font');
+
+ // Work out the longest bit of text
+ for (i=0; i<key.length; ++i) {
+ width = Math.max(width, context.measureText(key[i]).width);
+ }
+
+ width += 5;
+ width += blob_size;
+ width += 5;
+ width += 5;
+ width += 5;
+
+ /**
+ * Now we know the width, we can move the key left more accurately
+ */
+ if ( obj.Get('chart.yaxispos') == 'left'
+ || (obj.type == 'pie' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'hbar' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'hbar' && obj.Get('chart.yaxispos') == 'center')
+ || (obj.type == 'rscatter' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'radar' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'rose' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'funnel' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'vprogress' && !obj.Get('chart.yaxispos'))
+ || (obj.type == 'hprogress' && !obj.Get('chart.yaxispos'))
+ ) {
+
+ hpos -= width;
+ }
+
+ /**
+ * Horizontal alignment
+ */
+ if (typeof(obj.Get('chart.key.halign')) == 'string') {
+ if (obj.Get('chart.key.halign') == 'left') {
+ hpos = gutterLeft + 10;
+ } else if (obj.Get('chart.key.halign') == 'right') {
+ hpos = RGraph.GetWidth(obj) - gutterRight - width;
+ }
+ }
+
+ /**
+ * Specific location coordinates
+ */
+ if (typeof(obj.Get('chart.key.position.x')) == 'number') {
+ hpos = obj.Get('chart.key.position.x');
+ }
+
+ if (typeof(obj.Get('chart.key.position.y')) == 'number') {
+ vpos = obj.Get('chart.key.position.y');
+ }
+
+
+ // Stipulate the shadow for the key box
+ if (obj.Get('chart.key.shadow')) {
+ context.shadowColor = obj.Get('chart.key.shadow.color');
+ context.shadowBlur = obj.Get('chart.key.shadow.blur');
+ context.shadowOffsetX = obj.Get('chart.key.shadow.offsetx');
+ context.shadowOffsetY = obj.Get('chart.key.shadow.offsety');
+ }
+
+
+
+
+ // Draw the box that the key resides in
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.key.background');
+ context.strokeStyle = 'black';
+
+
+ if (arguments[3] != false) {
+
+ context.lineWidth = obj.Get('chart.key.linewidth') ? obj.Get('chart.key.linewidth') : 1;
+
+ // The older square rectangled key
+ if (obj.Get('chart.key.rounded') == true) {
+ context.beginPath();
+ context.strokeStyle = strokestyle;
+ RGraph.strokedCurvyRect(context, hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key)),4);
+
+ context.stroke();
+ context.fill();
+
+ RGraph.NoShadow(obj);
+
+ } else {
+ context.strokeRect(hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key)));
+ context.fillRect(hpos, vpos, width - 5, 5 + ( (text_size + 5) * RGraph.getKeyLength(key)));
+ }
+ }
+
+ RGraph.NoShadow(obj);
+
+ context.beginPath();
+
+ // Draw the labels given
+ for (var i=key.length - 1; i>=0; i--) {
+
+ var j = Number(i) + 1;
+
+ // Draw the blob of color
+ if (obj.Get('chart.key.color.shape') == 'circle') {
+ context.beginPath();
+ context.strokeStyle = 'rgba(0,0,0,0)';
+ context.fillStyle = colors[i];
+ context.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0);
+ context.fill();
+
+ } else if (obj.Get('chart.key.color.shape') == 'line') {
+ context.beginPath();
+ context.strokeStyle = colors[i];
+ context.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
+ context.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
+ context.stroke();
+
+ } else {
+ context.fillStyle = colors[i];
+ context.fillRect(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size, text_size, text_size + 1);
+ }
+
+ context.beginPath();
+
+ context.fillStyle = 'black';
+
+ RGraph.Text(context,
+ text_font,
+ text_size,
+ hpos + blob_size + 5 + 5,
+ vpos + (5 * j) + (text_size * j),
+ key[i]);
+
+ if (obj.Get('chart.key.interactive')) {
+
+ var px = hpos + 5;
+ var py = vpos + (5 * j) + (text_size * j) - text_size;
+ var pw = width - 5 - 5 - 5;
+ var ph = text_size;
+
+
+ obj.coordsKey.push([px, py, pw, ph]);
+ }
+
+ }
+ context.fill();
+
+ /**
+ * Install the interactivity event handler
+ */
+ if (obj.Get('chart.key.interactive')) {
+
+ RGraph.Register(obj);
+
+ var key_mousemove = function (e)
+ {
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ for (var i=0; i<obj.coordsKey.length; ++i) {
+
+ var px = obj.coordsKey[i][0];
+ var py = obj.coordsKey[i][1];
+ var pw = obj.coordsKey[i][2];
+ var ph = obj.coordsKey[i][3];
+
+ if ( mouseX > (px-2) && mouseX < (px + pw + 2) && mouseY > (py - 2) && mouseY < (py + ph + 2) ) {
+
+ // Necessary?
+ //var index = obj.coordsKey.length - i - 1;
+
+ canvas.style.cursor = 'pointer';
+
+
+
+ return;
+ }
+
+ canvas.style.cursor = 'default';
+
+ if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(canvas_onmousemove_func) == 'function') {
+ canvas_onmousemove_func(e);
+ }
+ }
+ }
+ canvas.addEventListener('mousemove', key_mousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', key_mousemove);
+
+
+ var key_click = function (e)
+ {
+ RGraph.Redraw();
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ /**
+ * Hand over highlighting the pie chart key to another function
+ */
+ if (obj.type == 'pie') {
+ return key_onclick_pie(e);
+ }
+
+ RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
+
+ for (var i=0; i<obj.coordsKey.length; ++i) {
+
+ var px = obj.coordsKey[i][0];
+ var py = obj.coordsKey[i][1];
+ var pw = obj.coordsKey[i][2];
+ var ph = obj.coordsKey[i][3];
+
+ if ( mouseX > px && mouseX < (px + pw) && mouseY > py && mouseY < (py + ph) ) {
+
+ /**
+ * Loop thru all objects. If they're objects with
+ * chart.key.interactive enabled, redraw them
+ */
+ for (j in RGraph.objects) {
+ if (RGraph.objects[j] && RGraph.objects[j].Get && RGraph.objects[j].Get('chart.key.interactive')) {
+
+ if (RGraph.objects[j].Get('chart.exploded')) {
+ RGraph.objects[j].Set('chart.exploded', []);
+ }
+
+ RGraph.Clear(RGraph.objects[j].canvas);
+ RGraph.objects[j].Draw();
+ }
+ }
+ var index = obj.coordsKey.length - i - 1;
+
+ // HIGHLIGHT THE LINE HERE
+ context.beginPath();
+ context.fillStyle = 'rgba(255,255,255,0.9)';
+ context.fillRect(obj.Get('chart.gutter.left'),obj.Get('chart.gutter.top'),canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right'),canvas.height - obj.Get('chart.gutter.top') - obj.Get('chart.gutter.bottom'));
+ context.fill();
+
+ context.beginPath();
+ context.strokeStyle = obj.Get('chart.colors')[index];
+ context.lineWidth = obj.Get('chart.linewidth');
+ if (obj.coords2 &&obj.coords2[index] &&obj.coords2[index].length) {
+ for (var j=0; j<obj.coords2[index].length; ++j) {
+
+ var x = obj.coords2[index][j][0];
+ var y = obj.coords2[index][j][1];
+
+ if (j == 0) {
+ context.moveTo(x, y);
+ } else {
+ context.lineTo(x, y);
+ }
+ }
+ }
+ context.stroke();
+
+
+ context.lineWidth = 1;
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.fillStyle = 'white';
+
+ RGraph.SetShadow(obj, 'rgba(0,0,0,0.5)', 0,0,10);
+
+ context.strokeRect(px - 2, py - 2, pw + 4, ph + 4);
+ context.fillRect(px - 2, py - 2, pw + 4, ph + 4);
+
+ context.stroke();
+ context.fill();
+
+
+ RGraph.NoShadow(obj);
+
+
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.colors')[index];
+ context.fillRect(px, py, blob_size, blob_size);
+ context.fill();
+
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.text.color');
+
+ RGraph.Text(context,
+ obj.Get('chart.text.font'),
+ obj.Get('chart.text.size'),
+ px + 5 + blob_size,
+ py + ph,
+ obj.Get('chart.key')[obj.Get('chart.key').length - i - 1]
+ );
+ context.fill();
+
+
+ canvas.style.cursor = 'pointer';
+
+ e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ }
+ canvas.addEventListener('click', key_click, false);
+ RGraph.AddEventListener(canvas.id, 'click', key_click);
+
+ /**
+ * This function handles the Pie chart interactive key (the click event)
+ *
+ * @param object e The event object
+ */
+ var key_onclick_pie = function (e)
+ {
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = e.target.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ //RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
+
+ for (var i=0; i<obj.coordsKey.length; ++i) {
+
+ var px = obj.coordsKey[i][0];
+ var py = obj.coordsKey[i][1];
+ var pw = obj.coordsKey[i][2];
+ var ph = obj.coordsKey[i][3];
+
+ if ( mouseX > (px - 2) && mouseX < (px + pw + 2) && mouseY > (py - 2) && mouseY < (py + ph + 2) ) {
+
+ var index = obj.coordsKey.length - i - 1;
+
+
+ e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+
+
+
+ // ==========================================================================
+ var highlight_key = function ()
+ {
+ context.lineWidth = 1;
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.fillStyle = 'white';
+
+ RGraph.SetShadow(obj, 'rgba(0,0,0,0.5)', 0,0,10);
+
+ context.strokeRect(px - 2, py - 2, pw + 4, ph + 4);
+ context.fillRect(px - 2, py - 2, pw + 4, ph + 4);
+
+ context.stroke();
+ context.fill();
+
+
+ RGraph.NoShadow(obj);
+
+
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.colors')[index];
+ context.fillRect(px, py, blob_size, blob_size);
+ context.fill();
+
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.text.color');
+
+ RGraph.Text(context,
+ obj.Get('chart.text.font'),
+ obj.Get('chart.text.size'),
+ px + 5 + blob_size,
+ py + ph,
+ obj.Get('chart.key')[obj.Get('chart.key').length - i - 1]
+ );
+ context.fill();
+ }
+ // ==========================================================================
+
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 2;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 20);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 4;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 40);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 6;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 60);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 8;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 80);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 10;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 100);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 12;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 120);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 14;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 140);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 16;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 160);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 18;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 180);
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 20;RGraph.Clear(obj.canvas);obj.Draw();highlight_key();}, 200);
+
+ /**
+ * This is here so that when calling the Redraw function the Pie chart
+ * is drawn unexploded
+ */
+ setTimeout(function (){obj.Get('chart.exploded')[index] = 0;}, 250);
+
+ return;
+ } else {
+ e.cancelBubble = true;
+ if (e.stopPropagation) e.stopPropagation();
+ }
+ }
+
+ //obj.Set('chart.exploded', []);
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+
+ /**
+ * The window onclick for the pie chart
+ */
+ var key_interactive_click = function (e)
+ {
+ if (obj && obj.type == 'pie') {
+ obj.Set('chart.exploded', []);
+ }
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+ window.addEventListener('click', key_interactive_click, false);
+ RGraph.AddEventListener('window_' + canvas.id, 'click', key_interactive_click);
+ }
+ }
+
+
+
+
+
+
+ /**
+ * This does the actual drawing of the key when it's in the gutter
+ *
+ * @param object obj The graph object
+ * @param array key The key items to draw
+ * @param array colors An aray of colors that the key will use
+ */
+ RGraph.DrawKey_gutter = function (obj, key, colors)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var text_size = typeof(obj.Get('chart.key.text.size')) == 'number' ? obj.Get('chart.key.text.size') : obj.Get('chart.text.size');
+ var text_font = obj.Get('chart.text.font');
+
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+
+ var hpos = RGraph.GetWidth(obj) / 2;
+ var vpos = (gutterTop / 2) - 5;
+ var title = obj.Get('chart.title');
+ var blob_size = text_size; // The blob of color
+ var hmargin = 8; // This is the size of the gaps between the blob of color and the text
+ var vmargin = 4; // This is the vertical margin of the key
+ var fillstyle = obj.Get('chart.key.background');
+ var strokestyle = 'black';
+ var length = 0;
+
+
+
+ // Need to work out the length of the key first
+ context.font = text_size + 'pt ' + text_font;
+ for (i=0; i<key.length; ++i) {
+ length += hmargin;
+ length += blob_size;
+ length += hmargin;
+ length += context.measureText(key[i]).width;
+ }
+ length += hmargin;
+
+
+
+
+ /**
+ * Work out hpos since in the Pie it isn't necessarily dead center
+ */
+ if (obj.type == 'pie') {
+ if (obj.Get('chart.align') == 'left') {
+ var hpos = obj.radius + gutterLeft;
+
+ } else if (obj.Get('chart.align') == 'right') {
+ var hpos = obj.canvas.width - obj.radius - gutterRight;
+
+ } else {
+ hpos = canvas.width / 2;
+ }
+ }
+
+
+
+
+
+ /**
+ * This makes the key centered
+ */
+ hpos -= (length / 2);
+
+
+ /**
+ * Override the horizontal/vertical positioning
+ */
+ if (typeof(obj.Get('chart.key.position.x')) == 'number') {
+ hpos = obj.Get('chart.key.position.x');
+ }
+ if (typeof(obj.Get('chart.key.position.y')) == 'number') {
+ vpos = obj.Get('chart.key.position.y');
+ }
+
+
+
+ /**
+ * Draw the box that the key sits in
+ */
+ if (obj.Get('chart.key.position.gutter.boxed')) {
+
+ if (obj.Get('chart.key.shadow')) {
+ context.shadowColor = obj.Get('chart.key.shadow.color');
+ context.shadowBlur = obj.Get('chart.key.shadow.blur');
+ context.shadowOffsetX = obj.Get('chart.key.shadow.offsetx');
+ context.shadowOffsetY = obj.Get('chart.key.shadow.offsety');
+ }
+
+
+ context.beginPath();
+ context.fillStyle = fillstyle;
+ context.strokeStyle = strokestyle;
+
+ if (obj.Get('chart.key.rounded')) {
+ RGraph.strokedCurvyRect(context, hpos, vpos - vmargin, length, text_size + vmargin + vmargin)
+ // Odd... RGraph.filledCurvyRect(context, hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
+ } else {
+ context.strokeRect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
+ context.fillRect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
+ }
+
+ context.stroke();
+ context.fill();
+
+
+ RGraph.NoShadow(obj);
+ }
+
+
+ /**
+ * Draw the blobs of color and the text
+ */
+ for (var i=0, pos=hpos; i<key.length; ++i) {
+ pos += hmargin;
+
+ // Draw the blob of color - line
+ if (obj.Get('chart.key.color.shape') =='line') {
+
+ context.beginPath();
+ context.strokeStyle = colors[i];
+ context.moveTo(pos, vpos + (blob_size / 2));
+ context.lineTo(pos + blob_size, vpos + (blob_size / 2));
+ context.stroke();
+
+ // Circle
+ } else if (obj.Get('chart.key.color.shape') == 'circle') {
+
+ context.beginPath();
+ context.fillStyle = colors[i];
+ context.moveTo(pos, vpos + (blob_size / 2));
+ context.arc(pos + (blob_size / 2), vpos + (blob_size / 2), (blob_size / 2), 0, 6.28, 0);
+ context.fill();
+
+
+ } else {
+
+ context.beginPath();
+ context.fillStyle = colors[i];
+ context.fillRect(pos, vpos, blob_size, blob_size);
+ context.fill();
+ }
+
+ pos += blob_size;
+
+ pos += hmargin;
+
+ context.beginPath();
+ context.fillStyle = 'black';
+ RGraph.Text(context, text_font, text_size, pos, vpos + text_size - 1, key[i]);
+ context.fill();
+ pos += context.measureText(key[i]).width;
+ }
+ }
+
+
+ /**
+ * Returns the key length, but accounts for null values
+ *
+ * @param array key The key elements
+ */
+ RGraph.getKeyLength = function (key)
+ {
+ var len = 0;
+
+ for (var i=0; i<key.length; ++i) {
+ if (key[i] != null) {
+ ++len;
+ }
+ }
+
+ return len;
+ }
+
+
+
+
+
+
+ /**
+ * A shortcut for RGraph.pr()
+ */
+ function pd(variable)
+ {
+ RGraph.pr(variable);
+ }
+
+ function p(variable)
+ {
+ RGraph.pr(variable);
+ }
+
+ /**
+ * A shortcut for console.log - as used by Firebug and Chromes console
+ */
+ function cl (variable)
+ {
+ return console.log(variable);
+ }
+
+
+ /**
+ * Makes a clone of an object
+ *
+ * @param obj val The object to clone
+ */
+ RGraph.array_clone = function (obj)
+ {
+ if(obj == null || typeof(obj) != 'object') {
+ return obj;
+ }
+
+ var temp = [];
+
+ for (var i=0;i<obj.length; ++i) {
+
+ if (typeof(obj[i]) == 'number') {
+ temp[i] = (function (arg) {return Number(arg);})(obj[i]);
+ } else if (typeof(obj[i]) == 'string') {
+ temp[i] = (function (arg) {return String(arg);})(obj[i]);
+ } else {
+ temp[i] = RGraph.array_clone(obj[i]);
+ }
+ }
+
+ return temp;
+ }
+
+
+ /**
+ * This function reverses an array
+ */
+ RGraph.array_reverse = function (arr)
+ {
+ var newarr = [];
+
+ for (var i=arr.length - 1; i>=0; i--) {
+ newarr.push(arr[i]);
+ }
+
+ return newarr;
+ }
+
+
+ /**
+ * Formats a number with thousand seperators so it's easier to read
+ *
+ * @param integer num The number to format
+ * @param string The (optional) string to prepend to the string
+ * @param string The (optional) string to ap
+ * pend to the string
+ * @return string The formatted number
+ */
+ RGraph.number_format = function (obj, num)
+ {
+ var i;
+ var prepend = arguments[2] ? String(arguments[2]) : '';
+ var append = arguments[3] ? String(arguments[3]) : '';
+ var output = '';
+ var decimal = '';
+ var decimal_seperator = obj.Get('chart.scale.point') ? obj.Get('chart.scale.point') : '.';
+ var thousand_seperator = obj.Get('chart.scale.thousand') ? obj.Get('chart.scale.thousand') : ',';
+ RegExp.$1 = '';
+ var i,j;
+
+if (typeof(obj.Get('chart.scale.formatter')) == 'function') {
+ return obj.Get('chart.scale.formatter')(obj, num);
+}
+
+ // Ignore the preformatted version of "1e-2"
+ if (String(num).indexOf('e') > 0) {
+ return String(prepend + String(num) + append);
+ }
+
+ // We need then number as a string
+ num = String(num);
+
+ // Take off the decimal part - we re-append it later
+ if (num.indexOf('.') > 0) {
+ num = num.replace(/\.(.*)/, '');
+ decimal = RegExp.$1;
+ }
+
+ // Thousand seperator
+ //var seperator = arguments[1] ? String(arguments[1]) : ',';
+ var seperator = thousand_seperator;
+
+ /**
+ * Work backwards adding the thousand seperators
+ */
+ var foundPoint;
+ for (i=(num.length - 1),j=0; i>=0; j++,i--) {
+ var character = num.charAt(i);
+
+ if ( j % 3 == 0 && j != 0) {
+ output += seperator;
+ }
+
+ /**
+ * Build the output
+ */
+ output += character;
+ }
+
+ /**
+ * Now need to reverse the string
+ */
+ var rev = output;
+ output = '';
+ for (i=(rev.length - 1); i>=0; i--) {
+ output += rev.charAt(i);
+ }
+
+ // Tidy up
+ output = output.replace(/^-,/, '-');
+
+ // Reappend the decimal
+ if (decimal.length) {
+ output = output + decimal_seperator + decimal;
+ decimal = '';
+ RegExp.$1 = '';
+ }
+
+ // Minor bugette
+ if (output.charAt(0) == '-') {
+ output = output.replace(/-/, '');
+ prepend = '-' + prepend;
+ }
+
+ return prepend + output + append;
+ }
+
+
+ /**
+ * Draws horizontal coloured bars on something like the bar, line or scatter
+ */
+ RGraph.DrawBars = function (obj)
+ {
+ var hbars = obj.Get('chart.background.hbars');
+
+ /**
+ * Draws a horizontal bar
+ */
+ obj.context.beginPath();
+
+ for (i=0; i<hbars.length; ++i) {
+
+ // If null is specified as the "height", set it to the upper max value
+ if (hbars[i][1] == null) {
+ hbars[i][1] = obj.max;
+
+ // If the first index plus the second index is greater than the max value, adjust accordingly
+ } else if (hbars[i][0] + hbars[i][1] > obj.max) {
+ hbars[i][1] = obj.max - hbars[i][0];
+ }
+
+
+ // If height is negative, and the abs() value is greater than .max, use a negative max instead
+ if (Math.abs(hbars[i][1]) > obj.max) {
+ hbars[i][1] = -1 * obj.max;
+ }
+
+
+ // If start point is greater than max, change it to max
+ if (Math.abs(hbars[i][0]) > obj.max) {
+ hbars[i][0] = obj.max;
+ }
+
+ // If start point plus height is less than negative max, use the negative max plus the start point
+ if (hbars[i][0] + hbars[i][1] < (-1 * obj.max) ) {
+ hbars[i][1] = -1 * (obj.max + hbars[i][0]);
+ }
+
+ // If the X axis is at the bottom, and a negative max is given, warn the user
+ if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) {
+ alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center');
+ }
+
+ var ystart = (obj.grapharea - ((hbars[i][0] / obj.max) * obj.grapharea));
+ var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / obj.max) * obj.grapharea;
+
+ // Account for the X axis being in the center
+ if (obj.Get('chart.xaxispos') == 'center') {
+ ystart /= 2;
+ height /= 2;
+ }
+
+ ystart += obj.Get('chart.gutter.top')
+
+ var x = obj.Get('chart.gutter.left');
+ var y = ystart - height;
+ var w = obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right');
+ var h = height;
+
+ // Accommodate Opera :-/
+ if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) {
+ h *= -1;
+ y = y - h;
+ }
+
+ /**
+ * Account for X axis at the top
+ */
+ if (obj.Get('chart.xaxispos') == 'top') {
+ y = obj.canvas.height - y;
+ h *= -1;
+ }
+
+ obj.context.fillStyle = hbars[i][2];
+ obj.context.fillRect(x, y, w, h);
+ }
+
+ obj.context.fill();
+ }
+
+
+ /**
+ * Draws in-graph labels.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.DrawInGraphLabels = function (obj)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var labels = obj.Get('chart.labels.ingraph');
+ var labels_processed = [];
+
+ // Defaults
+ var fgcolor = 'black';
+ var bgcolor = 'white';
+ var direction = 1;
+
+ if (!labels) {
+ return;
+ }
+
+ /**
+ * Preprocess the labels array. Numbers are expanded
+ */
+ for (var i=0; i<labels.length; ++i) {
+ if (typeof(labels[i]) == 'number') {
+ for (var j=0; j<labels[i]; ++j) {
+ labels_processed.push(null);
+ }
+ } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
+ labels_processed.push(labels[i]);
+
+ } else {
+ labels_processed.push('');
+ }
+ }
+
+ /**
+ * Turn off any shadow
+ */
+ RGraph.NoShadow(obj);
+
+ if (labels_processed && labels_processed.length > 0) {
+
+ for (var i=0; i<labels_processed.length; ++i) {
+ if (labels_processed[i]) {
+ var coords = obj.coords[i];
+
+ if (coords && coords.length > 0) {
+ var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]);
+ var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]);
+ var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
+
+
+ context.beginPath();
+ context.fillStyle = 'black';
+ context.strokeStyle = 'black';
+
+
+ if (obj.type == 'bar') {
+
+ /**
+ * X axis at the top
+ */
+ if (obj.Get('chart.xaxispos') == 'top') {
+ length *= -1;
+ }
+
+ if (obj.Get('chart.variant') == 'dot') {
+ context.moveTo(x, obj.coords[i][1] - 5);
+ context.lineTo(x, obj.coords[i][1] - 5 - length);
+
+ var text_x = x;
+ var text_y = obj.coords[i][1] - 5 - length;
+
+ } else if (obj.Get('chart.variant') == 'arrow') {
+ context.moveTo(x, obj.coords[i][1] - 5);
+ context.lineTo(x, obj.coords[i][1] - 5 - length);
+
+ var text_x = x;
+ var text_y = obj.coords[i][1] - 5 - length;
+
+ } else {
+
+ context.arc(x, y, 2.5, 0, 6.28, 0);
+ context.moveTo(x, y);
+ context.lineTo(x, y - length);
+
+ var text_x = x;
+ var text_y = y - length;
+ }
+
+ context.stroke();
+ context.fill();
+
+
+ } else if (obj.type == 'line') {
+
+ if (
+ typeof(labels_processed[i]) == 'object' &&
+ typeof(labels_processed[i][3]) == 'number' &&
+ labels_processed[i][3] == -1
+ ) {
+
+ context.moveTo(x, y + 5);
+ context.lineTo(x, y + 5 + length);
+
+ context.stroke();
+ context.beginPath();
+
+ // This draws the arrow
+ context.moveTo(x, y + 5);
+ context.lineTo(x - 3, y + 10);
+ context.lineTo(x + 3, y + 10);
+ context.closePath();
+
+ var text_x = x;
+ var text_y = y + 5 + length;
+
+ } else {
+
+ var text_x = x;
+ var text_y = y - 5 - length;
+
+ context.moveTo(x, y - 5);
+ context.lineTo(x, y - 5 - length);
+
+ context.stroke();
+ context.beginPath();
+
+ // This draws the arrow
+ context.moveTo(x, y - 5);
+ context.lineTo(x - 3, y - 10);
+ context.lineTo(x + 3, y - 10);
+ context.closePath();
+ }
+
+ context.fill();
+ }
+
+ // Taken out on the 10th Nov 2010 - unnecessary
+ //var width = context.measureText(labels[i]).width;
+
+ context.beginPath();
+
+ // Fore ground color
+ context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
+
+ RGraph.Text(context,
+ obj.Get('chart.text.font'),
+ obj.Get('chart.text.size'),
+ text_x,
+ text_y,
+ (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
+ 'bottom',
+ 'center',
+ true,
+ null,
+ (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white');
+ context.fill();
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function "fills in" key missing properties that various implementations lack
+ *
+ * @param object e The event object
+ */
+ RGraph.FixEventObject = function (e)
+ {
+ if (RGraph.isIE8()) {
+
+ var e = event;
+
+ e.pageX = (event.clientX + document.body.scrollLeft);
+ e.pageY = (event.clientY + document.body.scrollTop);
+ e.target = event.srcElement;
+
+ if (!document.body.scrollTop && document.documentElement.scrollTop) {
+ e.pageX += parseInt(document.documentElement.scrollLeft);
+ e.pageY += parseInt(document.documentElement.scrollTop);
+ }
+ }
+
+ // This is mainly for FF which doesn't provide offsetX
+ if (typeof(e.offsetX) == 'undefined' && typeof(e.offsetY) == 'undefined') {
+ var coords = RGraph.getMouseXY(e);
+ e.offsetX = coords[0];
+ e.offsetY = coords[1];
+ }
+
+ // Any browser that doesn't implement stopPropagation() (MSIE)
+ if (!e.stopPropagation) {
+ e.stopPropagation = function () {window.event.cancelBubble = true;}
+ }
+
+ return e;
+ }
+
+
+ /**
+ * Draw crosshairs if enabled
+ *
+ * @param object obj The graph object (from which we can get the context and canvas as required)
+ */
+ RGraph.DrawCrosshairs = function (obj)
+ {
+ if (obj.Get('chart.crosshairs')) {
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ // 5th November 2010 - removed now that tooltips are DOM2 based.
+ //if (obj.Get('chart.tooltips') && obj.Get('chart.tooltips').length > 0) {
+ //alert('[' + obj.type.toUpperCase() + '] Sorry - you cannot have crosshairs enabled with tooltips! Turning off crosshairs...');
+ //obj.Set('chart.crosshairs', false);
+ //return;
+ //}
+
+ //canvas.onmousemove = function (e)
+ var crosshairs_mousemove = function (e)
+ {
+ var e = RGraph.FixEventObject(e);
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var width = canvas.width;
+ var height = canvas.height;
+ var adjustments = obj.Get('chart.tooltips.coords.adjust');
+
+
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+
+ if (typeof(adjustments) == 'object' && adjustments[0] && adjustments[1]) {
+ x = x - adjustments[0];
+ y = y - adjustments[1];
+ }
+
+ RGraph.Clear(canvas);
+ obj.Draw();
+
+ if ( x >= gutterLeft
+ && y >= gutterTop
+ && x <= (width - gutterRight)
+ && y <= (height - gutterBottom)
+ ) {
+
+ var linewidth = obj.Get('chart.crosshairs.linewidth');
+ context.lineWidth = linewidth ? linewidth : 1;
+
+ context.beginPath();
+ context.strokeStyle = obj.Get('chart.crosshairs.color');
+
+ // Draw a top vertical line
+ if (obj.Get('chart.crosshairs.vline')) {
+ context.moveTo(x, gutterTop);
+ context.lineTo(x, height - gutterBottom);
+ }
+
+ // Draw a horizontal line
+ if (obj.Get('chart.crosshairs.hline')) {
+ context.moveTo(gutterLeft, y);
+ context.lineTo(width - gutterRight, y);
+ }
+
+ context.stroke();
+
+
+ /**
+ * Need to show the coords?
+ */
+ if (obj.Get('chart.crosshairs.coords')) {
+ if (obj.type == 'scatter') {
+
+ var xCoord = (((x - obj.Get('chart.gutter.left')) / (obj.canvas.width - gutterLeft - gutterRight)) * (obj.Get('chart.xmax') - obj.Get('chart.xmin'))) + obj.Get('chart.xmin');
+ xCoord = xCoord.toFixed(obj.Get('chart.scale.decimals'));
+ var yCoord = obj.max - (((y - obj.Get('chart.gutter.top')) / (obj.canvas.height - gutterTop - gutterBottom)) * obj.max);
+
+ if (obj.type == 'scatter' && obj.Get('chart.xaxispos') == 'center') {
+ yCoord = (yCoord - (obj.max / 2)) * 2;
+ }
+
+ yCoord = yCoord.toFixed(obj.Get('chart.scale.decimals'));
+
+ var div = RGraph.Registry.Get('chart.coordinates.coords.div');
+ var mouseCoords = RGraph.getMouseXY(e);
+ var canvasXY = RGraph.getCanvasXY(canvas);
+
+ if (!div) {
+
+ div = document.createElement('DIV');
+ div.__object__ = obj;
+ div.style.position = 'absolute';
+ div.style.backgroundColor = 'white';
+ div.style.border = '1px solid black';
+ div.style.fontFamily = 'Arial, Verdana, sans-serif';
+ div.style.fontSize = '10pt'
+ div.style.padding = '2px';
+ div.style.opacity = 1;
+ div.style.WebkitBorderRadius = '3px';
+ div.style.borderRadius = '3px';
+ div.style.MozBorderRadius = '3px';
+ document.body.appendChild(div);
+
+ RGraph.Registry.Set('chart.coordinates.coords.div', div);
+ }
+
+ // Convert the X/Y pixel coords to correspond to the scale
+
+ div.style.opacity = 1;
+ div.style.display = 'inline';
+
+ if (!obj.Get('chart.crosshairs.coords.fixed')) {
+ div.style.left = Math.max(2, (e.pageX - div.offsetWidth - 3)) + 'px';
+ div.style.top = Math.max(2, (e.pageY - div.offsetHeight - 3)) + 'px';
+ } else {
+ div.style.left = canvasXY[0] + gutterLeft + 3 + 'px';
+ div.style.top = canvasXY[1] + gutterTop + 3 + 'px';
+ }
+
+ div.innerHTML = '<span style="color: #666">' + obj.Get('chart.crosshairs.coords.labels.x') + ':</span> ' + xCoord + '<br><span style="color: #666">' + obj.Get('chart.crosshairs.coords.labels.y') + ':</span> ' + yCoord;
+
+ canvas.addEventListener('mouseout', RGraph.HideCrosshairCoords, false);
+
+ obj.canvas.__crosshairs_labels__ = div;
+ obj.canvas.__crosshairs_x__ = xCoord;
+ obj.canvas.__crosshairs_y__ = yCoord;
+
+ } else {
+ alert('[RGRAPH] Showing crosshair coordinates is only supported on the Scatter chart');
+ }
+ }
+
+ /**
+ * Fire the oncrosshairs custom event
+ */
+ RGraph.FireCustomEvent(obj, 'oncrosshairs');
+
+ } else {
+ RGraph.HideCrosshairCoords();
+ }
+ }
+ canvas.addEventListener('mousemove', crosshairs_mousemove, false);
+ RGraph.AddEventListener(obj.id, 'mousemove', crosshairs_mousemove);
+ }
+ }
+
+ /**
+ * Thisz function hides the crosshairs coordinates
+ */
+ RGraph.HideCrosshairCoords = function ()
+ {
+ var div = RGraph.Registry.Get('chart.coordinates.coords.div');
+
+ if ( div
+ && div.style.opacity == 1
+ && div.__object__.Get('chart.crosshairs.coords.fadeout')
+ ) {
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.9;}, 50);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.8;}, 100);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.7;}, 150);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.6;}, 200);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.5;}, 250);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.4;}, 300);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.3;}, 350);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.2;}, 400);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0.1;}, 450);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.opacity = 0;}, 500);
+ setTimeout(function() {RGraph.Registry.Get('chart.coordinates.coords.div').style.display = 'none';}, 550);
+ }
+ }
+
+
+ /**
+ * Trims the right hand side of a string. Removes SPACE, TAB
+ * CR and LF.
+ *
+ * @param string str The string to trim
+ */
+ RGraph.rtrim = function (str)
+ {
+ return str.replace(/( |\n|\r|\t)+$/, '');
+ }
+
+
+ /**
+ * Draws the3D axes/background
+ */
+ RGraph.Draw3DAxes = function (obj)
+ {
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+
+ var context = obj.context;
+ var canvas = obj.canvas;
+
+ context.strokeStyle = '#aaa';
+ context.fillStyle = '#ddd';
+
+ // Draw the vertical left side
+ context.beginPath();
+ context.moveTo(gutterLeft, gutterTop);
+ context.lineTo(gutterLeft + 10, gutterTop - 5);
+ context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5);
+ context.lineTo(gutterLeft, canvas.height - gutterBottom);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ // Draw the bottom floor
+ context.beginPath();
+ context.moveTo(gutterLeft, canvas.height - gutterBottom);
+ context.lineTo(gutterLeft + 10, canvas.height - gutterBottom - 5);
+ context.lineTo(canvas.width - gutterRight + 10, canvas.height - gutterBottom - 5);
+ context.lineTo(canvas.width - gutterRight, canvas.height - gutterBottom);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+ }
+
+ /**
+ * Turns off any shadow
+ *
+ * @param object obj The graph object
+ */
+ RGraph.NoShadow = function (obj)
+ {
+ obj.context.shadowColor = 'rgba(0,0,0,0)';
+ obj.context.shadowBlur = 0;
+ obj.context.shadowOffsetX = 0;
+ obj.context.shadowOffsetY = 0;
+ }
+
+
+ /**
+ * Sets the four shadow properties - a shortcut function
+ *
+ * @param object obj Your graph object
+ * @param string color The shadow color
+ * @param number offsetx The shadows X offset
+ * @param number offsety The shadows Y offset
+ * @param number blur The blurring effect applied to the shadow
+ */
+ RGraph.SetShadow = function (obj, color, offsetx, offsety, blur)
+ {
+ obj.context.shadowColor = color;
+ obj.context.shadowOffsetX = offsetx;
+ obj.context.shadowOffsetY = offsety;
+ obj.context.shadowBlur = blur;
+ }
+
+
+ /**
+ * This function attempts to "fill in" missing functions from the canvas
+ * context object. Only two at the moment - measureText() nd fillText().
+ *
+ * @param object context The canvas 2D context
+ */
+ RGraph.OldBrowserCompat = function (context)
+ {
+ if (!context.measureText) {
+
+ // This emulates the measureText() function
+ context.measureText = function (text)
+ {
+ var textObj = document.createElement('DIV');
+ textObj.innerHTML = text;
+ textObj.style.backgroundColor = 'white';
+ textObj.style.position = 'absolute';
+ textObj.style.top = -100
+ textObj.style.left = 0;
+ document.body.appendChild(textObj);
+
+ var width = {width: textObj.offsetWidth};
+
+ textObj.style.display = 'none';
+
+ return width;
+ }
+ }
+
+ if (!context.fillText) {
+ // This emulates the fillText() method
+ context.fillText = function (text, targetX, targetY)
+ {
+ return false;
+ }
+ }
+
+ // If IE8, add addEventListener()
+ if (!context.canvas.addEventListener) {
+ window.addEventListener = function (ev, func, bubble)
+ {
+ return this.attachEvent('on' + ev, func);
+ }
+
+ context.canvas.addEventListener = function (ev, func, bubble)
+ {
+ return this.attachEvent('on' + ev, func);
+ }
+ }
+ }
+
+
+
+ /**
+ * This is a function that can be used to run code asynchronously, which can
+ * be used to speed up the loading of you pages.
+ *
+ * @param string func This is the code to run. It can also be a function pointer.
+ * The front page graphs show this function in action. Basically
+ * each graphs code is made in a function, and that function is
+ * passed to this function to run asychronously.
+ */
+ RGraph.Async = function (func)
+ {
+ return setTimeout(func, arguments[1] ? arguments[1] : 1);
+ }
+
+
+ /**
+ * A custom random number function
+ *
+ * @param number min The minimum that the number should be
+ * @param number max The maximum that the number should be
+ * @param number How many decimal places there should be. Default for this is 0
+ */
+ RGraph.random = function (min, max)
+ {
+ var dp = arguments[2] ? arguments[2] : 0;
+ var r = Math.random();
+
+ return Number((((max - min) * r) + min).toFixed(dp));
+ }
+
+
+ /**
+ * Draws a rectangle with curvy corners
+ *
+ * @param context object The context
+ * @param x number The X coordinate (top left of the square)
+ * @param y number The Y coordinate (top left of the square)
+ * @param w number The width of the rectangle
+ * @param h number The height of the rectangle
+ * @param number The radius of the curved corners
+ * @param boolean Whether the top left corner is curvy
+ * @param boolean Whether the top right corner is curvy
+ * @param boolean Whether the bottom right corner is curvy
+ * @param boolean Whether the bottom left corner is curvy
+ */
+ RGraph.strokedCurvyRect = function (context, x, y, w, h)
+ {
+ // The corner radius
+ var r = arguments[5] ? arguments[5] : 3;
+
+ // The corners
+ var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
+ var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
+ var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
+ var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
+
+ context.beginPath();
+
+ // Top left side
+ context.moveTo(x + (corner_tl ? r : 0), y);
+ context.lineTo(x + w - (corner_tr ? r : 0), y);
+
+ // Top right corner
+ if (corner_tr) {
+ context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2, false);
+ }
+
+ // Top right side
+ context.lineTo(x + w, y + h - (corner_br ? r : 0) );
+
+ // Bottom right corner
+ if (corner_br) {
+ context.arc(x + w - r, y - r + h, r, Math.PI * 2, Math.PI * 0.5, false);
+ }
+
+ // Bottom right side
+ context.lineTo(x + (corner_bl ? r : 0), y + h);
+
+ // Bottom left corner
+ if (corner_bl) {
+ context.arc(x + r, y - r + h, r, Math.PI * 0.5, Math.PI, false);
+ }
+
+ // Bottom left side
+ context.lineTo(x, y + (corner_tl ? r : 0) );
+
+ // Top left corner
+ if (corner_tl) {
+ context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5, false);
+ }
+
+ context.stroke();
+ }
+
+
+ /**
+ * Draws a filled rectangle with curvy corners
+ *
+ * @param context object The context
+ * @param x number The X coordinate (top left of the square)
+ * @param y number The Y coordinate (top left of the square)
+ * @param w number The width of the rectangle
+ * @param h number The height of the rectangle
+ * @param number The radius of the curved corners
+ * @param boolean Whether the top left corner is curvy
+ * @param boolean Whether the top right corner is curvy
+ * @param boolean Whether the bottom right corner is curvy
+ * @param boolean Whether the bottom left corner is curvy
+ */
+ RGraph.filledCurvyRect = function (context, x, y, w, h)
+ {
+ // The corner radius
+ var r = arguments[5] ? arguments[5] : 3;
+
+ // The corners
+ var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
+ var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
+ var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
+ var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
+
+ context.beginPath();
+
+ // First draw the corners
+
+ // Top left corner
+ if (corner_tl) {
+ context.moveTo(x + r, y + r);
+ context.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI, false);
+ } else {
+ context.fillRect(x, y, r, r);
+ }
+
+ // Top right corner
+ if (corner_tr) {
+ context.moveTo(x + w - r, y + r);
+ context.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0, false);
+ } else {
+ context.moveTo(x + w - r, y);
+ context.fillRect(x + w - r, y, r, r);
+ }
+
+
+ // Bottom right corner
+ if (corner_br) {
+ context.moveTo(x + w - r, y + h - r);
+ context.arc(x + w - r, y - r + h, r, 0, Math.PI / 2, false);
+ } else {
+ context.moveTo(x + w - r, y + h - r);
+ context.fillRect(x + w - r, y + h - r, r, r);
+ }
+
+ // Bottom left corner
+ if (corner_bl) {
+ context.moveTo(x + r, y + h - r);
+ context.arc(x + r, y - r + h, r, Math.PI / 2, Math.PI, false);
+ } else {
+ context.moveTo(x, y + h - r);
+ context.fillRect(x, y + h - r, r, r);
+ }
+
+ // Now fill it in
+ context.fillRect(x + r, y, w - r - r, h);
+ context.fillRect(x, y + r, r + 1, h - r - r);
+ context.fillRect(x + w - r - 1, y + r, r + 1, h - r - r);
+
+ context.fill();
+ }
+
+
+ /**
+ * A crude timing function
+ *
+ * @param string label The label to use for the time
+ */
+ RGraph.Timer = function (label)
+ {
+ var d = new Date();
+
+ // This uses the Firebug console
+ console.log(label + ': ' + d.getSeconds() + '.' + d.getMilliseconds());
+ }
+
+
+ /**
+ * Hides the palette if it's visible
+ */
+ RGraph.HidePalette = function ()
+ {
+ var div = RGraph.Registry.Get('palette');
+
+ if (typeof(div) == 'object' && div) {
+ div.style.visibility = 'hidden';
+ div.style.display = 'none';
+ RGraph.Registry.Set('palette', null);
+ }
+ }
+
+
+ /**
+ * Hides the zoomed canvas
+ */
+ RGraph.HideZoomedCanvas = function ()
+ {
+ var interval = 15;
+ var frames = 10;
+
+ if (typeof(__zoomedimage__) == 'object') {
+ obj = __zoomedimage__.obj;
+ } else {
+ return;
+ }
+
+ if (obj.Get('chart.zoom.fade.out')) {
+ for (var i=frames,j=1; i>=0; --i, ++j) {
+ if (typeof(__zoomedimage__) == 'object') {
+ setTimeout("__zoomedimage__.style.opacity = " + String(i / 10), j * interval);
+ }
+ }
+
+ if (typeof(__zoomedbackground__) == 'object') {
+ setTimeout("__zoomedbackground__.style.opacity = " + String(i / frames), j * interval);
+ }
+ }
+
+ if (typeof(__zoomedimage__) == 'object') {
+ setTimeout("__zoomedimage__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0);
+ }
+
+ if (typeof(__zoomedbackground__) == 'object') {
+ setTimeout("__zoomedbackground__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? (frames * interval) + 10 : 0);
+ }
+ }
+
+
+ /**
+ * Adds an event handler
+ *
+ * @param object obj The graph object
+ * @param string event The name of the event, eg ontooltip
+ * @param object func The callback function
+ */
+ RGraph.AddCustomEventListener = function (obj, name, func)
+ {
+ if (typeof(RGraph.events[obj.id]) == 'undefined') {
+ RGraph.events[obj.id] = [];
+ }
+
+ RGraph.events[obj.id].push([obj, name, func]);
+
+ return RGraph.events[obj.id].length - 1;
+ }
+
+
+ /**
+ * Used to fire one of the RGraph custom events
+ *
+ * @param object obj The graph object that fires the event
+ * @param string event The name of the event to fire
+ */
+ RGraph.FireCustomEvent = function (obj, name)
+ {
+ if (obj && obj.isRGraph) {
+ var id = obj.id;
+
+ if ( typeof(id) == 'string'
+ && typeof(RGraph.events) == 'object'
+ && typeof(RGraph.events[id]) == 'object'
+ && RGraph.events[id].length > 0) {
+
+ for(var j=0; j<RGraph.events[id].length; ++j) {
+ if (RGraph.events[id][j] && RGraph.events[id][j][1] == name) {
+ RGraph.events[id][j][2](obj);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Checks the browser for traces of MSIE8
+ */
+ RGraph.isIE8 = function ()
+ {
+ return navigator.userAgent.indexOf('MSIE 8') > 0;
+ }
+
+
+ /**
+ * Checks the browser for traces of MSIE9
+ */
+ RGraph.isIE9 = function ()
+ {
+ return navigator.userAgent.indexOf('MSIE 9') > 0;
+ }
+
+
+ /**
+ * Checks the browser for traces of MSIE9
+ */
+ RGraph.isIE9up = function ()
+ {
+ navigator.userAgent.match(/MSIE (\d+)/);
+
+ return Number(RegExp.$1) >= 9;
+ }
+
+
+ /**
+ * This clears a canvases event handlers. Used at the start of each graphs .Draw() method.
+ *
+ * @param string id The ID of the canvas whose event handlers will be cleared
+ */
+ RGraph.ClearEventListeners = function (id)
+ {
+ for (var i=0; i<RGraph.Registry.Get('chart.event.handlers').length; ++i) {
+
+ var el = RGraph.Registry.Get('chart.event.handlers')[i];
+
+ if (el && (el[0] == id || el[0] == ('window_' + id))) {
+ if (el[0].substring(0, 7) == 'window_') {
+ window.removeEventListener(el[1], el[2], false);
+ } else {
+ if (document.getElementById(id)) {
+ document.getElementById(id).removeEventListener(el[1], el[2], false);
+ }
+ }
+
+ RGraph.Registry.Get('chart.event.handlers')[i] = null;
+ }
+ }
+ }
+
+
+ /**
+ *
+ */
+ RGraph.AddEventListener = function (id, e, func)
+ {
+ var type = arguments[3] ? arguments[3] : 'unknown';
+
+ RGraph.Registry.Get('chart.event.handlers').push([id, e, func, type]);
+ }
+
+
+ /**
+ * This function suggests a gutter size based on the widest left label. Given that the bottom
+ * labels may be longer, this may be a little out.
+ *
+ * @param object obj The graph object
+ * @param array data An array of graph data
+ * @return int A suggested gutter setting
+ */
+ RGraph.getGutterSuggest = function (obj, data)
+ {
+ var str = RGraph.number_format(obj, RGraph.array_max(RGraph.getScale(RGraph.array_max(data), obj)), obj.Get('chart.units.pre'), obj.Get('chart.units.post'));
+
+ // Take into account the HBar
+ if (obj.type == 'hbar') {
+
+ var str = '';
+ var len = 0;
+
+ for (var i=0; i<obj.Get('chart.labels').length; ++i) {
+ str = (obj.Get('chart.labels').length > str.length ? obj.Get('chart.labels')[i] : str);
+ }
+ }
+
+ obj.context.font = obj.Get('chart.text.size') + 'pt ' + obj.Get('chart.text.font');
+
+ len = obj.context.measureText(str).width + 5;
+
+ return (obj.type == 'hbar' ? len / 3 : len);
+ }
+
+
+ /**
+ * A basic Array shift gunction
+ *
+ * @param object The numerical array to work on
+ * @return The new array
+ */
+ RGraph.array_shift = function (arr)
+ {
+ var ret = [];
+
+ for (var i=1; i<arr.length; ++i) ret.push(arr[i]);
+
+ return ret;
+ }
+
+
+ /**
+ * If you prefer, you can use the SetConfig() method to set the configuration information
+ * for your chart. You may find that setting the configuration this way eases reuse.
+ *
+ * @param object obj The graph object
+ * @param object config The graph configuration information
+ */
+ RGraph.SetConfig = function (obj, c)
+ {
+ for (i in c) {
+ if (typeof(i) == 'string') {
+ obj.Set(i, c[i]);
+ }
+ }
+
+ return obj;
+ }
+
+
+ /**
+ * This function gets the canvas height. Defaults to the actual
+ * height but this can be changed by setting chart.height.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.GetHeight = function (obj)
+ {
+ return obj.canvas.height;
+ }
+
+
+ /**
+ * This function gets the canvas width. Defaults to the actual
+ * width but this can be changed by setting chart.width.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.GetWidth = function (obj)
+ {
+ return obj.canvas.width;
+ }
+
+
+ /**
+ * Clears all the custom event listeners that have been registered
+ *
+ * @param string Limits the clearing to this object ID
+ */
+ RGraph.RemoveAllCustomEventListeners = function ()
+ {
+ var id = arguments[0];
+
+ if (id && RGraph.events[id]) {
+ RGraph.events[id] = [];
+ } else {
+ RGraph.events = [];
+ }
+ }
+
+
+ /**
+ * Clears a particular custom event listener
+ *
+ * @param object obj The graph object
+ * @param number i This is the index that is return by .AddCustomEventListener()
+ */
+ RGraph.RemoveCustomEventListener = function (obj, i)
+ {
+ if ( typeof(RGraph.events) == 'object'
+ && typeof(RGraph.events[obj.id]) == 'object'
+ && typeof(RGraph.events[obj.id][i]) == 'object') {
+
+ RGraph.events[obj.id][i] = null;
+ }
+ }
+
+
+ /**
+ * This draws the background
+ *
+ * @param object obj The graph object
+ */
+ RGraph.DrawBackgroundImage = function (obj)
+ {
+ var img = new Image();
+ img.__object__ = obj;
+ img.__canvas__ = obj.canvas;
+ img.__context__ = obj.context;
+ img.src = obj.Get('chart.background.image');
+
+ obj.__background_image__ = img;
+
+ img.onload = function ()
+ {
+ var obj = this.__object__;
+
+ var gutterLeft = obj.Get('chart.gutter.left');
+ var gutterRight = obj.Get('chart.gutter.right');
+ var gutterTop = obj.Get('chart.gutter.top');
+ var gutterBottom = obj.Get('chart.gutter.bottom');
+
+ RGraph.Clear(obj.canvas);
+
+ obj.context.drawImage(this,gutterLeft,gutterTop, RGraph.GetWidth(obj) - gutterLeft - gutterRight, RGraph.GetHeight(obj) - gutterTop - gutterBottom);
+
+ // Draw the graph
+ obj.Draw();
+ }
+
+ img.onerror = function ()
+ {
+ var obj = this.__canvas__.__object__;
+
+ // Show an error alert
+ alert('[ERROR] There was an error with the background image that you specified: ' + img.src);
+
+ // Draw the graph, because the onload doesn't fire, and thus that won't draw the chart
+ obj.Draw();
+ }
+ }
+
+
+ /**
+ * This resets the canvas. Keep in mind that any translate() that has been performed will also be reset.
+ *
+ * @param object canvas The canvas
+ */
+ RGraph.Reset = function (canvas)
+ {
+ canvas.width = canvas.width;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.effects.js b/schall/static/RGraph/libraries/RGraph.common.effects.js
new file mode 100644
index 0000000..cb63371
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.effects.js
@@ -0,0 +1,1516 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ /**
+ * This is a library of a few functions that make it easier to do
+ * effects like fade-ins or eaxpansion.
+ */
+
+ /**
+ * Initialise the various objects
+ */
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+ RGraph.Effects = {}
+ RGraph.Effects.Fade = {}
+ RGraph.Effects.jQuery = {}
+ RGraph.Effects.jQuery.HBlinds = {}
+ RGraph.Effects.jQuery.VBlinds = {}
+ RGraph.Effects.jQuery.Slide = {}
+ RGraph.Effects.Pie = {}
+ RGraph.Effects.Bar = {}
+ RGraph.Effects.Line = {}
+ RGraph.Effects.Line.jQuery = {}
+ RGraph.Effects.Fuel = {}
+ RGraph.Effects.Rose = {}
+ RGraph.Effects.Odo = {}
+ RGraph.Effects.Gauge = {}
+ RGraph.Effects.Meter = {}
+ RGraph.Effects.HBar = {}
+ RGraph.Effects.Radar = {}
+ RGraph.Effects.Waterfall = {}
+
+
+ /**
+ * Fadein
+ *
+ * This function simply uses the CSS opacity property - initially set to zero and
+ * increasing to 1 over the period of 0.5 second
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.Fade.In = function (obj)
+ {
+ var canvas = obj.canvas;
+
+ // Initially the opacity should be zero
+ canvas.style.opacity = 0;
+
+ // Draw the chart
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ // Now fade the chart in
+ for (var i=1; i<=10; ++i) {
+ setTimeout('document.getElementById("' + canvas.id + '").style.opacity = ' + (i * 0.1), i * 50);
+ }
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], 500);
+ }
+ }
+
+
+ /**
+ * Fadeout
+ *
+ * This function is a reversal of the above function - fading out instead of in
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.Fade.Out = function (obj)
+ {
+ var canvas = obj.canvas;
+
+ // Draw the chart
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ // Now fade the chart in
+ for (var i=10; i>=0; --i) {
+ setTimeout('document.getElementById("' + canvas.id + '").style.opacity = ' + (i * 0.1), (10 - i) * 50);
+ }
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], 500);
+ }
+ }
+
+
+ /**
+ * Expand
+ *
+ * This effect is like the tooltip effect of the same name. I starts in the middle
+ * and expands out to full size.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.jQuery.Expand = function (obj)
+ {
+ // Check for jQuery
+ if (typeof(jQuery) == 'undefined') {
+ alert('[ERROR] Could not find jQuery object - have you included the jQuery file?');
+ }
+
+ var canvas = obj.canvas;
+
+ if (!canvas.__rgraph_div_placeholder__) {
+ var div = RGraph.Effects.ReplaceCanvasWithDIV(canvas);
+ canvas.__rgraph_div_placeholder__ = div;
+ } else {
+ div = canvas.__rgraph_div_placeholder__;
+ }
+
+ canvas.style.position = 'relative';
+ canvas.style.top = (canvas.height / 2) + 'px';
+ canvas.style.left = (canvas.width / 2) + 'px';
+
+
+ canvas.style.width = 0;
+ canvas.style.height = 0;
+
+
+ canvas.style.opacity = 0;
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ $('#' + obj.id).animate({
+ opacity: 1,
+ width: parseInt(div.style.width) + 'px',
+ height: parseInt(div.style.height) + 'px',
+ left: '-=' + (obj.canvas.width / 2) + 'px',
+ top: '-=' + (obj.canvas.height / 2) + 'px'
+ }, 1000);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], 1000);
+ }
+ }
+
+
+ /**
+ * A function used to replace the canvas witha Div, which inturn holds the canvas. This way the page
+ * layout doesn't shift in the canvas is resized.
+ *
+ * @param object canvas The canvas to replace.
+ */
+ RGraph.Effects.ReplaceCanvasWithDIV = function (canvas)
+ {
+ if (!canvas.replacementDIV) {
+ // Create the place holder DIV
+ var div = document.createElement('DIV');
+ div.style.width = canvas.width + 'px';
+ div.style.height = canvas.height + 'px';
+ div.style.cssFloat = canvas.style.cssFloat;
+ div.style.left = canvas.style.left;
+ div.style.top = canvas.style.top;
+ //div.style.position = canvas.style.position;
+ div.style.display = 'inline-block';
+ canvas.parentNode.insertBefore(div, canvas);
+
+
+ // Remove the canvas from the document
+ canvas.parentNode.removeChild(canvas);
+
+ // Add it back in as a child of the place holder
+ div.appendChild(canvas);
+
+ // Reset the positioning information on the canvas
+ canvas.style.position = 'relative';
+ canvas.style.left = (div.offsetWidth / 2) + 'px';
+ canvas.style.top = (div.offsetHeight / 2) + 'px';
+ canvas.style.cssFloat = '';
+
+ // Add a reference to the canvas to the DIV so that repeated plays of the anumation
+ // don't keep replacing the canvas with a new DIV
+ canvas.replacementDIV = div;
+
+ } else {
+ var div = canvas.replacementDIV;
+ }
+
+ return div;
+ }
+
+
+ /**
+ * Snap
+ *
+ * Similar to the tooltip effect of the same name, this moves the canvas in from the top left corner
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.jQuery.Snap = function (obj)
+ {
+ var delay = 500;
+
+ var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas);
+
+ obj.canvas.style.position = 'absolute';
+ obj.canvas.style.top = 0;
+ obj.canvas.style.left = 0;
+ obj.canvas.style.width = 0;
+ obj.canvas.style.height = 0;
+ obj.canvas.style.opacity = 0;
+
+ var targetLeft = div.offsetLeft;
+ var targetTop = div.offsetTop;
+ var targetWidth = div.offsetWidth;
+ var targetHeight = div.offsetHeight;
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ $('#' + obj.id).animate({
+ opacity: 1,
+ width: targetWidth + 'px',
+ height: targetHeight + 'px',
+ left: targetLeft + 'px',
+ top: targetTop + 'px'
+ }, delay);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay + 50);
+ }
+ }
+
+
+ /**
+ * Reveal
+ *
+ * This effect issmilat to the Expand effect - the canvas is slowly revealed from
+ * the centre outwards
+ *
+ * @param object obj The chart object
+ */
+ RGraph.Effects.jQuery.Reveal = function (obj)
+ {
+ var opts = arguments[1] ? arguments[1] : null;
+ var delay = 1000;
+ var canvas = obj.canvas;
+ var xy = RGraph.getCanvasXY(obj.canvas);
+
+ var divs = [
+ ['reveal_left', xy[0], xy[1], obj.canvas.width / 2, obj.canvas.height],
+ ['reveal_right',(xy[0] + (obj.canvas.width / 2)),xy[1],(obj.canvas.width / 2),obj.canvas.height],
+ ['reveal_top',xy[0],xy[1],obj.canvas.width,(obj.canvas.height / 2)],
+ ['reveal_bottom',xy[0],(xy[1] + (obj.canvas.height / 2)),obj.canvas.width,(obj.canvas.height / 2)]
+ ];
+
+ for (var i=0; i<divs.length; ++i) {
+ var div = document.createElement('DIV');
+ div.id = divs[i][0];
+ div.style.width = divs[i][3]+ 'px';
+ div.style.height = divs[i][4] + 'px';
+ div.style.left = divs[i][1] + 'px';
+ div.style.top = divs[i][2] + 'px';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = opts && typeof(opts['color']) == 'string' ? opts['color'] : 'white';
+ document.body.appendChild(div);
+ }
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ $('#reveal_left').animate({width: 0}, delay);
+ $('#reveal_right').animate({left: '+=' + (obj.canvas.width / 2),width: 0}, delay);
+ $('#reveal_top').animate({height: 0}, delay);
+ $('#reveal_bottom').animate({top: '+=' + (obj.canvas.height / 2),height: 0}, delay);
+
+ // Remove the DIVs from the DOM 100ms after the animation ends
+ setTimeout(
+ function ()
+ {
+ document.body.removeChild(document.getElementById("reveal_top"))
+ document.body.removeChild(document.getElementById("reveal_bottom"))
+ document.body.removeChild(document.getElementById("reveal_left"))
+ document.body.removeChild(document.getElementById("reveal_right"))
+ }
+ , delay);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Horizontal Blinds (open)
+ *
+ * @params object obj The graph object
+ */
+ RGraph.Effects.jQuery.HBlinds.Open = function (obj)
+ {
+ var canvas = obj.canvas;
+ var opts = arguments[1] ? arguments[1] : [];
+ var delay = 1000;
+ var color = opts['color'] ? opts['color'] : 'white';
+ var xy = RGraph.getCanvasXY(canvas);
+ var height = canvas.height / 5;
+
+ /**
+ * First draw the chart
+ */
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ for (var i=0; i<5; ++i) {
+ var div = document.createElement('DIV');
+ div.id = 'blinds_' + i;
+ div.style.width = canvas.width + 'px';
+ div.style.height = height + 'px';
+ div.style.left = xy[0] + 'px';
+ div.style.top = (xy[1] + (canvas.height * (i / 5))) + 'px';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = color;
+ document.body.appendChild(div);
+
+ $('#blinds_' + i).animate({height: 0}, delay);
+ }
+
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Horizontal Blinds (close)
+ *
+ * @params object obj The graph object
+ */
+ RGraph.Effects.jQuery.HBlinds.Close = function (obj)
+ {
+ var canvas = obj.canvas;
+ var opts = arguments[1] ? arguments[1] : [];
+ var delay = 1000;
+ var color = opts['color'] ? opts['color'] : 'white';
+ var xy = RGraph.getCanvasXY(canvas);
+ var height = canvas.height / 5;
+
+ for (var i=0; i<5; ++i) {
+ var div = document.createElement('DIV');
+ div.id = 'blinds_' + i;
+ div.style.width = canvas.width + 'px';
+ div.style.height = 0;
+ div.style.left = xy[0] + 'px';
+ div.style.top = (xy[1] + (canvas.height * (i / 5))) + 'px';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = color;
+ document.body.appendChild(div);
+
+ $('#blinds_' + i).animate({height: height + 'px'}, delay);
+ }
+
+ setTimeout(function () {RGraph.Clear(obj.canvas);}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Vertical Blinds (open)
+ *
+ * @params object obj The graph object
+ */
+ RGraph.Effects.jQuery.VBlinds.Open = function (obj)
+ {
+ var canvas = obj.canvas;
+ var opts = arguments[1] ? arguments[1] : [];
+ var delay = 1000;
+ var color = opts['color'] ? opts['color'] : 'white';
+ var xy = RGraph.getCanvasXY(canvas);
+ var width = canvas.width / 10;
+
+ /**
+ * First draw the chart
+ */
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ for (var i=0; i<10; ++i) {
+ var div = document.createElement('DIV');
+ div.id = 'blinds_' + i;
+ div.style.width = width + 'px';
+ div.style.height = canvas.height + 'px';
+ div.style.left = (xy[0] + (canvas.width * (i / 10))) + 'px';
+ div.style.top = (xy[1]) + 'px';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = color;
+ document.body.appendChild(div);
+
+ $('#blinds_' + i).animate({width: 0}, delay);
+ }
+
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_5'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_6'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_7'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_8'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_9'));}, delay + 100);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Vertical Blinds (close)
+ *
+ * @params object obj The graph object
+ */
+ RGraph.Effects.jQuery.VBlinds.Close = function (obj)
+ {
+ var canvas = obj.canvas;
+ var opts = arguments[1] ? arguments[1] : [];
+ var delay = 1000;
+ var color = opts['color'] ? opts['color'] : 'white';
+ var xy = RGraph.getCanvasXY(canvas);
+ var width = canvas.width / 10;
+
+ // Don't draw the chart
+
+ for (var i=0; i<10; ++i) {
+ var div = document.createElement('DIV');
+ div.id = 'blinds_' + i;
+ div.style.width = 0;
+ div.style.height = canvas.height + 'px';
+ div.style.left = (xy[0] + (canvas.width * (i / 10))) + 'px';
+ div.style.top = (xy[1]) + 'px';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = color;
+ document.body.appendChild(div);
+
+ $('#blinds_' + i).animate({width: width}, delay);
+ }
+
+ setTimeout(function () {RGraph.Clear(obj.canvas, color);}, delay + 100);
+
+ if (opts['remove']) {
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_0'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_1'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_2'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_3'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_4'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_5'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_6'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_7'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_8'));}, delay + 100);
+ setTimeout(function () {document.body.removeChild(document.getElementById('blinds_9'));}, delay + 100);
+ }
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Pie chart grow
+ *
+ * Gradually increases the pie chart radius
+ *
+ * @params object obj The graph object
+ */
+ RGraph.Effects.Pie.Grow = function (obj)
+ {
+ var canvas = obj.canvas;
+ var opts = arguments[1] ? arguments[1] : [];
+ var color = opts['color'] ? opts['color'] : 'white';
+ var xy = RGraph.getCanvasXY(canvas);
+
+ canvas.style.visibility = 'hidden';
+ obj.Draw();
+ var radius = obj.getRadius();
+ RGraph.Clear(obj.canvas);
+ canvas.style.visibility = 'visible';
+
+ obj.Set('chart.radius', 0);
+
+ RGraph.Effects.Animate(obj, {'chart.radius': radius}, arguments[2]);
+ }
+
+
+ /**
+ * Grow
+ *
+ * The Bar chart Grow effect gradually increases the values of the bars
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.Bar.Grow = function (obj)
+ {
+ // Save the data
+ obj.original_data = RGraph.array_clone(obj.data);
+
+ // Zero the data
+ obj.__animation_frame__ = 0;
+
+ // Stop the scale from changing by setting chart.ymax (if it's not already set)
+ if (obj.Get('chart.ymax') == null) {
+
+ var ymax = 0;
+
+ for (var i=0; i<obj.data.length; ++i) {
+ if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') {
+ ymax = Math.max(ymax, RGraph.array_sum(obj.data[i]));
+ } else {
+ ymax = Math.max(ymax, obj.data[i]);
+ }
+ }
+
+ ymax = RGraph.getScale(ymax)[4];
+
+ obj.Set('chart.ymax', ymax);
+ }
+
+ function Grow ()
+ {
+ var numFrames = 30;
+
+ if (!obj.__animation_frame__) {
+ obj.__animation_frame__ = 0;
+ obj.__original_hmargin__ = obj.Get('chart.hmargin');
+ obj.__hmargin__ = ((obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right')) / obj.data.length) / 2;
+ obj.Set('chart.hmargin', obj.__hmargin__);
+ }
+
+ // Alter the Bar chart data depending on the frame
+ for (var j=0; j<obj.original_data.length; ++j) {
+ if (typeof(obj.data[j]) == 'object') {
+ for (var k=0; k<obj.data[j].length; ++k) {
+ obj.data[j][k] = (obj.__animation_frame__ / numFrames) * obj.original_data[j][k];
+ }
+ } else {
+ obj.data[j] = (obj.__animation_frame__ / numFrames) * obj.original_data[j];
+ }
+ }
+
+ /**
+ * Increment the hmargin to the target
+ */
+ obj.Set('chart.hmargin', ((1 - (obj.__animation_frame__ / numFrames)) * (obj.__hmargin__ - obj.__original_hmargin__)) + obj.__original_hmargin__);
+
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (obj.__animation_frame__ < numFrames) {
+ obj.__animation_frame__ += 1;
+
+ if (location.href.indexOf('?settimeout') > 0) {
+ setTimeout(Grow, 40);
+ } else {
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+
+
+ /**
+ * A wrapper function that encapsulate requestAnimationFrame
+ *
+ * @param function func The animation function
+ */
+ RGraph.Effects.UpdateCanvas = function (func)
+ {
+ // Standard
+ if (typeof(window.requestAnimationFrame) == 'function') {
+ window.requestAnimationFrame(func);
+ // IE 10+
+ } else if (typeof(window.msRequestAnimationFrame) == 'function') {
+ window.msRequestAnimationFrame(func);
+ // Chrome
+ } else if (typeof(window.webkitRequestAnimationFrame) == 'function') {
+ window.webkitRequestAnimationFrame(func);
+ // Firefox
+ } else if (window.mozRequestAnimationFrame && 0) { // Seems rather slow in FF6 - so disabled
+ window.mozRequestAnimationFrame(func);
+ // Default fallback to setTimeout
+ } else {
+ setTimeout(func, 16.666);
+ }
+ }
+
+
+ /**
+ * Grow
+ *
+ * The Fuel chart Grow effect gradually increases the values of the Fuel chart
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.Fuel.Grow = function (fuelObj)
+ {
+ function Grow ()
+ {
+ var numFrames = 35;
+
+ if (!fuelObj.__animation_frame__) {
+ fuelObj.__animation_frame__ = 0;
+ fuelObj.__min__ = fuelObj.min;
+ fuelObj.__value__ = fuelObj.value;
+ }
+
+
+ fuelObj.value = fuelObj.__min__ + (((fuelObj.__value__ - fuelObj.__min__) / numFrames) * fuelObj.__animation_frame__);
+
+ RGraph.Clear(fuelObj.canvas);
+ fuelObj.Draw();
+
+ if (fuelObj.__animation_frame__ < numFrames) {
+ fuelObj.__animation_frame__ += 1;
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+
+
+ /**
+ * The Animate function. Similar to the jQuery Animate() function - simply pass it a
+ * map of the properties and their target values, and this function will animate
+ * them to get to those values.
+ *
+ * @param object obj The chart object
+ * @param object map A map (an associative array) of the properties and their target values.
+ * @param An optional function which will be called when the animation is complete
+ */
+ RGraph.Effects.Animate = function (obj, map)
+ {
+ obj.Draw();
+
+ RGraph.Effects.__total_frames__ = (map && map['frames']) ? map['frames'] : 30;
+
+ function Animate_Iterator (func)
+ {
+ var id = [obj.id + '_' + obj.type];
+
+ // Very first time in - initialise the arrays
+ if (typeof(RGraph.Effects.__current_frame__ ) == 'undefined') {
+ RGraph.Effects.__current_frame__ = new Array();
+ RGraph.Effects.__original_values__ = new Array();
+ RGraph.Effects.__diffs__ = new Array();
+ RGraph.Effects.__steps__ = new Array();
+ RGraph.Effects.__callback__ = new Array();
+ }
+
+ // Initialise the arrays for THIS animation (not necessrily the first in the page)
+ if (!RGraph.Effects.__current_frame__[id]) {
+ RGraph.Effects.__current_frame__[id] = RGraph.Effects.__total_frames__;
+ RGraph.Effects.__original_values__[id] = {};
+ RGraph.Effects.__diffs__[id] = {};
+ RGraph.Effects.__steps__[id] = {};
+ RGraph.Effects.__callback__[id] = func;
+ }
+
+ for (var i in map) {
+ if (typeof(map[i]) == 'string' || typeof(map[i]) == 'number') {
+
+ // If this the first frame, record the proginal value
+ if (RGraph.Effects.__current_frame__[id] == RGraph.Effects.__total_frames__) {
+ RGraph.Effects.__original_values__[id][i] = obj.Get(i);
+ RGraph.Effects.__diffs__[id][i] = map[i] - RGraph.Effects.__original_values__[id][i];
+ RGraph.Effects.__steps__[id][i] = RGraph.Effects.__diffs__[id][i] / RGraph.Effects.__total_frames__;
+ }
+
+ obj.Set(i, obj.Get(i) + RGraph.Effects.__steps__[id][i]);
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+ }
+
+ // If the current frame number is above zero, run the animation iterator again
+ if (--RGraph.Effects.__current_frame__[id] > 0) {
+ //setTimeout(Animate_Iterator, 100)
+ RGraph.Effects.UpdateCanvas(Animate_Iterator);
+
+ // Optional callback
+ } else {
+
+ if (typeof(RGraph.Effects.__callback__[id]) == 'function') {
+ (RGraph.Effects.__callback__[id])(obj);
+ }
+
+ // Get rid of the arrays
+ RGraph.Effects.__current_frame__[id] = null;
+ RGraph.Effects.__original_values__[id] = null;
+ RGraph.Effects.__diffs__[id] = null;
+ RGraph.Effects.__steps__[id] = null;
+ RGraph.Effects.__callback__[id] = null;
+
+ }
+ }
+
+ Animate_Iterator(arguments[2]);
+ }
+
+
+ /**
+ * Slide in
+ *
+ * This function is a wipe that can be used when switching the canvas to a new graph
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.jQuery.Slide.In = function (obj)
+ {
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ var canvas = obj.canvas;
+ var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas);
+ var delay = 1000;
+ div.style.overflow= 'hidden';
+ var direction = typeof(arguments[1]) == 'object' && typeof(arguments[1]['direction']) == 'string' ? arguments[1]['direction'] : 'left';
+
+ canvas.style.position = 'relative';
+
+ if (direction == 'left') {
+ canvas.style.left = (0 - div.offsetWidth) + 'px';
+ canvas.style.top = 0;
+ } else if (direction == 'top') {
+ canvas.style.left = 0;
+ canvas.style.top = (0 - div.offsetHeight) + 'px';
+ } else if (direction == 'bottom') {
+ canvas.style.left = 0;
+ canvas.style.top = div.offsetHeight + 'px';
+ } else {
+ canvas.style.left = div.offsetWidth + 'px';
+ canvas.style.top = 0;
+ }
+
+ $('#' + obj.id).animate({left:0,top:0}, delay);
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Slide out
+ *
+ * This function is a wipe that can be used when switching the canvas to a new graph
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.jQuery.Slide.Out = function (obj)
+ {
+ var canvas = obj.canvas;
+ var div = RGraph.Effects.ReplaceCanvasWithDIV(obj.canvas);
+ var delay = 1000;
+ div.style.overflow= 'hidden';
+ var direction = typeof(arguments[1]) == 'object' && typeof(arguments[1]['direction']) == 'string' ? arguments[1]['direction'] : 'left';
+
+ canvas.style.position = 'relative';
+ canvas.style.left = 0;
+ canvas.style.top = 0;
+
+ if (direction == 'left') {
+ $('#' + obj.id).animate({left: (0 - canvas.width) + 'px'}, delay);
+ } else if (direction == 'top') {
+ $('#' + obj.id).animate({left: 0, top: (0 - div.offsetHeight) + 'px'}, delay);
+ } else if (direction == 'bottom') {
+ $('#' + obj.id).animate({top: (0 + div.offsetHeight) + 'px'}, delay);
+ } else {
+ $('#' + obj.id).animate({left: (0 + canvas.width) + 'px'}, delay);
+ }
+
+ /**
+ * Callback
+ */
+ if (typeof(arguments[2]) == 'function') {
+ setTimeout(arguments[2], delay);
+ }
+ }
+
+
+ /**
+ * Unfold
+ *
+ * This effect gradually increases the X/Y coordinatesfrom 0
+ *
+ * @param object obj The chart object
+ */
+ RGraph.Effects.Line.Unfold = function (obj)
+ {
+ obj.Set('chart.animation.factor', obj.Get('chart.animation.unfold.initial'));
+ RGraph.Effects.Animate(obj, {'chart.animation.factor': 1}, arguments[2]);
+ }
+
+
+ /**
+ * Unfold
+ *
+ * This effect gradually increases the radiuss and decrease the margin of the Rose chart
+ *
+ * @param object obj The chart object
+ * @param Not used - pass null
+ * @param function An optional callback function
+ */
+ RGraph.Effects.Rose.Grow = function (obj)
+ {
+ var original_margin = obj.Get('chart.margin');
+ var margin = (360 / obj.data.length) / 2;
+
+ obj.Set('chart.margin', margin);
+ obj.Set('chart.animation.grow.factor', 0);
+
+ RGraph.Effects.Animate(obj, {'chart.margin': original_margin, 'chart.animation.grow.factor': 1, 'frames': 45}, arguments[2]);
+ }
+
+
+ /**
+ * UnfoldFromCenter
+ *
+ * Line chart unfold from center
+ */
+ RGraph.Effects.Line.UnfoldFromCenter = function (obj)
+ {
+ var numFrames = 30;
+
+ var original_opacity = obj.canvas.style.opacity;
+ obj.canvas.style.opacity = 0;
+ obj.Draw();
+ var center_value = obj.scale[4] / 2;
+ obj.Set('chart.ymax', Number(obj.scale[4]));
+ RGraph.Clear(obj.canvas);
+ obj.canvas.style.opacity = original_opacity;
+ var original_data = RGraph.array_clone(obj.original_data);
+ var original_blur = obj.Get('chart.shadow.blur');
+ obj.Set('chart.shadow.blur', 0);
+ var callback = arguments[2];
+
+ if (!obj.__increments__) {
+
+ obj.__increments__ = new Array();
+
+ for (var dataset=0; dataset<original_data.length; ++dataset) {
+
+ obj.__increments__[dataset] = new Array();
+
+ for (var i=0; i<original_data[dataset].length; ++i) {
+ obj.__increments__[dataset][i] = (original_data[dataset][i] - center_value) / numFrames;
+
+ obj.original_data[dataset][i] = center_value;
+ }
+ }
+ }
+
+ function UnfoldFromCenter ()
+ {
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ for (var dataset=0; dataset<original_data.length; ++dataset) {
+ for (var i=0; i<original_data[dataset].length; ++i) {
+ obj.original_data[dataset][i] += obj.__increments__[dataset][i];
+ }
+ }
+
+ if (--numFrames > 0) {
+ RGraph.Effects.UpdateCanvas(UnfoldFromCenter);
+ } else {
+ obj.original_data = RGraph.array_clone(original_data);
+ obj.__increments__ = null;
+ obj.Set('chart.shadow.blur', original_blur);
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+ }
+
+ UnfoldFromCenter();
+ }
+
+
+ /**
+ * FoldToCenter
+ *
+ * Line chart FoldTocenter
+ */
+ RGraph.Effects.Line.FoldToCenter = function (obj)
+ {
+ var totalFrames = 30;
+ var numFrame = totalFrames;
+ obj.Draw();
+ var center_value = obj.scale[4] / 2;
+ obj.Set('chart.ymax', Number(obj.scale[4]));
+ RGraph.Clear(obj.canvas);
+ var original_data = RGraph.array_clone(obj.original_data);
+ obj.Set('chart.shadow.blur', 0);
+ var callback = arguments[2];
+
+ function FoldToCenter ()
+ {
+ for (var i=0; i<obj.data.length; ++i) {
+ if (obj.data[i].length) {
+ for (var j=0; j<obj.data[i].length; ++j) {
+ if (obj.original_data[i][j] > center_value) {
+ obj.original_data[i][j] = ((original_data[i][j] - center_value) * (numFrame/totalFrames)) + center_value;
+ } else {
+ obj.original_data[i][j] = center_value - ((center_value - original_data[i][j]) * (numFrame/totalFrames));
+ }
+ }
+ }
+ }
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (numFrame-- > 0) {
+ RGraph.Effects.UpdateCanvas(FoldToCenter);
+ } else if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(FoldToCenter);
+ }
+
+
+ /**
+ * Odo Grow
+ *
+ * This effect gradually increases the represented value
+ *
+ * @param object obj The chart object
+ * @param Not used - pass null
+ * @param function An optional callback function
+ */
+ RGraph.Effects.Odo.Grow = function (obj)
+ {
+ var numFrames = 30;
+ var origValue = Number(obj.currentValue);
+ var newValue = obj.value;
+ var diff = newValue - origValue;
+ var step = (diff / numFrames);
+ var callback = arguments[2];
+
+ function Grow ()
+ {
+ if (obj.currentValue != newValue) {
+ obj.value = Number(obj.currentValue) + step;
+ }
+
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (numFrames-- > 0) {
+ //setTimeout(Grow, 100);
+ RGraph.Effects.UpdateCanvas(Grow);
+ } else if (callback) {
+ callback(obj)
+ }
+ }
+
+ //setTimeout(Grow, 100);
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+
+
+ /**
+ * Meter Grow
+ *
+ * This effect gradually increases the represented value
+ *
+ * @param object obj The chart object
+ * @param Not used - pass null
+ * @param function An optional callback function
+ */
+ RGraph.Effects.Meter.Grow = function (obj)
+ {
+ if (!obj.currentValue) {
+ obj.currentValue = obj.min;
+ }
+
+ var totalFrames = 60;
+ var numFrame = 0;
+ var diff = obj.value - obj.currentValue;
+ var step = diff / totalFrames
+ var callback = arguments[2];
+
+ function Grow_meter_inner ()
+ {
+ obj.value = obj.currentValue + step;
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (numFrame++ < totalFrames) {
+ RGraph.Effects.UpdateCanvas(Grow_meter_inner);
+ } else if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow_meter_inner);
+ }
+
+
+ /**
+ * Grow
+ *
+ * The HBar chart Grow effect gradually increases the values of the bars
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.HBar.Grow = function (obj)
+ {
+ // Save the data
+ obj.original_data = RGraph.array_clone(obj.data);
+
+ // Zero the data
+ obj.__animation_frame__ = 0;
+
+ // Stop the scale from changing by setting chart.ymax (if it's not already set)
+ if (obj.Get('chart.xmax') == 0) {
+
+ var xmax = 0;
+
+ for (var i=0; i<obj.data.length; ++i) {
+ if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'stacked') {
+ xmax = Math.max(xmax, RGraph.array_sum(obj.data[i]));
+ } else if (RGraph.is_array(obj.data[i]) && obj.Get('chart.grouping') == 'grouped') {
+ xmax = Math.max(xmax, RGraph.array_max(obj.data[i]));
+ } else {
+ xmax = Math.max(xmax, RGraph.array_max(obj.data[i]));
+ }
+ }
+
+ xmax = RGraph.getScale(xmax)[4];
+
+ obj.Set('chart.xmax', xmax);
+ }
+
+ /**
+ * Turn off shadow blur for the duration of the animation
+ */
+ if (obj.Get('chart.shadow.blur') > 0) {
+ var __original_shadow_blur__ = obj.Get('chart.shadow.blur');
+ obj.Set('chart.shadow.blur', 0);
+ }
+
+ function Grow ()
+ {
+ var numFrames = 30;
+
+ if (!obj.__animation_frame__) {
+ obj.__animation_frame__ = 0;
+ obj.__original_vmargin__ = obj.Get('chart.vmargin');
+ obj.__vmargin__ = ((obj.canvas.height - obj.Get('chart.gutter.top') - obj.Get('chart.gutter.bottom')) / obj.data.length) / 2;
+ obj.Set('chart.vmargin', obj.__vmargin__);
+ }
+
+ // Alter the Bar chart data depending on the frame
+ for (var j=0; j<obj.original_data.length; ++j) {
+
+ // This stops the animatioon from being completely linear
+ var easing = Math.pow(Math.sin((obj.__animation_frame__ * (90 / numFrames)) / (180 / Math.PI)), 4);
+
+ if (typeof(obj.data[j]) == 'object') {
+ for (var k=0; k<obj.data[j].length; ++k) {
+ obj.data[j][k] = (obj.__animation_frame__ / numFrames) * obj.original_data[j][k] * easing;
+ }
+ } else {
+ obj.data[j] = (obj.__animation_frame__ / numFrames) * obj.original_data[j] * easing;
+ }
+ }
+
+ /**
+ * Increment the vmargin to the target
+ */
+ obj.Set('chart.vmargin', ((1 - (obj.__animation_frame__ / numFrames)) * (obj.__vmargin__ - obj.__original_vmargin__)) + obj.__original_vmargin__);
+
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (obj.__animation_frame__ < numFrames) {
+ obj.__animation_frame__ += 1;
+
+ RGraph.Effects.UpdateCanvas(Grow);
+
+ // Turn any shadow blur back on
+ } else {
+ if (typeof(__original_shadow_blur__) == 'number' && __original_shadow_blur__ > 0) {
+ obj.Set('chart.shadow.blur', __original_shadow_blur__);
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+
+
+ /**
+ * Trace
+ *
+ * This effect is for the Line chart, uses the jQuery library and slowly
+ * uncovers the Line , but you can see the background of the chart. This effect
+ * is quite new (1/10/2011) and as such should be used with caution.
+ *
+ * @param object obj The graph object
+ * @param object Not used
+ * @param int A number denoting how long (in millseconds) the animation should last for. Defauld
+ * is 1500
+ */
+ RGraph.Effects.Line.jQuery.Trace = function (obj)
+ {
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ /**
+ * Create the DIV that the second canvas will sit in
+ */
+ var div = document.createElement('DIV');
+ var xy = RGraph.getCanvasXY(obj.canvas);
+ div.id = '__rgraph_trace_animation_' + RGraph.random(0, 4351623) + '__';
+ div.style.left = xy[0] + 'px';
+ div.style.top = xy[1] + 'px';
+ div.style.width = obj.Get('chart.gutter.left');
+ div.style.height = obj.canvas.height + 'px';
+ div.style.position = 'absolute';
+ div.style.overflow = 'hidden';
+ document.body.appendChild(div);
+
+ /**
+ * Make the second canvas
+ */
+ var id = '__rgraph_line_reveal_animation_' + RGraph.random(0, 99999999) + '__';
+ var canvas2 = document.createElement('CANVAS');
+ canvas2.width = obj.canvas.width;
+ canvas2.height = obj.canvas.height;
+ canvas2.style.position = 'absolute';
+ canvas2.style.left = 0;
+ canvas2.style.top = 0;
+
+ canvas2.id = id;
+ div.appendChild(canvas2);
+
+ var reposition_canvas2 = function (e)
+ {
+ var xy = RGraph.getCanvasXY(obj.canvas);
+
+ div.style.left = xy[0] + 'px';
+ div.style.top = xy[1] + 'px';
+ }
+ window.addEventListener('resize', reposition_canvas2, false)
+
+ /**
+ * Make a copy of the original Line object
+ */
+ var obj2 = new RGraph.Line(id, RGraph.array_clone(obj.original_data));
+
+ for (i in obj.properties) {
+ if (typeof(i) == 'string' && obj.properties[i]) {
+ obj2.Set(i, obj.properties[i]);
+ }
+ }
+
+ obj2.Set('chart.labels', []);
+ obj2.Set('chart.background.grid', false);
+ obj2.Set('chart.ylabels', false);
+ obj2.Set('chart.noaxes', true);
+ obj2.Set('chart.title', '');
+ obj2.Set('chart.title.xaxis', '');
+ obj2.Set('chart.title.yaxis', '');
+ obj2.Draw();
+
+ /**
+ * This effectively hides the line
+ */
+ obj.Set('chart.line.visible', false);
+ obj.Set('chart.colors', ['rgba(0,0,0,0)']);
+ if (obj.Get('chart.filled')) {
+ obj.Set('chart.fillstyle', 'rgba(0,0,0,0)');
+ }
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+
+ /**
+ * Get rid of the second canvas and turn the line back on
+ * on the original.
+ */
+ RGraph.Effects.Line.Trace_callback = function ()
+ {
+ // Remove the window resize listener
+ window.removeEventListener('resize', reposition_canvas2, false);
+
+ div.parentNode.removeChild(div);
+ div.removeChild(canvas2);
+ obj.Set('chart.line.visible', true);
+
+ // Revert the filled status back to as it was
+ obj.Set('chart.filled', RGraph.array_clone(obj2.Get('chart.filled')));
+ obj.Set('chart.fillstyle', RGraph.array_clone(obj2.Get('chart.fillstyle')));
+ obj.Set('chart.colors', RGraph.array_clone(obj2.Get('chart.colors')));
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+
+
+ $('#' + div.id).animate({
+ width: obj.canvas.width + 'px'
+ }, arguments[2] ? arguments[2] : 1500, RGraph.Effects.Line.Trace_callback);
+ }
+
+
+
+ /**
+ * RoundRobin
+ *
+ * This effect does two things:
+ * 1. Gradually increases the size of each segment
+ * 2. Gradually increases the size of the radius from 0
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Effects.Pie.RoundRobin = function (obj)
+ {
+ var callback = arguments[2] ? arguments[2] : null;
+ var opt = arguments[1];
+ obj.canvas.style.visibility = 'hidden';
+ obj.Draw();
+ obj.__animation_frame__ = 0;
+
+ var targetRadius = typeof(obj.Get('chart.radius')) == 'number' ? obj.Get('chart.radius') : obj.getRadius();
+ RGraph.Clear(obj.canvas);
+
+ obj.canvas.style.visibility = 'visible';
+
+ function RoundRobin_inner ()
+ {
+ // Increase this for a longer animation, decrease it for a shorter animation
+ var numFrames = 90;
+
+ obj.Set('chart.effect.roundrobin.multiplier', Math.pow(Math.sin((obj.__animation_frame__ * (90 / numFrames)) / (180 / Math.PI)), 2) * (obj.__animation_frame__ / numFrames) );
+
+ if (!opt || opt['radius']) {
+ obj.Set('chart.radius', targetRadius * obj.Get('chart.effect.roundrobin.multiplier'));
+ }
+
+ RGraph.Clear(obj.canvas)
+ obj.Draw();
+
+ if (obj.__animation_frame__ < numFrames) {
+ obj.__animation_frame__ += 1;
+ RGraph.Effects.UpdateCanvas(RoundRobin_inner);
+
+ } else if (callback) {
+ callback(obj);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(RoundRobin_inner);
+ }
+
+
+ /**
+ * Implode (pie chart)
+ *
+ * Here the segments are initially exploded - and gradually
+ * contract inwards to create the Pie chart
+ *
+ * @param object obj The Pie chart object
+ */
+ RGraph.Effects.Pie.Implode = function (obj)
+ {
+ var numFrames = 90;
+ var distance = Math.min(obj.canvas.width, obj.canvas.height);
+
+ function Implode_inner ()
+ {
+ obj.Set('chart.exploded', Math.sin(numFrames / 57.3) * distance);
+ RGraph.Clear(obj.canvas)
+ obj.Draw();
+
+ if (numFrames > 0) {
+ numFrames--;
+ RGraph.Effects.UpdateCanvas(Implode_inner);
+ } else {
+ // Finish off the animation
+ obj.Set('chart.exploded', 0);
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Implode_inner);
+ }
+
+
+ /**
+ * Gauge Grow
+ *
+ * This effect gradually increases the represented value
+ *
+ * @param object obj The chart object
+ * @param Not used - pass null
+ * @param function An optional callback function
+ */
+ RGraph.Effects.Gauge.Grow = function (obj)
+ {
+ var numFrames = 30;
+ var origValue = Number(obj.currentValue);
+
+ if (obj.currentValue == null) {
+ obj.currentValue = obj.min;
+ origValue = obj.min;
+ }
+
+ var newValue = obj.value;
+ var diff = newValue - origValue;
+ var step = (diff / numFrames);
+
+
+ function Grow ()
+ {
+ if (obj.currentValue != newValue) {
+ obj.value = Number(obj.currentValue) + step;
+ }
+
+ if (obj.value > obj.max) {
+ obj.value = obj.max;
+ }
+
+ if (obj.value < obj.min) {
+ obj.value = obj.min;
+ }
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (numFrames-- > 0) {
+ //setTimeout(Grow, 100);
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+ }
+
+ //setTimeout(Grow, 100);
+ RGraph.Effects.UpdateCanvas(Grow);
+ }
+
+
+ /**
+ * Radar chart grow
+ *
+ * This effect gradually increases the magnitude of the points on the radar chart
+ *
+ * @param object obj The chart object
+ * @param null Not used
+ * @param function An optional callback that is run when the effect is finished
+ */
+ RGraph.Effects.Radar.Grow = function (obj)
+ {
+ var totalframes = 30;
+ var framenum = totalframes;
+ var data = obj.data;
+ var callback = arguments[2];
+ obj.data = [];
+
+ function Grow_inner ()
+ {
+ for (var i=0; i<data.length; ++i) {
+
+ if (obj.data[i] == null) {
+ obj.data[i] = [];
+ }
+
+ for (var j=0; j<data[i].length; ++j) {
+ obj.data[i][j] = ((totalframes - framenum)/totalframes) * data[i][j];
+ }
+ }
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (framenum > 0) {
+ framenum--;
+ RGraph.Effects.UpdateCanvas(Grow_inner);
+ } else if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow_inner);
+ }
+
+
+ /**
+ * Waterfall Grow
+ *
+ * @param object obj The chart object
+ * @param null Not used
+ * @param function An optional function which is called when the animation is finished
+ */
+ RGraph.Effects.Waterfall.Grow = function (obj)
+ {
+ var totalFrames = 45;
+ var numFrame = 0;
+ var data = RGraph.array_clone(obj.data);
+ var callback = arguments[2];
+
+ //Reset The data to zeros
+ for (var i=0; i<obj.data.length; ++i) {
+ obj.data[i] /= totalFrames;
+
+ }
+
+ /**
+ * Fix the scale
+ */
+ if (obj.Get('chart.ymax') == null) {
+ var max = RGraph.getScale(obj.getMax(data))[4]
+ obj.Set('chart.ymax', max);
+ }
+
+ obj.Set('chart.multiplier.x', 0);
+ obj.Set('chart.multiplier.w', 0);
+
+ function Grow_inner ()
+ {
+ for (var i=0; i<obj.data.length; ++i) {
+ obj.data[i] = data[i] * (numFrame/totalFrames);
+ }
+
+ var multiplier = Math.pow(Math.sin(((numFrame / totalFrames) * 90) / 57.3), 20);
+ obj.Set('chart.multiplier.x', (numFrame / totalFrames) * multiplier);
+ obj.Set('chart.multiplier.w', (numFrame / totalFrames) * multiplier);
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (numFrame++ < totalFrames) {
+ RGraph.Effects.UpdateCanvas(Grow_inner);
+ } else if (typeof(callback) == 'function') {
+ callback(obj);
+ }
+ }
+
+ RGraph.Effects.UpdateCanvas(Grow_inner)
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.resizing.js b/schall/static/RGraph/libraries/RGraph.common.resizing.js
new file mode 100644
index 0000000..ee96747
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.resizing.js
@@ -0,0 +1,471 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+
+ /**
+ * This is an array of CSS properties that should be preserved when adding theplaceholder DIV
+ */
+ __rgraph_resizing_preserve_css_properties__ = [];
+
+ /**
+ * This function can be used to allow resizing
+ *
+ * @param object obj Your graph object
+ */
+ RGraph.AllowResizing = function (obj)
+ {
+ if (obj.Get('chart.resizable')) {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var resizeHandle = 15;
+ RGraph.Resizing.canvas = canvas;
+ RGraph.Resizing.placeHolders = [];
+
+ /**
+ * Add the original width and height to the canvas
+ */
+ if (!canvas.__original_width__ && !canvas.__original_height__) {
+ canvas.__original_width__ = canvas.width;
+ canvas.__original_height__ = canvas.height;
+ }
+
+
+ var adjustX = (typeof(obj.Get('chart.resize.handle.adjust')) == 'object' && typeof(obj.Get('chart.resize.handle.adjust')[0]) == 'number' ? obj.Get('chart.resize.handle.adjust')[0] : 0);
+ var adjustY = (typeof(obj.Get('chart.resize.handle.adjust')) == 'object' && typeof(obj.Get('chart.resize.handle.adjust')[1]) == 'number' ? obj.Get('chart.resize.handle.adjust')[1] : 0);
+
+
+ /**
+ * Draw the resize handle
+ */
+ var textWidth = context.measureText('Reset').width + 2;
+
+
+ // Draw the white background for the resize handle - OPTIONAL default is rgba(0,0,0,0);
+ var bgcolor = obj.Get('chart.resize.handle.background');
+
+ if (!bgcolor) {
+ bgcolor = 'rgba(0,0,0,0)';
+ }
+
+ context.beginPath();
+ context.fillStyle = bgcolor;
+ context.moveTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - resizeHandle);
+ context.fillRect(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - resizeHandle + adjustY, 2 * resizeHandle, resizeHandle);
+ context.fill();
+
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = 'gray';
+ obj.context.fillStyle = 'rgba(0,0,0,0)';
+ obj.context.lineWidth = 1;
+ obj.context.fillRect(obj.canvas.width - resizeHandle + adjustX, obj.canvas.height - resizeHandle - 2 + adjustY, resizeHandle, resizeHandle + 2);
+ obj.context.fillRect(obj.canvas.width - resizeHandle - textWidth + adjustX, obj.canvas.height - resizeHandle + adjustY, resizeHandle + textWidth, resizeHandle + 2);
+
+
+ // Draw the arrows
+
+ // Vertical line
+ obj.context.moveTo(obj.canvas.width - (resizeHandle / 2) + adjustX, obj.canvas.height - resizeHandle + adjustY);
+ obj.context.lineTo(obj.canvas.width - (resizeHandle / 2) + adjustX, obj.canvas.height + adjustY);
+
+
+ // Horizontal line
+ obj.context.moveTo(obj.canvas.width + adjustX, obj.canvas.height - (resizeHandle / 2) + adjustY);
+ obj.context.lineTo(obj.canvas.width - resizeHandle + adjustX, obj.canvas.height - (resizeHandle / 2) + adjustY);
+
+ context.fill();
+ context.stroke();
+
+
+ // Top arrow head
+ context.fillStyle = 'gray';
+ context.beginPath();
+ context.moveTo(canvas.width - (resizeHandle / 2) + adjustX, canvas.height - resizeHandle + adjustY);
+ context.lineTo(canvas.width - (resizeHandle / 2) + 3 + adjustX, canvas.height - resizeHandle + 3 + adjustY);
+ context.lineTo(canvas.width - (resizeHandle / 2) - 3 + adjustX, canvas.height - resizeHandle + 3 + adjustY);
+ context.closePath();
+ context.fill();
+
+ // Bottom arrow head
+ context.beginPath();
+ context.moveTo(canvas.width - (resizeHandle / 2) + adjustX, canvas.height + adjustY);
+ context.lineTo(canvas.width - (resizeHandle / 2) + 3 + adjustX, canvas.height - 3 + adjustY);
+ context.lineTo(canvas.width - (resizeHandle / 2) - 3 + adjustX, canvas.height - 3 + adjustY);
+ context.closePath();
+ context.fill();
+
+ // Left arrow head
+ context.beginPath();
+ context.moveTo(canvas.width - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY);
+ context.lineTo(canvas.width - resizeHandle + 3 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY);
+ context.lineTo(canvas.width - resizeHandle + 3 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY);
+ context.closePath();
+ context.fill();
+
+ // Right arrow head
+ context.beginPath();
+ context.moveTo(canvas.width + adjustX, canvas.height - (resizeHandle / 2) + adjustY);
+ context.lineTo(canvas.width - 3 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY);
+ context.lineTo(canvas.width - 3 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY);
+ context.closePath();
+ context.fill();
+
+ // Square at the centre of the arrows
+ context.beginPath();
+ context.fillStyle = 'white';
+ context.moveTo(canvas.width + adjustX, canvas.height - (resizeHandle / 2) + adjustY);
+ context.strokeRect(canvas.width - (resizeHandle / 2) - 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY, 4, 4);
+ context.fillRect(canvas.width - (resizeHandle / 2) - 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY, 4, 4);
+ context.fill();
+ context.stroke();
+
+
+ // Draw the "Reset" button
+ context.beginPath();
+ context.fillStyle = 'gray';
+ context.moveTo(canvas.width - resizeHandle - 3 + adjustX, canvas.height - resizeHandle / 2 + adjustY);
+ context.lineTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY);
+ context.lineTo(canvas.width - resizeHandle - resizeHandle + 2 + adjustX, canvas.height - (resizeHandle / 2) - 2 + adjustY);
+ context.lineTo(canvas.width - resizeHandle - resizeHandle + 2 + adjustX, canvas.height - (resizeHandle / 2) + 2 + adjustY);
+ context.lineTo(canvas.width - resizeHandle - resizeHandle + adjustX, canvas.height - (resizeHandle / 2) + adjustY);
+ context.stroke();
+ context.fill();
+
+ context.beginPath();
+ context.moveTo(canvas.width - resizeHandle - resizeHandle - 1 + adjustX, canvas.height - (resizeHandle / 2) - 3 + adjustY);
+ context.lineTo(canvas.width - resizeHandle - resizeHandle - 1 + adjustX, canvas.height - (resizeHandle / 2) + 3 + adjustY);
+ context.stroke();
+ context.fill();
+
+
+
+ var window_onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = RGraph.Resizing.canvas;
+ var newWidth = RGraph.Resizing.originalw - (RGraph.Resizing.originalx - e.pageX);// - 5
+ var newHeight = RGraph.Resizing.originalh - (RGraph.Resizing.originaly - e.pageY);// - 5
+
+ if (RGraph.Resizing.mousedown) {
+ if (newWidth > (canvas.__original_width__ / 2)) RGraph.Resizing.div.style.width = newWidth + 'px';
+ if (newHeight > (canvas.__original_height__ / 2)) RGraph.Resizing.div.style.height = newHeight + 'px';
+
+ RGraph.FireCustomEvent(canvas.__object__, 'onresize');
+ }
+ }
+ window.addEventListener('mousemove', window_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'window_mousemove', window_onmousemove);
+
+ /**
+ * The window onmouseup function
+ */
+ var MouseupFunc = function (e)
+ {
+ if (!RGraph.Resizing || !RGraph.Resizing.div || !RGraph.Resizing.mousedown) {
+ return;
+ }
+
+ if (RGraph.Resizing.div) {
+
+ var div = RGraph.Resizing.div;
+ var canvas = div.__canvas__;
+ var coords = RGraph.getCanvasXY(div.__canvas__);
+
+ var parentNode = canvas.parentNode;
+
+ if (canvas.style.position != 'absolute') {
+ // Create a DIV to go in the canvases place
+ var placeHolderDIV = document.createElement('DIV');
+ placeHolderDIV.style.width = RGraph.Resizing.originalw + 'px';
+ placeHolderDIV.style.height = RGraph.Resizing.originalh + 'px';
+ //placeHolderDIV.style.backgroundColor = 'red';
+ placeHolderDIV.style.display = 'inline-block'; // Added 5th Nov 2010
+ placeHolderDIV.style.position = canvas.style.position;
+ placeHolderDIV.style.left = canvas.style.left;
+ placeHolderDIV.style.top = canvas.style.top;
+ placeHolderDIV.style.cssFloat = canvas.style.cssFloat;
+
+ parentNode.insertBefore(placeHolderDIV, canvas);
+ }
+
+
+ // Now set the canvas to be positioned absolutely
+ canvas.style.backgroundColor = 'white';
+ canvas.style.position = 'absolute';
+ canvas.style.border = '1px dashed gray';
+ canvas.style.left = (RGraph.Resizing.originalCanvasX - 1) + 'px';
+ canvas.style.top = (RGraph.Resizing.originalCanvasY - 1) + 'px';
+
+ canvas.width = parseInt(div.style.width);
+ canvas.height = parseInt(div.style.height);
+
+
+
+ /**
+ * Fire the onresize event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw');
+
+ canvas.__object__.Draw();
+
+ // Get rid of transparent semi-opaque DIV
+ RGraph.Resizing.mousedown = false;
+ div.style.display = 'none';
+ document.body.removeChild(div);
+ }
+
+ /**
+ * If there is zoom enabled in thumbnail mode, lose the zoom image
+ */
+ if (RGraph.Registry.Get('chart.zoomed.div') || RGraph.Registry.Get('chart.zoomed.img')) {
+ RGraph.Registry.Set('chart.zoomed.div', null);
+ RGraph.Registry.Set('chart.zoomed.img', null);
+ }
+
+ /**
+ * Fire the onresize event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizeend');
+ }
+
+
+ var window_onmouseup = MouseupFunc;
+ window.addEventListener('onmouseup', window_onmouseup, false);
+ RGraph.AddEventListener(canvas.id, 'window_mouseup', window_onmouseup);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var coords = RGraph.getMouseXY(e);
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ //var orig_cursor = canvas.style.cursor; // Used for playing well with tooltips
+
+ RGraph.Resizing.title = canvas.title;
+
+ if ( (coords[0] > (canvas.width - resizeHandle)
+ && coords[0] < canvas.width
+ && coords[1] > (canvas.height - resizeHandle)
+ && coords[1] < canvas.height)) {
+
+ canvas.style.cursor = 'move';
+
+ if (navigator.userAgent.indexOf('Chrome') > 0 || document.all) {
+ canvas.title = 'Resize the graph';
+ }
+
+ } else if ( coords[0] > (canvas.width - resizeHandle - resizeHandle)
+ && coords[0] < canvas.width - resizeHandle
+ && coords[1] > (canvas.height - resizeHandle)
+ && coords[1] < canvas.height) {
+
+ canvas.style.cursor = 'pointer';
+
+ if (navigator.userAgent.indexOf('Chrome') > 0 || document.all) {
+ canvas.title = 'Reset graph to original size';
+ }
+
+ } else {
+
+ // orig_cursor
+ canvas.style.cursor = 'default';
+ canvas.title = '';
+ }
+ }
+ canvas.addEventListener('mousemove', canvas_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'mousemove', canvas_onmousemove);
+
+
+
+ var canvas_onmouseout = function (e)
+ {
+ e.target.style.cursor = 'default';
+ e.target.title = '';
+ }
+ canvas.addEventListener('mouseout', canvas_onmouseout, false);
+ RGraph.AddEventListener(canvas.id, 'mouseout', canvas_onmouseout);
+
+
+
+ var canvas_onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var coords = RGraph.getMouseXY(e);
+ var canvasCoords = RGraph.getCanvasXY(e.target);
+ var canvas = e.target;
+
+ if ( coords[0] > (obj.canvas.width - resizeHandle)
+ && coords[0] < obj.canvas.width
+ && coords[1] > (obj.canvas.height - resizeHandle)
+ && coords[1] < obj.canvas.height) {
+
+ RGraph.FireCustomEvent(obj, 'onresizebegin');
+
+ // Save the existing border
+ if (canvas.__original_css_border__ == null) {
+ canvas.__original_css_border__ = canvas.style.border;
+ }
+
+ RGraph.Resizing.mousedown = true;
+
+
+ /**
+ * Create the semi-opaque DIV
+ */
+
+ var div = document.createElement('DIV');
+ div.style.position = 'absolute';
+ div.style.left = canvasCoords[0] + 'px';
+ div.style.top = canvasCoords[1] + 'px';
+ div.style.width = canvas.width + 'px';
+ div.style.height = canvas.height + 'px';
+ div.style.border = '1px dotted black';
+ div.style.backgroundColor = 'gray';
+ div.style.opacity = 0.5;
+ div.__canvas__ = e.target;
+
+ document.body.appendChild(div);
+ RGraph.Resizing.div = div;
+ RGraph.Resizing.placeHolders.push(div);
+
+ // Hide the previous resize indicator layers. This is only necessary it seems for the Meter chart
+ for (var i=0; i<(RGraph.Resizing.placeHolders.length - 1); ++i) {
+ RGraph.Resizing.placeHolders[i].style.display = 'none';
+ }
+
+ // This is a repetition of the window.onmouseup function (No need to use DOM2 here)
+ div.onmouseup = function (e)
+ {
+ MouseupFunc(e);
+ }
+
+
+ // No need to use DOM2 here
+ RGraph.Resizing.div.onmouseover = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+ e.stopPropagation();
+ }
+
+ // The mouse
+ RGraph.Resizing.originalx = e.pageX;
+ RGraph.Resizing.originaly = e.pageY;
+
+ RGraph.Resizing.originalw = obj.canvas.width;
+ RGraph.Resizing.originalh = obj.canvas.height;
+
+ RGraph.Resizing.originalCanvasX = RGraph.getCanvasXY(obj.canvas)[0];
+ RGraph.Resizing.originalCanvasY = RGraph.getCanvasXY(obj.canvas)[1];
+ }
+
+
+ /**
+ * This facilitates the reset button
+ */
+ if ( coords[0] > (canvas.width - resizeHandle - resizeHandle)
+ && coords[0] < canvas.width - resizeHandle
+ && coords[1] > (canvas.height - resizeHandle)
+ && coords[1] < canvas.height) {
+
+ /**
+ * Fire the onresizebegin event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizebegin');
+
+ // Restore the original width and height
+ canvas.width = canvas.__original_width__;
+ canvas.height = canvas.__original_height__;
+
+ // Lose the border
+ canvas.style.border = canvas.__original_css_border__;
+ //canvas.__original_css_border__ = null;
+
+ // Add 1 pixel to the top/left because the border is going
+ canvas.style.left = (parseInt(canvas.style.left)) + 'px';
+ canvas.style.top = (parseInt(canvas.style.top)) + 'px';
+
+
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw');
+
+ // Redraw the canvas
+ canvas.__object__.Draw();
+
+ // Set the width and height on the DIV
+ if (RGraph.Resizing.div) {
+ RGraph.Resizing.div.style.width = canvas.__original_width__ + 'px';
+ RGraph.Resizing.div.style.height = canvas.__original_height__ + 'px';
+ }
+
+ /**
+ * Fire the resize event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'onresize');
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizeend');
+ }
+ }
+ canvas.addEventListener('mousedown', canvas_onmousedown, false);
+ RGraph.AddEventListener(canvas.id, 'mousedown', canvas_onmousedown);
+
+
+ /**
+ * This function facilitates the reset button
+ *
+ * NOTE: 31st December 2010 - doesn't appear to be being used any more
+ */
+
+ /*
+ canvas.onclick = function (e)
+ {
+ var coords = RGraph.getMouseXY(e);
+ var canvas = e.target;
+
+ if ( coords[0] > (canvas.width - resizeHandle - resizeHandle)
+ && coords[0] < canvas.width - resizeHandle
+ && coords[1] > (canvas.height - resizeHandle)
+ && coords[1] < canvas.height) {
+
+ // Restore the original width and height
+ canvas.width = canvas.__original_width__;
+ canvas.height = canvas.__original_height__;
+
+ // Lose the border
+ canvas.style.border = '';
+
+ // Add 1 pixel to the top/left because the border is going
+ canvas.style.left = (parseInt(canvas.style.left) + 1) + 'px';
+ canvas.style.top = (parseInt(canvas.style.top) + 1) + 'px';
+
+ // Fire the onresizebeforedraw event
+ RGraph.FireCustomEvent(canvas.__object__, 'onresizebeforedraw');
+
+ // Redraw the canvas
+ canvas.__object__.Draw();
+
+ // Set the width and height on the DIV
+ RGraph.Resizing.div.style.width = canvas.__original_width__ + 'px';
+ RGraph.Resizing.div.style.height = canvas.__original_height__ + 'px';
+
+ // Fire the resize event
+ RGraph.FireCustomEvent(canvas.__object__, 'onresize');
+ }
+ }
+ */
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.tooltips.js b/schall/static/RGraph/libraries/RGraph.common.tooltips.js
new file mode 100644
index 0000000..3df3ded
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.tooltips.js
@@ -0,0 +1,847 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+ /**
+ * This is used in two functions, hence it's here
+ */
+ RGraph.tooltips = {};
+ RGraph.tooltips.padding = '3px';
+ RGraph.tooltips.font_face = 'Tahoma';
+ RGraph.tooltips.font_size = '10pt';
+
+
+ /**
+ * Shows a tooltip next to the mouse pointer
+ *
+ * @param canvas object The canvas element object
+ * @param text string The tooltip text
+ * @param int x The X position that the tooltip should appear at. Combined with the canvases offsetLeft
+ * gives the absolute X position
+ * @param int y The Y position the tooltip should appear at. Combined with the canvases offsetTop
+ * gives the absolute Y position
+ * @param int idx The index of the tooltip in the graph objects tooltip array
+ */
+ RGraph.Tooltip = function (canvas, text, x, y, idx)
+ {
+ /**
+ * chart.tooltip.override allows you to totally take control of rendering the tooltip yourself
+ */
+ if (typeof(canvas.__object__.Get('chart.tooltips.override')) == 'function') {
+ return canvas.__object__.Get('chart.tooltips.override')(canvas, text, x, y, idx);
+ }
+
+ /**
+ * This facilitates the "id:xxx" format
+ */
+ text = RGraph.getTooltipTextFromDIV(text);
+
+ /**
+ * First clear any exising timers
+ */
+ var timers = RGraph.Registry.Get('chart.tooltip.timers');
+
+ if (timers && timers.length) {
+ for (i=0; i<timers.length; ++i) {
+ clearTimeout(timers[i]);
+ }
+ }
+ RGraph.Registry.Set('chart.tooltip.timers', []);
+
+ /**
+ * Hide the context menu if it's currently shown
+ */
+ if (canvas.__object__.Get('chart.contextmenu')) {
+ RGraph.HideContext();
+ }
+
+ // Redraw the canvas?
+ if (canvas.__object__.Get('chart.tooltips.highlight')) {
+ RGraph.Redraw(canvas.id);
+ }
+
+ var effect = canvas.__object__.Get('chart.tooltips.effect').toLowerCase();
+
+ if (effect == 'snap' && RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__canvas__.id == canvas.id) {
+
+ if ( canvas.__object__.type == 'line'
+ || canvas.__object__.type == 'radar'
+ || canvas.__object__.type == 'scatter'
+ || canvas.__object__.type == 'rscatter'
+ ) {
+
+ var tooltipObj = RGraph.Registry.Get('chart.tooltip');
+
+
+ tooltipObj.style.width = null;
+ tooltipObj.style.height = null;
+
+ tooltipObj.innerHTML = text;
+ tooltipObj.__text__ = text;
+
+ /**
+ * Now that the new content has been set, re-set the width & height
+ */
+ RGraph.Registry.Get('chart.tooltip').style.width = RGraph.getTooltipWidth(text, canvas.__object__) + 'px';
+ RGraph.Registry.Get('chart.tooltip').style.height = RGraph.Registry.Get('chart.tooltip').offsetHeight + 'px';
+
+ /**
+ * Now (25th September 2011) use jQuery if it's available
+ */
+ if (typeof(jQuery) == 'function' && typeof($) == 'function') {
+ $('#' + tooltipObj.id).animate({
+ opacity: 1,
+ width: tooltipObj.offsetWidth + 'px',
+ height: tooltipObj.offsetHeight + 'px',
+ left: x + 'px',
+ top: (y - tooltipObj.offsetHeight) + 'px'
+ }, 150);
+ } else {
+ var currentx = parseInt(RGraph.Registry.Get('chart.tooltip').style.left);
+ var currenty = parseInt(RGraph.Registry.Get('chart.tooltip').style.top);
+
+ var diffx = x - currentx - ((x + RGraph.Registry.Get('chart.tooltip').offsetWidth) > document.body.offsetWidth ? RGraph.Registry.Get('chart.tooltip').offsetWidth : 0);
+ var diffy = y - currenty - RGraph.Registry.Get('chart.tooltip').offsetHeight;
+
+ // Position the tooltip
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.2)) + 'px"', 25);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.4)) + 'px"', 50);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.6)) + 'px"', 75);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 0.8)) + 'px"', 100);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.left = "' + (currentx + (diffx * 1.0)) + 'px"', 125);
+
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.2)) + 'px"', 25);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.4)) + 'px"', 50);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.6)) + 'px"', 75);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 0.8)) + 'px"', 100);
+ setTimeout('RGraph.Registry.Get("chart.tooltip").style.top = "' + (currenty + (diffy * 1.0)) + 'px"', 125);
+ }
+
+ } else {
+
+ alert('[TOOLTIPS] The "snap" effect is only supported on the Line, Rscatter, Scatter and Radar charts (tried to use it with type: ' + canvas.__object__.type);
+ }
+
+ /**
+ * Fire the tooltip event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'ontooltip');
+
+ return;
+ }
+
+ /**
+ * Hide any currently shown tooltip
+ */
+ RGraph.HideTooltip();
+
+
+ /**
+ * Show a tool tip
+ */
+ var tooltipObj = document.createElement('DIV');
+ tooltipObj.className = canvas.__object__.Get('chart.tooltips.css.class');
+ tooltipObj.style.display = 'none';
+ tooltipObj.style.position = 'absolute';
+ tooltipObj.style.left = 0;
+ tooltipObj.style.top = 0;
+ tooltipObj.style.backgroundColor = 'rgba(255,255,239,0.9)';
+ tooltipObj.style.color = 'black';
+ if (!document.all) tooltipObj.style.border = '';
+ tooltipObj.style.visibility = 'visible';
+ tooltipObj.style.paddingLeft = RGraph.tooltips.padding;
+ tooltipObj.style.paddingRight = RGraph.tooltips.padding;
+ tooltipObj.style.fontFamily = RGraph.tooltips.font_face;
+ tooltipObj.style.fontSize = RGraph.tooltips.font_size;
+ tooltipObj.style.zIndex = 3;
+ tooltipObj.style.borderRadius = '5px';
+ tooltipObj.style.MozBorderRadius = '5px';
+ tooltipObj.style.WebkitBorderRadius = '5px';
+ tooltipObj.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
+ tooltipObj.style.MozBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
+ tooltipObj.style.boxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
+ tooltipObj.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)';
+ tooltipObj.style.opacity = 0;
+ tooltipObj.style.overflow = 'hidden';
+ tooltipObj.innerHTML = text;
+ tooltipObj.__text__ = text; // This is set because the innerHTML can change when it's set
+ tooltipObj.__canvas__ = canvas;
+ tooltipObj.style.display = 'inline';
+ tooltipObj.id = '__rgraph_tooltip_' + canvas.id + '_' + idx;
+
+ if (typeof(idx) == 'number') {
+ tooltipObj.__index__ = idx;
+
+ /**
+ * Just for the line chart
+ */
+ if (canvas.__object__.type == 'line') {
+ var index2 = idx;
+
+ while (index2 >= canvas.__object__.data[0].length) {
+ index2 -= canvas.__object__.data[0].length;
+ }
+
+ tooltipObj.__index2__ = index2;
+ }
+ }
+
+ document.body.appendChild(tooltipObj);
+
+ var width = tooltipObj.offsetWidth;
+ var height = tooltipObj.offsetHeight;
+
+ if ((y - height - 2) > 0) {
+ y = y - height - 2;
+ } else {
+ y = y + 2;
+ }
+
+ /**
+ * Set the width on the tooltip so it doesn't resize if the window is resized
+ */
+ tooltipObj.style.width = width + 'px';
+ //tooltipObj.style.height = 0; // Initially set the tooltip height to nothing
+
+ /**
+ * If the mouse is towards the right of the browser window and the tooltip would go outside of the window,
+ * move it left
+ */
+ if ( (x + width) > document.body.offsetWidth ) {
+ x = x - width - 7;
+ var placementLeft = true;
+
+ if (canvas.__object__.Get('chart.tooltips.effect') == 'none') {
+ x = x - 3;
+ }
+
+ tooltipObj.style.left = x + 'px';
+ tooltipObj.style.top = y + 'px';
+
+ } else {
+ x += 5;
+
+ tooltipObj.style.left = x + 'px';
+ tooltipObj.style.top = y + 'px';
+ }
+
+
+ if (effect == 'expand') {
+
+ tooltipObj.style.left = (x + (width / 2)) + 'px';
+ tooltipObj.style.top = (y + (height / 2)) + 'px';
+ leftDelta = (width / 2) / 10;
+ topDelta = (height / 2) / 10;
+
+ tooltipObj.style.width = 0;
+ tooltipObj.style.height = 0;
+ //tooltipObj.style.boxShadow = '';
+ //tooltipObj.style.MozBoxShadow = '';
+ //tooltipObj.style.WebkitBoxShadow = '';
+ //tooltipObj.style.borderRadius = 0;
+ //tooltipObj.style.MozBorderRadius = 0;
+ //tooltipObj.style.WebkitBorderRadius = 0;
+ tooltipObj.style.opacity = 1;
+
+ // Progressively move the tooltip to where it should be (the x position)
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 250));
+
+ // Progressively move the tooltip to where it should be (the Y position)
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 250));
+
+ // Progressively grow the tooltip width
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.1) + "px'; }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.2) + "px'; }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.3) + "px'; }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.4) + "px'; }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.5) + "px'; }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.6) + "px'; }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.7) + "px'; }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.8) + "px'; }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.9) + "px'; }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250));
+
+ // Progressively grow the tooltip height
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.1) + "px'; }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.2) + "px'; }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.3) + "px'; }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.4) + "px'; }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.5) + "px'; }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.6) + "px'; }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.7) + "px'; }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.8) + "px'; }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.9) + "px'; }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250));
+
+ // When the animation is finished, set the tooltip HTML
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250));
+
+ } else if (effect == 'contract') {
+
+ tooltipObj.style.left = (x - width) + 'px';
+ tooltipObj.style.top = (y - (height * 2)) + 'px';
+ tooltipObj.style.cursor = 'pointer';
+
+ leftDelta = width / 10;
+ topDelta = height / 10;
+
+ tooltipObj.style.width = (width * 5) + 'px';
+ tooltipObj.style.height = (height * 5) + 'px';
+
+ tooltipObj.style.opacity = 0.2;
+
+ // Progressively move the tooltip to where it should be (the x position)
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) + leftDelta) + 'px' }", 250));
+
+ // Progressively move the tooltip to where it should be (the Y position)
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) + (topDelta*2)) + 'px' }", 250));
+
+ // Progressively shrink the tooltip width
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.5) + "px'; }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 5.0) + "px'; }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.5) + "px'; }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 4.0) + "px'; }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.5) + "px'; }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 3.0) + "px'; }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.5) + "px'; }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 2.0) + "px'; }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 1.5) + "px'; }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250));
+
+ // Progressively shrink the tooltip height
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.5) + "px'; }", 25));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 5.0) + "px'; }", 50));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.5) + "px'; }", 75));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 4.0) + "px'; }", 100));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.5) + "px'; }", 125));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 3.0) + "px'; }", 150));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.5) + "px'; }", 175));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 2.0) + "px'; }", 200));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 1.5) + "px'; }", 225));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250));
+
+ // When the animation is finished, set the tooltip HTML
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250));
+
+ /**
+ * This resets the pointer
+ */
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.cursor = 'default'; }", 275));
+
+ } else if (effect == 'snap') {
+
+ /*******************************************************
+ * Move the tooltip
+ *******************************************************/
+ for (var i=1; i<=10; ++i) {
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.left = '" + (x * 0.1 * i) + "px'; }", 15 * i));
+ RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.top = '" + (y * 0.1 * i) + "px'; }", 15 * i));
+ }
+
+ tooltipObj.style.left = 0 - tooltipObj.offsetWidth + 'px';
+ tooltipObj.style.top = 0 - tooltipObj.offsetHeight + 'px';
+
+ } else if (effect != 'fade' && effect != 'expand' && effect != 'none' && effect != 'snap' && effect != 'contract') {
+ alert('[COMMON] Unknown tooltip effect: ' + effect);
+ }
+
+ if (effect != 'none') {
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.1; }", 25);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.2; }", 50);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.3; }", 75);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.4; }", 100);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.5; }", 125);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.6; }", 150);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.7; }", 175);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.8; }", 200);
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 0.9; }", 225);
+ }
+
+ setTimeout("if (RGraph.Registry.Get('chart.tooltip')) { RGraph.Registry.Get('chart.tooltip').style.opacity = 1;}", effect == 'none' ? 50 : 250);
+
+ /**
+ * If the tooltip it self is clicked, cancel it
+ */
+ tooltipObj.onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e)
+ e.stopPropagation();
+ }
+
+ tooltipObj.onclick = function (e)
+ {
+ if (e.button == 0) {
+ e = RGraph.FixEventObject(e);
+ e.stopPropagation();
+ }
+ }
+
+ /**
+ * Install the function for hiding the tooltip.
+ */
+ document.body.onmousedown = function (event)
+ {
+ var tooltip = RGraph.Registry.Get('chart.tooltip');
+
+ if (tooltip) {
+ RGraph.HideTooltip();
+
+ // Redraw if highlighting is enabled
+ if (tooltip.__canvas__.__object__.Get('chart.tooltips.highlight')) {
+ RGraph.Redraw();
+ }
+ }
+ }
+
+ /**
+ * If the window is resized, hide the tooltip
+ */
+ window.onresize = function ()
+ {
+ var tooltip = RGraph.Registry.Get('chart.tooltip');
+
+ if (tooltip) {
+ tooltip.parentNode.removeChild(tooltip);
+ tooltip.style.display = 'none';
+ tooltip.style.visibility = 'hidden';
+ RGraph.Registry.Set('chart.tooltip', null);
+
+ // Redraw the graph if necessary
+ if (canvas.__object__.Get('chart.tooltips.highlight')) {
+ RGraph.Clear(canvas);
+ canvas.__object__.Draw();
+ }
+ }
+ }
+
+ /**
+ * Keep a reference to the tooltip
+ */
+ RGraph.Registry.Set('chart.tooltip', tooltipObj);
+
+ /**
+ * Fire the tooltip event
+ */
+ RGraph.FireCustomEvent(canvas.__object__, 'ontooltip');
+ }
+
+
+ /**
+ *
+ */
+ RGraph.getTooltipTextFromDIV = function (text)
+ {
+ var result = /^id:(.*)/.exec(text);
+
+ if (result && result[1] && document.getElementById(result[1])) {
+ text = document.getElementById(result[1]).innerHTML;
+ }
+
+ return text;
+ }
+
+
+ /**
+ * This function handles the tooltip text being a string, function, or an array of functions.
+ */
+ RGraph.parseTooltipText = function (tooltips, idx)
+ {
+ // Get the tooltip text
+ if (typeof(tooltips) == 'function') {
+ var text = tooltips(idx);
+
+ } else if (typeof(tooltips) == 'object' && tooltips && typeof(tooltips[idx]) == 'function') {
+ var text = tooltips[idx](idx);
+
+ } else if (typeof(tooltips) == 'object' && tooltips) {
+ var text = String(tooltips[idx]);
+
+ } else {
+ var text = '';
+ }
+
+ if (text == 'undefined') {
+ text = '';
+ }
+
+ return text;
+ }
+
+
+ /**
+ *
+ */
+ RGraph.getTooltipWidth = function (text, obj)
+ {
+ var div = document.createElement('DIV');
+ div.className = obj.Get('chart.tooltips.css.class');
+ div.style.paddingLeft = RGraph.tooltips.padding;
+ div.style.paddingRight = RGraph.tooltips.padding;
+ div.style.fontFamily = RGraph.tooltips.font_face;
+ div.style.fontSize = RGraph.tooltips.font_size;
+ div.style.visibility = 'hidden';
+ div.style.position = 'absolute';
+ div.style.top = '300px';
+ div.style.left = 0;
+ div.style.display = 'inline';
+ div.innerHTML = RGraph.getTooltipTextFromDIV(text);
+ document.body.appendChild(div);
+
+ return div.offsetWidth;
+ }
+
+
+ /**
+ * Hides the currently shown tooltip
+ */
+ RGraph.HideTooltip = function ()
+ {
+ var tooltip = RGraph.Registry.Get('chart.tooltip');
+
+ if (tooltip) {
+ tooltip.parentNode.removeChild(tooltip);
+ tooltip.style.display = 'none';
+ tooltip.style.visibility = 'hidden';
+ RGraph.Registry.Set('chart.tooltip', null);
+ }
+ }
+
+
+
+
+
+
+ /**
+ * The BAR chart onmousemove event Bar chart tooltips can now
+ * be based around the onmousemove event
+ */
+ RGraph.InstallBarTooltipEventListeners = function (obj)
+ {
+ /**
+ * Install the window onclick handler
+ */
+ var window_onclick_func = function (){RGraph.Redraw();};
+ window.addEventListener('click', window_onclick_func, false);
+ RGraph.AddEventListener('window_' + obj.id, 'click', window_onclick_func);
+
+
+ var canvas_onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(e.target.id);
+ var obj = canvas.__object__;
+
+ if (obj.__bar__) {
+ var lineObj = obj;
+ obj = obj.__bar__;
+ obj.__line__ = lineObj;
+ }
+
+ var barCoords = obj.getBar(e);
+
+ /**
+ * If there are bar coords AND the bar has height
+ */
+
+ if (barCoords && barCoords[4] > 0) {
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), barCoords[5]);
+
+
+ if (text) {
+ canvas.style.cursor = 'pointer';
+ } else {
+ canvas.style.cursor = 'default';
+ }
+
+ /**
+ * Hide the currently displayed tooltip if the index is the same
+ */
+ if ( RGraph.Registry.Get('chart.tooltip')
+ && RGraph.Registry.Get('chart.tooltip').__canvas__.id != obj.id
+ && obj.Get('chart.tooltips.event') == 'onmousemove') {
+
+ RGraph.Redraw();
+ RGraph.HideTooltip();
+ }
+
+ /**
+ * This facilitates the tooltips using the onmousemove event
+ */
+
+ if ( obj.Get('chart.tooltips.event') == 'onmousemove'
+ && (
+ (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ != barCoords[5])
+ || !RGraph.Registry.Get('chart.tooltip')
+ )
+ && text) {
+ /**
+ * Show a tooltip if it's defined
+ */
+ RGraph.Redraw(obj);
+
+ if (obj.Get('chart.tooltips.highlight')) {
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
+ obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
+
+ obj.context.stroke();
+ obj.context.fill();
+ }
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
+ }
+ } else if (obj.__line__ && obj.__line__.getPoint && obj.__line__.getPoint(e)) {
+ canvas.style.cursor = 'pointer';
+ } else {
+ canvas.style.cursor = 'default';
+ }
+ }
+ RGraph.AddEventListener(obj.id, 'mousemove', canvas_onmousemove);
+ obj.canvas.addEventListener('mousemove', canvas_onmousemove, false);
+
+
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ if (obj.Get('chart.tooltips.event') == 'onclick') {
+
+ var canvas_onclick = function (e)
+ {
+ var e = RGraph.FixEventObject(e);
+
+ // If the button pressed isn't the left, we're not interested
+ if (e.button != 0) return;
+
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+
+ if (obj.__bar__) {
+ obj = obj.__bar__;
+ }
+
+ var barCoords = obj.getBar(e);
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Redraw();
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ if (barCoords) {
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), barCoords[5]);
+
+ /**
+ * Show a tooltip if it's defined
+ */
+ if (text && text != 'undefined') {
+ if (obj.Get('chart.tooltips.highlight')) {
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
+ obj.context.fillRect(barCoords[1], barCoords[2], barCoords[3], barCoords[4]);
+ obj.context.stroke();
+ obj.context.fill();
+ }
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, barCoords[5]);
+ }
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+ }
+ RGraph.AddEventListener(obj.id, 'click', canvas_onclick);
+ obj.canvas.addEventListener('click', canvas_onclick, false);
+ }
+ }
+
+
+
+ /**
+ * The LINE chart tooltips event handlers
+ *
+ * @param object obj The graph object
+ */
+ RGraph.InstallLineTooltipEventListeners = function (obj)
+ {
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var point = obj.getPoint(e);
+
+ if (obj.Get('chart.tooltips.highlight')) {
+ RGraph.Register(obj);
+ }
+
+ if ( point
+ && typeof(point[0]) == 'object'
+ && typeof(point[1]) == 'number'
+ && typeof(point[2]) == 'number'
+ && typeof(point[3]) == 'number'
+ ) {
+
+ // point[0] is the graph object
+ var xCoord = point[1];
+ var yCoord = point[2];
+ var idx = point[3];
+
+ if ((obj.Get('chart.tooltips')[idx] || typeof(obj.Get('chart.tooltips')) == 'function')) {
+
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx);
+
+
+ // No tooltip text - do nada
+ if (text.match(/^id:(.*)$/) && RGraph.getTooltipTextFromDIV(text).substring(0,3) == 'id:') {
+ return;
+ }
+
+ // Chnage the pointer to a hand
+ canvas.style.cursor = 'pointer';
+
+ /**
+ * If the tooltip is the same one as is currently visible (going by the array index), don't do squat and return.
+ */
+
+ if ( RGraph.Registry.Get('chart.tooltip')
+ && RGraph.Registry.Get('chart.tooltip').__index__ == idx
+ && RGraph.Registry.Get('chart.tooltip').__canvas__.id == canvas.id
+ && RGraph.Registry.Get('chart.tooltip').__event__
+ && RGraph.Registry.Get('chart.tooltip').__event__ == 'mousemove') {
+
+ return;
+ }
+
+ /**
+ * Redraw the graph
+ */
+ if (obj.Get('chart.tooltips.highlight') || obj.__bar__) {
+
+ RGraph.Redraw();
+
+ if (obj.__bar__) {
+ RGraph.Clear(obj.canvas);
+ obj.__bar__.Draw();
+ }
+ }
+
+ // SHOW THE CORRECT TOOLTIP
+ RGraph.Tooltip(canvas, text, e.pageX, obj.Get('chart.tooltips.hotspot.xonly') ? (point[2] + RGraph.getCanvasXY(canvas)[1]) : e.pageY, idx);
+
+ // Store the tooltip index on the tooltip object
+ RGraph.Registry.Get('chart.tooltip').__index__ = Number(idx);
+
+ /**
+ * This converts idx into the index that is not greater than the
+ * number of items in the data array
+ */
+ while (idx >= obj.data[0].length) {
+ idx -= obj.data[0].length
+ }
+
+ RGraph.Registry.Get('chart.tooltip').__index2__ = idx;
+
+ /**
+ * Set the source event
+ */
+ RGraph.Registry.Get('chart.tooltip').__event__ = 'mousemove';
+
+ /**
+ * Highlight the graph
+ */
+ if (obj.Get('chart.tooltips.highlight')) {
+
+ context.beginPath();
+ context.moveTo(xCoord, yCoord);
+ context.arc(xCoord, yCoord, 2, 0, 6.28, 0);
+ context.strokeStyle = obj.Get('chart.highlight.stroke');
+ context.fillStyle = obj.Get('chart.highlight.fill');
+ context.stroke();
+ context.fill();
+ }
+
+ e.stopPropagation();
+ return;
+ }
+ }
+
+ /**
+ * Not over a hotspot?
+ */
+ canvas.style.cursor = 'default';
+ }
+ obj.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(obj.id, 'mousemove', canvas_onmousemove_func);
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.common.zoom.js b/schall/static/RGraph/libraries/RGraph.common.zoom.js
new file mode 100644
index 0000000..2f797b9
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.common.zoom.js
@@ -0,0 +1,886 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {isRGraph:true,type:'common'};
+
+
+ /**
+ * Installs the event handlers for zooming an area of the graph
+ *
+ * @param object obj Your graph object
+ */
+ RGraph.ZoomArea = function (obj)
+ {
+ if (obj.Get('chart.zoom.mode') == 'area') {
+
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ obj.canvas.style.cursor = 'crosshair';
+
+ RGraph.Register(obj);
+
+
+ canvas.onmousedown = function(e)
+ {
+ var canvas = e.target;
+ var coords = RGraph.getMouseXY(e);
+ var obj = canvas.__object__;
+
+ if (RGraph.Registry.Get('chart.zoomed.area.div')) {
+ RGraph.Registry.Get('chart.zoomed.area.div').style.display = 'none';
+ }
+
+ if (typeof(RGraph.Registry.Get('chart.zoomed.area.divs')) == null) {
+ RGraph.Registry.Set('chart.zoomed.area.divs', []);
+
+ } else {
+
+ var divs = RGraph.Registry.Get('chart.zoomed.area.divs');
+
+ if (divs && divs.length) {
+ for (var i=0; i<divs.length; ++i) {
+ if (divs[i]) {
+ divs[i].style.display = 'none';
+ divs[i] = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Create the indicator DIV
+ */
+ var canvasXY = RGraph.getCanvasXY(canvas);
+ var areaDiv = document.createElement('DIV');
+ areaDiv.__canvas__ = canvas;
+ areaDiv.style.position = 'absolute';
+ areaDiv.style.left = canvasXY[0] + coords[0] + 'px'
+ areaDiv.style.top = canvasXY[1] + coords[1] + 'px'
+ areaDiv.style.width = 0;
+ areaDiv.style.height = 0;
+ areaDiv.style.border = '1px solid black';
+ areaDiv.style.backgroundColor = 'rgba(220,220,220,0.3)';
+ areaDiv.style.display = 'none';
+
+ areaDiv.onmouseover = function (e)
+ {
+ setTimeout(function () {e.target.style.opacity = 0.8;}, 50);
+ setTimeout(function () {e.target.style.opacity = 0.6;}, 100);
+ setTimeout(function () {e.target.style.opacity = 0.4;}, 150);
+ setTimeout(function () {e.target.style.opacity = 0.2;}, 200);
+ setTimeout(function () {e.target.style.opacity = 0;}, 250);
+ setTimeout(function () {e.target.style.display = 'none';}, 300);
+ setTimeout(function () {e.target = null;}, 350);
+ }
+
+ document.body.appendChild(areaDiv);
+
+
+ RGraph.Registry.Set('chart.zoomed.area.div', null);
+ RGraph.Registry.Set('chart.zoomed.area.mousedown', coords);
+ RGraph.Registry.Set('chart.zoomed.area.areadiv', areaDiv);
+ }
+
+
+ var window_onmousemove = function (e)
+ {
+ var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown');
+
+ if (startCoords && startCoords.length) {
+
+ var coords = RGraph.getMouseXY(e);
+ var canvasXY = RGraph.getCanvasXY(e.target);
+ var obj = e.target.__object__;
+ var context = obj.context;
+ var canvas = obj.canvas;
+ var startx = startCoords[0];
+ var starty = startCoords[1];
+ var endx = coords[0];
+ var endy = coords[1];
+ var width = endx - startx;
+ var height = endy - starty;
+ var areaDiv = RGraph.Registry.Get('chart.zoomed.area.areadiv');
+
+ if (width >= 0 && height >= 0) {
+
+ if (width > 5 && height > 5) {
+ areaDiv.style.width = (width - 15) + 'px';
+ areaDiv.style.height = (height - 15) + 'px';
+ areaDiv.style.display = 'inline';
+ } else {
+ areaDiv.style.display = 'none';
+ }
+
+ } else if (width < -5 || height < -5) {
+
+ var canvasCoords = RGraph.getCanvasXY(canvas);
+ var noticeDiv = document.createElement('DIV');
+
+ noticeDiv.style.position = 'absolute';
+ noticeDiv.style.top = 0;
+ noticeDiv.style.left = 0;
+ noticeDiv.style.zIndex = 99;
+ noticeDiv.style.border = '1px dashed black';
+ noticeDiv.style.backgroundColor = '#ffc1c1';
+ noticeDiv.style.MozBoxShadow = '0 0 5px #999';
+ noticeDiv.style.WebkitBoxShadow = '0 0 5px #999';
+ noticeDiv.style.boxShadow = '0 0 5px #999';
+ noticeDiv.style.padding = '2px';
+ noticeDiv.innerHTML = 'Zoom goes right and down';
+ document.body.appendChild(noticeDiv);
+
+ // Reposition the warning
+ noticeDiv.style.top = canvasCoords[1] + startCoords[1] - noticeDiv.offsetHeight + 'px';
+ noticeDiv.style.left = canvasCoords[0] + startCoords[0] - (noticeDiv.offsetWidth / 2) + 'px';
+ noticeDiv.style.width = noticeDiv.offsetWidth + 'px';
+
+ setTimeout(function () {noticeDiv.style.opacity = 0.8;}, 2100);
+ setTimeout(function () {noticeDiv.style.opacity = 0.6;}, 2200);
+ setTimeout(function () {noticeDiv.style.opacity = 0.4;}, 2300);
+ setTimeout(function () {noticeDiv.style.opacity = 0.2;}, 2400);
+ setTimeout(function () {noticeDiv.style.display = 'none';}, 2500);
+ setTimeout(function () {noticeDiv = null;}, 2600);
+ }
+ }
+ }
+ window.addEventListener('mousemove', window_onmousemove, false);
+ RGraph.AddEventListener(canvas.id, 'window_mousemove', window_onmousemove);
+
+
+ canvas.onmouseup = function (e)
+ {
+ RGraph.FixEventObject(e);
+
+ var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown');
+ var coords = RGraph.getMouseXY(e);
+
+ // Do the zooming here
+ if (RGraph.Registry.Get('chart.zoomed.area.mousedown')) {
+
+ //RGraph.Redraw();
+
+ RGraph.Registry.Get('chart.zoomed.area.areadiv').style.display = 'none';
+ RGraph.Registry.Get('chart.zoomed.area.areadiv').style.left = '-1000px';
+ RGraph.Registry.Get('chart.zoomed.area.areadiv').style.top = '-1000px';
+ RGraph.Registry.Set('chart.zoomed.area.areadiv', null);
+
+ if (coords[0] < startCoords[0] || coords[1] < startCoords[1]) {
+ RGraph.Registry.Set('chart.zoomed.area.mousedown', false);
+ return;
+ }
+
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var context = obj.context;
+ var canvasCoords = RGraph.getCanvasXY(e.target);
+ var coords = RGraph.getMouseXY(e);
+ var startCoords = RGraph.Registry.Get('chart.zoomed.area.mousedown');
+ var startx = startCoords[0];
+ var starty = startCoords[1];
+ var endx = coords[0] - 15;
+ var endy = coords[1] - 15;
+ var width = Math.abs(endx - startx);
+ var height = Math.abs(endy - starty);
+ var factor = obj.Get('chart.zoom.factor') - 1;
+
+ var img = document.createElement('IMG');
+ img.src = canvas.toDataURL();
+ img.style.position = 'relative';
+ img.style.left = (factor + 1) * -1 * startx + 'px';
+ img.style.top = (factor + 1) * -1 * starty + 'px';
+ img.width = (factor + 1) * canvas.width;
+ img.height = (factor + 1) * canvas.height;
+
+ var div = document.createElement('DIV');
+ div.__object__ = obj;
+ div.className = 'RGraph_zoomed_area';
+ div.style.position = 'absolute';
+ div.style.backgroundColor = 'white';
+ div.style.cursor = 'move';
+
+ div.style.top = e.pageY + 'px';
+ div.style.left = e.pageX + 'px';
+ div.origWidth = width;
+ div.origHeight = height;
+ div.style.width = width + 'px';
+ div.style.height = height + 'px';
+ div.style.border = '1px solid black';
+ div.style.boxShadow = '0 0 15px black';
+ div.style.MozBoxShadow = '0 0 15px black';
+ div.style.WebkitBoxShadow = '0 0 15px black';
+ div.style.overflow = 'hidden';
+ div.style.opacity = 0;
+ div.style.zIndex = 99;
+
+ document.body.appendChild(div);
+ div.appendChild(img);
+
+
+ /**
+ * This facilitates expanding the zoomed area once visible (the double click)
+ */
+ div.ondblclick = function (event)
+ {
+ var event = RGraph.FixEventObject(event);
+ var img = event.target;
+ var div = event.target.parentNode;
+
+ var current_width = div.offsetWidth
+ var current_height = div.offsetHeight
+ var target_width = img.offsetWidth;
+ var target_height = img.offsetHeight;
+ var diff_width = target_width - current_width;
+ var diff_height = target_height - current_height;
+
+ var img_offset_left = parseInt(img.offsetLeft);
+ var img_offset_top = parseInt(img.offsetTop);
+
+ // Global vars on purpose so the timers can access them
+ __zoomed_div_style__ = div.style;
+ __zoomed_img_style__ = img.style;
+
+ setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 50);
+ setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 100);
+ setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 150);
+ setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 200);
+ setTimeout("__zoomed_div_style__.left = parseInt(__zoomed_div_style__.left) - " + (diff_width * 0.1) + " + 'px'", 250);
+
+ setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 50);
+ setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 100);
+ setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 150);
+ setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 200);
+ setTimeout("__zoomed_div_style__.top = parseInt(__zoomed_div_style__.top) - " + (diff_height * 0.1) + " + 'px'", 250);
+
+ setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 50);
+ setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 100);
+ setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 150);
+ setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 200);
+ setTimeout("__zoomed_div_style__.width = parseInt(__zoomed_div_style__.width) + " + (diff_width * 0.2) + " + 'px'", 250);
+
+ setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 50);
+ setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 100);
+ setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 150);
+ setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 200);
+ setTimeout("__zoomed_div_style__.height = parseInt(__zoomed_div_style__.height) + " + (diff_height * 0.2) + " + 'px'", 250);
+
+ // Move the image within the div
+ setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.8) + " + 'px'", 50);
+ setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.6) + " + 'px'", 100);
+ setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.4) + " + 'px'", 150);
+ setTimeout("__zoomed_img_style__.left = " + (img_offset_left * 0.2) + " + 'px'", 200);
+ setTimeout("__zoomed_img_style__.left = 0", 250);
+
+ setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.8) + " + 'px'", 50);
+ setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.6) + " + 'px'", 100);
+ setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.4) + " + 'px'", 150);
+ setTimeout("__zoomed_img_style__.top = " + (img_offset_top * 0.2) + " + 'px'", 200);
+ setTimeout("__zoomed_img_style__.top = 0", 250);
+
+ div.ondblclick = null;
+ }
+ /**
+ * Make the zoomed bit draggable
+ */
+ div.onmousedown = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var div = e.target.parentNode;
+ var img = div.childNodes[0];
+
+ if (e.button == 0 || e.button == 1 ) {
+
+ div.__offsetx__ = e.offsetX + img.offsetLeft;
+ div.__offsety__ = e.offsetY + img.offsetTop;
+
+ RGraph.Registry.Set('chart.mousedown', div);
+ RGraph.Registry.Set('chart.button', 0);
+
+ } else {
+
+ img.__startx__ = e.pageX;
+ img.__starty__ = e.pageY;
+
+ img.__originalx__ = img.offsetLeft;
+ img.__originaly__ = img.offsetTop;
+
+ RGraph.Registry.Set('chart.mousedown', img);
+ RGraph.Registry.Set('chart.button', 2);
+
+ /**
+ * Don't show a context menu when the zoomed area is right-clicked on
+ */
+ window.oncontextmenu = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ e.stopPropagation();
+
+ // [18th July 2010] Is this reallly necessary?
+ window.oncontextmenu = function (e)
+ {
+ return true;
+ }
+
+
+ return false;
+ }
+ }
+
+ e.stopPropagation();
+
+ return false;
+ }
+
+ window.onmouseup = function (e)
+ {
+ RGraph.Registry.Set('chart.mousedown', false);
+ }
+
+ window.onmousemove = function (e)
+ {
+ if (RGraph.Registry.Get('chart.mousedown') && RGraph.Registry.Get('chart.button') == 0) {
+
+ var div = RGraph.Registry.Get('chart.mousedown');
+
+ var x = e.pageX - div.__offsetx__;
+ var y = e.pageY - div.__offsety__;
+
+ div.style.left = x + 'px';
+ div.style.top = y + 'px';
+
+ } else if (RGraph.Registry.Get('chart.mousedown') && RGraph.Registry.Get('chart.button') == 2) {
+
+ var img = RGraph.Registry.Get('chart.mousedown');
+
+ var x = img.__originalx__ + e.pageX - img.__startx__;
+ var y = img.__originaly__ + e.pageY - img.__starty__;
+
+ img.style.left = x + 'px';
+ img.style.top = y + 'px';
+ }
+ }
+ // End dragging code
+
+
+ var divs = RGraph.Registry.Get('chart.zoomed.area.divs');
+ if (typeof(divs) == 'object') {
+ divs.push(div);
+ } else {
+ RGraph.Registry.Set('chart.zoomed.area.divs', [div])
+ }
+
+ // Create the background
+ var bg = document.createElement('DIV');
+ bg.style.position = 'fixed'
+ bg.style.zIndex = 98;
+ bg.style.top = 0;
+ bg.style.left = 0;
+ bg.style.backgroundColor = '#999';
+ bg.style.opacity = 0;
+ bg.style.width = (screen.width + 100) + 'px';
+ bg.style.height = (screen.height + 100) + 'px';
+ document.body.appendChild(bg);
+
+ bg.onclick = function (e)
+ {
+ div.style.display = 'none';
+ bg.style.display = 'none';
+ div = null;
+ bg = null;
+ }
+
+
+ setTimeout(function (){div.style.opacity = 0.2;}, 50);
+ setTimeout(function (){div.style.opacity = 0.4;}, 100);
+ setTimeout(function (){div.style.opacity = 0.6;}, 150);
+ setTimeout(function (){div.style.opacity = 0.8;}, 200);
+ setTimeout(function (){div.style.opacity = 1.0;}, 250);
+
+ setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.1) + 'px'}, 50);
+ setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.2) + 'px'}, 100);
+ setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.3) + 'px'}, 150);
+ setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.4) + 'px'}, 200);
+ setTimeout(function () {div.style.left = canvasCoords[0] + startx - (width * factor * 0.5) + 'px'}, 250);
+
+ setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.1) + 'px'}, 50);
+ setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.2) + 'px'}, 100);
+ setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.3) + 'px'}, 150);
+ setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.4) + 'px'}, 200);
+ setTimeout(function () {div.style.top = canvasCoords[1] + starty - (height * factor * 0.5) + 'px'}, 250);
+
+ setTimeout(function () {div.style.width = width + (width * factor * 0.2) + 'px'}, 50);
+ setTimeout(function () {div.style.width = width + (width * factor * 0.4) + 'px'}, 100);
+ setTimeout(function () {div.style.width = width + (width * factor * 0.6) + 'px'}, 150);
+ setTimeout(function () {div.style.width = width + (width * factor * 0.8) + 'px'}, 200);
+ setTimeout(function () {div.style.width = width + (width * factor * 1.0) + 'px'}, 250);
+
+ setTimeout(function () {div.style.height = height + (height * factor * 0.2) + 'px'}, 50);
+ setTimeout(function () {div.style.height = height + (height * factor * 0.4) + 'px'}, 100);
+ setTimeout(function () {div.style.height = height + (height * factor * 0.6) + 'px'}, 150);
+ setTimeout(function () {div.style.height = height + (height * factor * 0.8) + 'px'}, 200);
+ setTimeout(function () {div.style.height = height + (height * factor * 1.0) + 'px'}, 250);
+
+ setTimeout(function (){bg.style.opacity = 0.1;}, 50);
+ setTimeout(function (){bg.style.opacity = 0.2;}, 100);
+ setTimeout(function (){bg.style.opacity = 0.3;}, 150);
+ setTimeout(function (){bg.style.opacity = 0.4;}, 200);
+ setTimeout(function (){bg.style.opacity = 0.5;}, 250);
+
+ RGraph.Registry.Set('chart.zoomed.area.bg', bg);
+ RGraph.Registry.Set('chart.zoomed.area.img', img);
+ RGraph.Registry.Set('chart.zoomed.area.div', div);
+ RGraph.Registry.Set('chart.zoomed.area.mousedown', null);
+ }
+
+ /**
+ * Fire the zoom event
+ */
+ RGraph.FireCustomEvent(obj, 'onzoom');
+ }
+
+ canvas.onmouseout = function (e)
+ {
+ RGraph.Registry.Set('chart.zoomed.area.areadiv', null);
+ RGraph.Registry.Set('chart.zoomed.area.mousedown', null);
+ RGraph.Registry.Set('chart.zoomed.area.div', null);
+ }
+ }
+ }
+
+
+ /**
+ * This function sets up the zoom window if requested
+ *
+ * @param obj object The graph object
+ */
+ RGraph.ShowZoomWindow = function (obj)
+ {
+ if (obj.Get('chart.zoom.mode') == 'thumbnail') {
+ RGraph.ZoomWindow(obj.canvas);
+ }
+
+ if (obj.Get('chart.zoom.mode') == 'area') {
+ RGraph.ZoomArea(obj);
+ }
+ }
+
+
+ /**
+ * Installs the evnt handler for the zoom window/THUMBNAIL
+ */
+ RGraph.ZoomWindow = function (canvas)
+ {
+ canvas.onmousemove = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var coords = RGraph.getMouseXY(e);
+
+ /**
+ * Create the DIV
+ */
+ if (!RGraph.Registry.Get('chart.zoomed.div')) {
+
+ var div = document.createElement('div');
+ div.className = 'RGraph_zoom_window';
+ div.style.width = obj.Get('chart.zoom.thumbnail.width') + 'px';
+ div.style.height = obj.Get('chart.zoom.thumbnail.height') + 'px';
+
+ // Added back in on the 17th December
+ div.style.border = '2px dashed gray';
+
+ div.style.position = 'absolute';
+ div.style.overflow = 'hidden';
+ div.style.backgroundColor = 'white';
+
+ // Initially the zoomed layer should be off-screen
+ div.style.left = '-1000px';
+ div.style.top = '-1000px';
+
+ // Should these be 0? No.
+ div.style.borderRadius = '5px';
+ div.style.MozBorderRadius = '5px';
+ div.style.WebkitBorderRadius = '5px';
+
+ if (obj.Get('chart.zoom.shadow')) {
+ div.style.boxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';
+ div.style.MozBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';
+ div.style.WebkitBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';
+ }
+
+ //div.style.opacity = 0.2;
+ div.__object__ = obj;
+ document.body.appendChild(div);
+
+ /**
+ * Get the canvas as an image
+ */
+ var img = document.createElement('img');
+ img.width = obj.canvas.width * obj.Get('chart.zoom.factor');
+ img.height = obj.canvas.height * obj.Get('chart.zoom.factor');
+ img.style.position = 'relative';
+ img.style.backgroundColor = 'white';
+ img.__object__ = obj;
+
+ div.appendChild(img);
+
+ RGraph.Registry.Set('chart.zoomed.div', div);
+ RGraph.Registry.Set('chart.zoomed.img', img);
+
+ // Fade the zoom in
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').__object__.canvas.onmouseover()", 5);
+
+ } else {
+
+ div = RGraph.Registry.Get('chart.zoomed.div');
+ img = RGraph.Registry.Get('chart.zoomed.img');
+ }
+
+ // Make sure the image is up-to-date
+ img.src = canvas.toDataURL();
+
+ /**
+ * Ensure the div is visible
+ */
+ if (div && div.style.opacity < 1) {
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400);
+ }
+
+ /**
+ * Get the canvas x/y coords
+ */
+ var c = RGraph.getCanvasXY(obj.canvas);
+ var x = c[0];
+ var y = c[1];
+
+ /**
+ * Position the div and img
+ */
+ var offset = 7;
+
+ div.style.left = (e.pageX - obj.Get('chart.zoom.thumbnail.width') - offset) + 'px';
+ div.style.top = (e.pageY - obj.Get('chart.zoom.thumbnail.height') - offset) + 'px';
+
+ var l = (obj.Get('chart.zoom.thumbnail.width') / 2) - (coords[0] * obj.Get('chart.zoom.factor'));
+ var t = (obj.Get('chart.zoom.thumbnail.height') / 2) - (coords[1] * obj.Get('chart.zoom.factor'));
+
+ // More positioning
+ img.style.left = (l + ((obj.Get('chart.zoom.thumbnail.width') / 2) * obj.Get('chart.zoom.factor'))) + 'px';
+ img.style.top = (t + ((obj.Get('chart.zoom.thumbnail.height') / 2) * obj.Get('chart.zoom.factor'))) + 'px';
+
+ /**
+ * Fire the onzoom event
+ */
+ RGraph.FireCustomEvent(obj, 'onzoom');
+ }
+
+ /**
+ * The onmouseover event. Evidently. Fades the zoom window in
+ */
+ canvas.onmouseover = function (e)
+ {
+ var div = RGraph.Registry.Get('chart.zoomed.div');
+
+ // ???
+ if (!div) return;
+
+ var obj = div.__object__;
+
+ // Used for the enlargement animation
+ var targetWidth = obj.Get('chart.zoom.thumbnail.width');
+ var targetHeight = obj.Get('chart.zoom.thumbnail.height');
+
+ div.style.width = 0;
+ div.style.height = 0;
+
+ if (obj.Get('chart.zoom.fade.in')) {
+
+ RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2;
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 100);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 200);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8", 300);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400);
+
+ } else {
+
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 1);
+ }
+
+ // The enlargement animation frames
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (1/5) ) + "px'", 75);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (2/5) ) + "px'", 150);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (3/5) ) + "px'", 225);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (4/5) ) + "px'", 300);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (5/5) ) + "px'", 325);
+
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (1/5) ) + "px'", 75);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (2/5) ) + "px'", 150);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (3/5) ) + "px'", 225);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (4/5) ) + "px'", 300);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (5/5) ) + "px'", 375);
+ }
+
+ canvas.onmouseout = function (e)
+ {
+ if (RGraph.Registry.Get('chart.zoomed.div') && RGraph.Registry.Get('chart.zoomed.div').__object__.Get('chart.zoom.fade.out')) {
+
+ RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8;
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 100);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 200);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2", 300);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0", 400);
+
+ // Get rid of the zoom window
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 400);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 400);
+
+ } else {
+ // Get rid of the zoom window
+ if (RGraph.Registry.Get('chart.zoomed.div')) {
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 1);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 41);
+ } }
+ }
+ }
+
+
+ /**
+ * A zoom in function
+ *
+ * @param e object The event object
+ */
+ RGraph.Zoom = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ /**
+ * Show the zoom window
+ */
+ //if ((e.target.__canvas__ && e.target.__canvas__.__object__.Get('chart.zoom.mode') == 'thumbnail') || (e.target.parentNode.__canvas__ && e.target.parentNode.__canvas__.__object__.Get('chart.zoom.mode') == 'thumbnail') ) {
+ // return RGraph.ZoomWindow(e);
+ //}
+ if (e && e.target && e.target.__canvas__) {
+ var canvas = e.target.__canvas__;
+
+ /*******************************************************
+ * This is here to facilitate zooming by just a single left click
+ *******************************************************/
+ } else if (e && e.target && e.target.__object__) {
+ var canvas = e.target.__object__.canvas;
+ e.stopPropagation(); // Hmmmm
+ }
+
+ // Fallback for MSIE9
+ if (!canvas) {
+ var registry_canvas = RGraph.Registry.Get('chart.contextmenu').__canvas__;
+ if (registry_canvas) {
+ var canvas = registry_canvas;
+ }
+ }
+
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var dataurl = canvas.toDataURL();
+ var tmp = canvas;
+ var coords = RGraph.getCanvasXY(canvas);
+ var factor = obj.Get('chart.zoom.factor') - 1;
+
+ var x = coords[0];
+ var y = coords[1];
+
+ var img = document.createElement('img');
+ img.className = 'RGraph_zoomed_canvas';
+ img.style.border = '3px solid gray';
+ img.style.width = canvas.width + 'px';
+ img.style.height = canvas.height + 'px';
+ img.style.position = 'absolute';
+ img.style.left = x + 'px';
+ img.style.top = y + 'px';
+ img.style.backgroundColor = 'white';
+ img.style.opacity = obj.Get('chart.zoom.fade.in') ? 0 : 1;
+ img.style.zIndex = 99;
+ img.src = dataurl;
+ document.body.appendChild(img);
+
+ //RGraph.Registry.Set('chart.zoomedimage', img);
+ // Store the zoomed image in a global var - NOT the registry
+ __zoomedimage__ = img;
+ __zoomedimage__.obj = obj;
+
+ // Image onclick should not hide the image
+ img.onclick = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+ e.stopPropagation();
+ return false;
+ }
+
+ setTimeout(function (){window.onclick = RGraph.HideZoomedCanvas;}, 1);
+
+ var width = parseInt(canvas.width);
+ var height = parseInt(canvas.height);
+ var frames = obj.Get('chart.zoom.frames');
+ var delay = obj.Get('chart.zoom.delay');
+
+ // Increase the width over 10 frames - center
+ if (obj.Get('chart.zoom.hdir') == 'center') {
+
+ for (var i=1; i<=frames; ++i) {
+ var newWidth = width * factor * (i/frames) + width;
+ var rightHandEdge = x + canvas.width;
+ var newLeft = (x + (canvas.width / 2)) - (newWidth / 2);
+
+ setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay);
+ }
+
+ // Left
+ } else if (obj.Get('chart.zoom.hdir') == 'left') {
+ for (var i=1; i<=frames; ++i) {
+ var newWidth = width * factor * (i/frames) + width;
+ var rightHandEdge = x + canvas.width;
+ var newLeft = rightHandEdge - newWidth;
+
+ setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay);
+ }
+
+ // Right (default)
+ } else {
+ for (var i=1; i<=frames; ++i) {
+ var newWidth = width * factor * (i/frames) + width;
+ setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'", i * delay);
+ }
+ }
+
+ // Increase the height over 10 frames - up
+ if (obj.Get('chart.zoom.vdir') == 'up') {
+ for (var i=1; i<=frames; ++i) {
+ var newHeight = (height * factor * (i/frames)) + height;
+ var bottomEdge = y + canvas.height;
+ var newTop = bottomEdge - newHeight;
+
+ setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay);
+ }
+
+ // center
+ } else if (obj.Get('chart.zoom.vdir') == 'center') {
+ for (var i=1; i<=frames; ++i) {
+ var newHeight = (height * factor * (i/frames)) + height;
+ var bottomEdge = (y + (canvas.height / 2)) + (newHeight / 2);
+ var newTop = bottomEdge - newHeight;
+
+ setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay);
+ }
+
+ // Down (default
+ } else {
+ for (var i=1; i<=frames; ++i) {
+ setTimeout("__zoomedimage__.style.height = '" + String(height * factor * (i/frames) + height) + "px'", i * delay);
+ }
+ }
+
+ // If enabled, increase the opactity over the requested number of frames
+ if (obj.Get('chart.zoom.fade.in')) {
+ for (var i=1; i<=frames; ++i) {
+ setTimeout("__zoomedimage__.style.opacity = " + Number(i / frames), i * (delay / 2));
+ }
+ }
+
+ // If stipulated, produce a shadow
+ if (obj.Get('chart.zoom.shadow')) {
+ for (var i=1; i<=frames; ++i) {
+ setTimeout("__zoomedimage__.style.boxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay);
+ setTimeout("__zoomedimage__.style.MozBoxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay);
+ setTimeout("__zoomedimage__.style.WebkitBoxShadow = 'rgba(128,128,128," + Number(i / frames) / 2 + ") 0 0 15px'", i * delay);
+ }
+ }
+
+ /**
+ * The onmouseout event. Hides the zoom window. Fades the zoom out
+ *
+ canvas.onmouseout = function (e)
+ {
+ if (RGraph.Registry.Get('chart.zoomed.div') && RGraph.Registry.Get('chart.zoomed.div').__object__.Get('chart.zoom.fade.out')) {
+
+ RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8;
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 100);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 200);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2", 300);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0", 400);
+
+ // Get rid of the zoom window
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 400);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 400);
+
+ } else {
+
+ // Get rid of the zoom window
+ if (RGraph.Registry.Get('chart.zoomed.div')) {
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 1);
+ setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 1);
+ }
+ }
+ }
+ */
+
+ // The background
+ if (obj.Get('chart.zoom.background')) {
+
+ var div = document.createElement('DIV');
+ div.style.backgroundColor = '#999';
+ div.style.opacity = 0;
+ div.style.position = 'fixed';
+ div.style.top = 0;
+ div.style.left = 0;
+ div.style.width = (screen.width + 100) + 'px';
+ div.style.height = (screen.height + 100) + 'px';
+ div.style.zIndex = 98;
+
+ // Hides the zoomed caboodle
+ div.oncontextmenu = function (e)
+ {
+ return RGraph.HideZoomedCanvas(e);
+ }
+
+ // 30th July 2010 - Is this necessary?
+ //for (var i=1; i<=frames; ++i) {
+ // setTimeout('__zoomedbackground__.style.opacity = ' + Number(0.04 * i), i * delay);
+ //
+ // // MSIE doesn't support zoom
+ // //setTimeout('__zoomedbackground__.style.filter = "progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135); Alpha(opacity=10)"', 50);
+ //}
+
+ div.origHeight = div.style.height;
+
+ document.body.appendChild(div);
+
+ __zoomedbackground__ = div;
+
+ // If the window is resized, hide the zoom
+ //window.onresize = RGraph.HideZoomedCanvas;
+
+ for (var i=1; i<=frames; ++i) {
+ setTimeout("__zoomedbackground__.style.opacity = " + (Number(i / frames) * 0.5), i * (delay / 2));
+ }
+ }
+
+ /**
+ * Fire the onzoom event
+ */
+ RGraph.FireCustomEvent(obj, 'onzoom');
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.fuel.js b/schall/static/RGraph/libraries/RGraph.fuel.js
new file mode 100644
index 0000000..2bdef0f
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.fuel.js
@@ -0,0 +1,364 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The Fuel widget constructor
+ *
+ * @param object canvas The canvas object
+ * @param int min The minimum value
+ * @param int max The maximum value
+ * @param int value The indicated value
+ */
+ RGraph.Fuel = function (id, min, max, value)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'fuel';
+ this.isRGraph = true;
+ this.min = min;
+ this.max = max;
+ this.value = value;
+ this.angles = {};
+ this.currentValue = null;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[FUEL] No canvas support');
+ return;
+ }
+
+ /**
+ * The funnel charts properties
+ */
+ this.properties =
+ {
+ 'chart.colors': ['red'],
+ 'chart.needle.color': 'red',
+ 'chart.gutter.left': 5,
+ 'chart.gutter.right': 5,
+ 'chart.gutter.top': 5,
+ 'chart.gutter.bottom': 5,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.background': null,
+ 'chart.icon': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAfCAYAAAD0ma06AAAEGElEQVRIS7VXSyhtYRT+jnfe5FEMjAwUBiQGHikzRWIkkgy8YyDK+xnJK5JCeZSUGKBMiAyYkMxMJAMpSfJ+2/d8695/33NunSPnHqt2Z5+91/9/' + '/' + '/et9a/1b8Pn56dmMBhg/IWDgwNoNzc38PHxkXtN0+Tiexp9eH18fIDj1Bj63N/fw8vLS/wsmcHoqKmXT09PuL29RVFREU5OTvTJ6UIAgioQ+vLe09MTb29v8PX1RWBgICYnJ+XXIqDRWXN0dJT3nIDsWlpadP+lpSWZlD4KmL/8/' + '/7+Ls/S09N1/7y8PISHh+sK/QssDJWcHEyGCnB1dRUDAwPIzMzUx5GpAnZ1dcXy8jK2trbM5j06OsLc3JzISx8q4OzsLOOsAq6treHg4AAeHh4WJbq7u0Nzc7P+PiYmBnt7ezg9PcXExAQCAgLg5OSEx8dHuLu7Wwfc3t7G/v6+yEcjO8rIROGKaWdnZ+jr6zMDjI6OxvT0tDzr6uqS2KtksspwZ2cHjY2NuqSUhnHmilUCraysmElaWloKJpQCjI2NRX5+Pl5eXr6WlCv08/MTEMVOZDH+Zzw4CdlfX1/rDHt7ezE1NQXGkcYEKi4ulkVKYlpLGouBs/JiaGgIZL25uSlecXFxohAz/ccAz8/P4e/vj7q6Ojw8PMje5DNRy94MQ0JCUFtbK2wqKipE+sHBQbi4uPwMQ86ak5ODxMREVFdXIywsDCUlJRJDXnZlmJqaip6eHuTm5kqikGlycjIyMjL+ZrY9JSUgMzQiIgINDQ2ypaqqqkCZWXHsnjQEHB8fR0pKigAxabq7uyWOlJNxtLukTJDs7GxUVlZKDNl5oqKi8Pr6+jOAIyMjiI+Pl5JGQG4F1Qy+LN7f3fiUdGZmBsHBwRgbG8Pw8LD01ba2NmlX0rTtnTQLCwvSjEdHR3FxcSExLCwsRGRkpBR9vePzeMDyw3bT1NT0XXLiT4a7u7s4Pj4GGzd7K8GCgoKEsRR8I4Cm6hwHXV5eiv62GAE5npMTmFuBTCkzmzT7qs5Q9TlW/o6ODlvwhCHPM5SVPZIxYzNeXFxEa2srvL29YTC2GI3aMm3Zeq6urv4LMC0tDRsbG1K8k5KS9DgS0IwhKVFjSsJA22r9/f0oKCgQdvPz83JEmZ2dlcpD9maSshow0KZnlO8Csx9yK3BLKCMJPpf2xGMigdi9WXooaWdn53dxdP+amhrZh4eHh1hfX5cTW319vZyBnp+ffzNkBWBmhYaGysB/j322oCckJCArK0uGMlsJ5ubmBoPxRiMzFlomjr2MGdne3i5ANILRJEtJt6ysTG8h9gDl4am8vFwSUWron1O9LulXIOqk9pWftfdSS40yyj5Uh101wPRryuR7R1ZMX/U1pfy5IF40xcgUnGAc9wsGYxsFhy87kwAAAABJRU5ErkJggg==',
+ 'chart.labels.full': 'F',
+ 'chart.labels.empty': 'E'
+ }
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Fuel.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Fuel.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.Fuel.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Get the center X and Y of the chart. This is the center of the needle bulb
+ */
+ this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
+ this.centery = this.canvas.height - 20 - this.gutterBottom
+
+ /**
+ * Work out the radius of the chart
+ */
+ this.radius = this.canvas.height - this.gutterTop - this.gutterBottom - 20;
+
+ /**
+ * The start and end angles of the chart
+ */
+ this.angles.start = 4.71 - 0.5;
+ this.angles.end = 4.71 + 0.5;
+ this.angles.needle = (((this.value - this.min) / (this.max - this.min)) * (this.angles.end - this.angles.start)) + this.angles.start;
+
+ /**
+ * Draw the fuel guage
+ */
+ this.DrawChart();
+
+
+ /**
+ * Draw the labels on the chart
+ */
+ this.DrawLabels();
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * This function actually draws the chart
+ */
+ RGraph.Fuel.prototype.DrawChart = function ()
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+
+ /**
+ * Draw the "Scale"
+ */
+ this.DrawScale();
+
+ /**
+ * Place the icon on the canvas
+ */
+ if (!RGraph.isIE8()) {
+ this.DrawIcon();
+ }
+
+ /**
+ * Draw the needle on to the chart
+ */
+ if (!this.Get('chart.icon') || RGraph.isIE8()) {
+ this.DrawNeedle();
+ }
+ }
+
+
+ /**
+ * Draws the labels
+ */
+ RGraph.Fuel.prototype.DrawLabels = function ()
+ {
+ var radius = (this.radius - 20);
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ // Draw the left label
+ var y = this.centery - Math.sin(this.angles.start - 3.14) * (this.radius - 25);
+ var x = this.centerx - Math.cos(this.angles.start - 3.14) * (this.radius - 25);
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, this.Get('chart.labels.empty'), 'center', 'center');
+
+ // Draw the right label
+ var y = this.centery - Math.sin(this.angles.start - 3.14) * (this.radius - 25);
+ var x = this.centerx + Math.cos(this.angles.start - 3.14) * (this.radius - 25);
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, this.Get('chart.labels.full'), 'center', 'center');
+ }
+
+
+ /**
+ * Draws the needle
+ */
+ RGraph.Fuel.prototype.DrawNeedle = function ()
+ {
+ // Draw the actual needle
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.lineCap = 'round';
+ this.context.strokeStyle = this.Get('chart.needle.color');
+
+ /**
+ * The angle for the needle
+ */
+ var angle = this.angles.needle;
+
+ this.context.arc(this.centerx, this.centery, this.radius - 30, angle, angle + 0.0001, false);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.stroke();
+
+ this.context.lineWidth = 1;
+
+ // Create the gradient for the bulb
+ var cx = this.centerx + 10;
+ var cy = this.centery - 10
+
+ var grad = this.context.createRadialGradient(cx, cy, 35, cx, cy, 0);
+ grad.addColorStop(0, 'black');
+ grad.addColorStop(1, '#eee');
+
+ if (navigator.userAgent.indexOf('Firefox/6.0') > 0) {
+ grad = this.context.createLinearGradient(cx + 10, cy - 10, cx - 10, cy + 10);
+ grad.addColorStop(1, '#666');
+ grad.addColorStop(0.5, '#ccc');
+ }
+
+ // Draw the bulb
+ this.context.beginPath();
+ this.context.fillStyle = grad;
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery, 20, 0, 6.28, 0);
+ this.context.fill();
+ }
+
+
+ /**
+ * Draws the "scale"
+ */
+ RGraph.Fuel.prototype.DrawScale = function ()
+ {
+ //First draw the fill background
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ this.context.fillStyle = 'white';
+ this.context.arc(this.centerx, this.centery, this.radius, this.angles.start, this.angles.end, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 10, this.angles.end, this.angles.start, true);
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ //First draw the fill itself
+ var start = this.angles.start;
+ var end = this.angles.needle;
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.colors')[0];
+ this.context.arc(this.centerx, this.centery, this.radius, start, end, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 10, end, start, true);
+ this.context.closePath();
+ //this.context.stroke();
+ this.context.fill();
+
+ // This draws the tickmarks
+ for (var a = this.angles.start; a<=this.angles.end+0.01; a+=((this.angles.end - this.angles.start) / 5)) {
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.radius - 10, a, a + 0.0001, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 15, a + 0.0001, a, true);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * Draws the icon
+ */
+ RGraph.Fuel.prototype.DrawIcon = function ()
+ {
+ var img = new Image();
+ img.src = this.Get('chart.icon');
+ img.__object__ = this;
+
+
+ img.onload = function (e)
+ {
+ var img = e.target;
+ var obj = img.__object__;
+ var context = obj.context;
+
+ context.drawImage(this,obj.centerx - (this.width / 2),obj.gutterTop + 30);
+
+ obj.DrawNeedle();
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.funnel.js b/schall/static/RGraph/libraries/RGraph.funnel.js
new file mode 100644
index 0000000..78a8b4e
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.funnel.js
@@ -0,0 +1,679 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The bar chart constructor
+ *
+ * @param object canvas The canvas object
+ * @param array data The chart data
+ */
+ RGraph.Funnel = function (id, data)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'funnel';
+ this.coords = [];
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[FUNNEL] No canvas support');
+ return;
+ }
+
+ /**
+ * The funnel charts properties
+ */
+ this.properties = {
+ 'chart.strokestyle': 'black',
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.labels': null,
+ 'chart.labels.sticks': false,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.colors': ['red', 'green', 'gray', 'blue', 'black', 'gray'],
+ 'chart.text.size': 10,
+ 'chart.text.boxed': true,
+ 'chart.text.halign': 'left',
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.tooltips': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.tooltips.highlight': true,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.taper': true}
+
+ // Store the data
+ this.data = data;
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getSegment;
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Funnel.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Funnel.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.Funnel.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // This stops the coords array from growing
+ this.coords = [];
+
+ RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+ this.DrawFunnel();
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * The tooltip handler
+ */
+ if (this.Get('chart.tooltips')) {
+
+ RGraph.Register(this);
+
+ var canvas_onclick_func = function (e)
+ {
+ RGraph.Redraw();
+
+ var e = RGraph.FixEventObject(e);
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var coords = obj.coords;
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+
+ var idx = segment[2];
+
+ // Is there a tooltip defined?
+ if (!obj.Get('chart.tooltips')[idx] && typeof(obj.Get('chart.tooltips')) != 'function') {
+ return;
+ }
+
+ context.beginPath();
+
+ RGraph.NoShadow(obj);
+
+ context.strokeStyle = obj.Get('chart.highlight.stroke');
+ context.fillStyle = obj.Get('chart.highlight.fill');
+
+ context.moveTo(coords[idx][0], coords[idx][1]);
+ context.lineTo(coords[idx][2], coords[idx][3]);
+ context.lineTo(coords[idx][4], coords[idx][5]);
+ context.lineTo(coords[idx][6], coords[idx][7]);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ /**
+ * Draw the key again for in-graph keys so they don't appear "under" the highlight
+ */
+ if (obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') {
+ RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
+ }
+
+ /**
+ * Redraw the labels if necessary
+ */
+ if (obj.Get('chart.labels')) {
+ obj.DrawLabels();
+ }
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = obj.Get('chart.tooltips')(idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
+ var text = obj.Get('chart.tooltips')[idx](idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
+ var text = obj.Get('chart.tooltips')[idx];
+
+ } else {
+ var text = '';
+ }
+
+ // Show the tooltip
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx);
+
+ // Stop the event propagating
+ e.stopPropagation();
+ }
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
+
+ /**
+ * Onmousemove event handler
+ */
+ var canvas_onmousemove_func = function (e)
+ {
+ var e = RGraph.FixEventObject(e);
+
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var overFunnel = false;
+ var coords = obj.coords;
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+
+ var idx = segment[2];
+
+ // Is there a tooltip defined?
+ if (obj.Get('chart.tooltips')[idx] || typeof(obj.Get('chart.tooltips')) == 'function') {
+
+ overFunnel = true;
+ canvas.style.cursor = 'pointer';
+
+ // Stop the event propagating
+ e.stopPropagation();
+
+ }
+ }
+
+ if (!overFunnel) {
+ canvas.style.cursor = 'default';
+ canvas.style.cursor = 'default';
+ }
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+
+ /**
+ * Draw the labels on the chart
+ */
+ this.DrawLabels();
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail'|| this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * This function actually draws the chart
+ */
+ RGraph.Funnel.prototype.DrawFunnel = function ()
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+ var width = this.canvas.width - this.gutterLeft - this.gutterRight;
+ var height = this.canvas.height - this.gutterTop - this.gutterBottom;
+ var total = RGraph.array_max(this.data);
+ var accheight = this.gutterTop;
+
+
+ /**
+ * Loop through each segment to draw
+ */
+
+ // Set a shadow if it's been requested
+ if (this.Get('chart.shadow')) {
+ context.shadowColor = this.Get('chart.shadow.color');
+ context.shadowBlur = this.Get('chart.shadow.blur');
+ context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ context.shadowOffsetY = this.Get('chart.shadow.offsety');
+ }
+
+ for (i=0; i<this.data.length; ++i) {
+
+ i = Number(i);
+
+ var firstvalue = this.data[0];
+ var firstwidth = (firstvalue / total) * width;
+ var curvalue = this.data[i];
+ var curwidth = (curvalue / total) * width;
+ var curheight = height / this.data.length;
+ var halfCurWidth = (curwidth / 2);
+ var nextvalue = this.data[i + 1] ? this.data[i + 1] : 0;
+ var nextwidth = this.data[i + 1] ? (nextvalue / total) * width : 0;
+ var halfNextWidth = (nextwidth / 2);
+ var center = this.gutterLeft + (firstwidth / 2);
+
+ /**
+ * First segment
+ */
+ if (i == 0) {
+ var x1 = center - halfCurWidth;
+ var y1 = this.gutterTop;
+ var x2 = center + halfCurWidth;
+ var y2 = this.gutterTop;
+ var x3 = center + halfNextWidth;
+ var y3 = accheight + curheight;
+ var x4 = center - halfNextWidth;
+ var y4 = accheight + curheight;
+
+ /**
+ * Subsequent segments
+ */
+ } else {
+ var x1 = center - halfCurWidth;
+ var y1 = accheight;
+ var x2 = center + halfCurWidth;
+ var y2 = accheight;
+ var x3 = center + halfNextWidth;
+ var y3 = accheight + curheight;
+ var x4 = center - halfNextWidth;
+ var y4 = accheight + curheight;
+ }
+
+ /**
+ * Set the fill colour. If i is over 0 then don't use an offset
+ */
+ if (document.all && this.Get('chart.shadow')) {
+ this.DrawIEShadow([x1, y1, x2, y2, x3, y3, x4, y4], i > 0 && this.Get('chart.shadow.offsety') < 0);
+ }
+
+ context.strokeStyle = this.Get('chart.strokestyle');
+ context.fillStyle = this.Get('chart.colors')[i];
+
+ if (halfNextWidth > 0 || this.Get('chart.taper')) {
+ context.beginPath();
+ context.moveTo(x1, y1);
+ context.lineTo(x2, y2);
+ context.lineTo(x3, y3);
+ context.lineTo(x4, y4);
+ context.closePath();
+
+ /**
+ * Store the coordinates
+ */
+ this.coords.push([x1, y1, x2, y2, x3, y3, x4, y4]);
+ }
+
+ // The redrawing if the shadow is on will do the stroke
+ if (!this.Get('chart.shadow')) {
+ context.stroke();
+ }
+
+ context.fill();
+
+ accheight += curheight;
+ }
+
+ /**
+ * If the shadow is enabled, redraw every segment, in order to allow for shadows going upwards
+ */
+ if (this.Get('chart.shadow')) {
+
+ RGraph.NoShadow(this);
+
+ for (i=0; i<this.coords.length; ++i) {
+
+ context.strokeStyle = this.Get('chart.strokestyle');
+ context.fillStyle = this.Get('chart.colors')[i];
+
+ context.beginPath();
+ context.moveTo(this.coords[i][0], this.coords[i][1]);
+ context.lineTo(this.coords[i][2], this.coords[i][3]);
+ context.lineTo(this.coords[i][4], this.coords[i][5]);
+ context.lineTo(this.coords[i][6], this.coords[i][7]);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+ }
+ }
+
+ /**
+ * Lastly, draw the key if necessary
+ */
+ if (this.Get('chart.key') && this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+ }
+
+ /**
+ * Draws the labels
+ */
+ RGraph.Funnel.prototype.DrawLabels = function ()
+ {
+ /**
+ * Draws the labels
+ */
+ if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) {
+
+ var context = this.context;
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+ var color = this.Get('chart.text.color');
+ var labels = this.Get('chart.labels');
+ var halign = this.Get('chart.text.halign') == 'left' ? 'left' : 'center';
+ var bgcolor = this.Get('chart.text.boxed') ? 'white' : null;
+
+ if (typeof(this.Get('chart.labels.x')) == 'number') {
+ var x = this.Get('chart.labels.x');
+ } else {
+ var x = halign == 'left' ? (this.gutterLeft - 15) : ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
+ }
+
+ for (var j=0; j<this.coords.length; ++j) { // MUST be "j"
+
+ context.beginPath();
+
+ // Set the color back to black
+ context.strokeStyle = 'black';
+ context.fillStyle = color;
+
+ // Turn off any shadow
+ RGraph.NoShadow(this);
+
+ var label = labels[j];
+
+ RGraph.Text(context,
+ font,
+ size,
+ x,
+ this.coords[j][1],
+ label,
+ 'center',
+ halign,
+ this.Get('chart.text.boxed'),
+ null,
+ bgcolor);
+
+ if (this.Get('chart.labels.sticks')) {
+ /**
+ * Measure the text
+ */
+ this.context.font = size + 'pt ' + font;
+ var labelWidth = this.context.measureText(label).width;
+
+ /**
+ * Draw the horizontal indicator line
+ */
+ this.context.beginPath();
+ this.context.strokeStyle = 'gray';
+
+ this.context.moveTo(x + labelWidth + 10, this.coords[j][1]);
+ this.context.lineTo(this.coords[j][0] - 10, this.coords[j][1]);
+ this.context.stroke();
+ }
+ }
+
+
+
+ /**
+ * This draws the last labels if defined
+ */
+ var lastLabel = labels[j];
+
+ if (lastLabel) {
+
+ RGraph.Text(context, font, size,x,this.coords[j - 1][5],lastLabel,'center',halign,this.Get('chart.text.boxed'),null,bgcolor);
+
+ if (this.Get('chart.labels.sticks')) {
+ /**
+ * Measure the text
+ */
+ this.context.font = size + 'pt ' + font;
+ var labelWidth = this.context.measureText(lastLabel).width;
+
+ /**
+ * Draw the horizontal indicator line
+ */
+ this.context.beginPath();
+ this.context.strokeStyle = 'gray';
+ this.context.moveTo(x + labelWidth + 10, this.coords[j - 1][7]);
+ this.context.lineTo(this.coords[j - 1][0] - 10, this.coords[j - 1][7]);
+ this.context.stroke();
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function is used by MSIE only to manually draw the shadow
+ *
+ * @param array coords The coords for the bar
+ */
+ RGraph.Funnel.prototype.DrawIEShadow = function (coords, noOffset)
+ {
+ var prevFillStyle = this.context.fillStyle;
+ var offsetx = this.Get('chart.shadow.offsetx');
+ var offsety = this.Get('chart.shadow.offsety');
+ var context = this.context;
+
+ context.lineWidth = 1;
+ context.fillStyle = this.Get('chart.shadow.color');
+
+ // Draw the shadow
+ context.beginPath();
+ context.moveTo(coords[0] + (noOffset ? 0 : offsetx), coords[1] + (noOffset ? 0 : offsety));
+ context.lineTo(coords[2] + (noOffset ? 0 : offsetx), coords[3] + (noOffset ? 0 : offsety));
+ context.lineTo(coords[4] + (noOffset ? 0 : offsetx), coords[5] + (noOffset ? 0 : offsety));
+ context.lineTo(coords[6] + (noOffset ? 0 : offsetx), coords[7] + (noOffset ? 0 : offsety));
+ context.closePath();
+
+ context.fill();
+
+
+
+ // Change the fillstyle back to what it was
+ this.context.fillStyle = prevFillStyle;
+ }
+
+
+ /**
+ * Gets the appropriate segment that has been highlighted
+ */
+ RGraph.Funnel.prototype.getSegment = function (e)
+ {
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var coords = obj.coords;
+ var x = mouseCoords[0];
+ var y = mouseCoords[1];
+
+
+
+ for (i=0; i<coords.length; ++i) {
+ if (
+ x > coords[i][0]
+ && x < coords[i][2]
+ && y > coords[i][1]
+ && y < coords[i][5]
+ ) {
+
+ /**
+ * Handle the right corner
+ */
+ if (x > coords[i][4]) {
+ var w1 = coords[i][2] - coords[i][4];
+ var h1 = coords[i][5] - coords[i][3];;
+ var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
+
+ var w2 = coords[i][2] - mouseCoords[0];
+ var h2 = mouseCoords[1] - coords[i][1];
+ var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
+
+ if (a2 > a1) {
+ continue;
+ }
+
+ /**
+ * Handle the left corner
+ */
+ } else if (x < coords[i][6]) {
+ var w1 = coords[i][6] - coords[i][0];
+ var h1 = coords[i][7] - coords[i][1];;
+ var a1 = Math.atan(h1 / w1) * 57.3; // DEGREES
+
+ var w2 = mouseCoords[0] - coords[i][0];
+ var h2 = mouseCoords[1] - coords[i][1];
+ var a2 = Math.atan(h2 / w2) * 57.3; // DEGREES
+
+ if (a2 > a1) {
+ continue;
+ }
+ }
+
+ return [obj, coords[i], i];
+ }
+ }
+
+ return null;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.gantt.js b/schall/static/RGraph/libraries/RGraph.gantt.js
new file mode 100644
index 0000000..1e0ae49
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.gantt.js
@@ -0,0 +1,514 @@
+7 /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The gantt chart constructor
+ *
+ * @param object canvas The cxanvas object
+ * @param array data The chart data
+ */
+ RGraph.Gantt = function (id)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext("2d");
+ this.canvas.__object__ = this;
+ this.type = 'gantt';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Set some defaults
+ this.properties = {
+ 'chart.background.barcolor1': 'white',
+ 'chart.background.barcolor2': 'white',
+ 'chart.background.grid': true,
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.hsize': 20,
+ 'chart.background.grid.vsize': 20,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':false,
+ 'chart.background.grid.autofit.numhlines': 7,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.background.vbars': [],
+ 'chart.text.size': 10,
+ 'chart.text.font': 'Verdana',
+ 'chart.text.color': 'black',
+ 'chart.gutter.left': 75,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 35,
+ 'chart.gutter.bottom': 25,
+ 'chart.labels': [],
+ 'chart.margin': 2,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.pos': null,
+ 'chart.title.yaxis.position': 'right',
+ 'chart.events': [],
+ 'chart.borders': true,
+ 'chart.defaultcolor': 'white',
+ 'chart.coords': [],
+ 'chart.tooltips': [],
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.xmin': 0,
+ 'chart.xmax': 0,
+ 'chart.contextmenu': null,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A peudo setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Gantt.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A peudo getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Gantt.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * Draws the chart
+ */
+ RGraph.Gantt.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Work out the graphArea
+ */
+ this.graphArea = this.canvas.width - this.gutterLeft - this.gutterRight;
+ this.graphHeight = this.canvas.height - this.gutterTop - this.gutterBottom;
+ this.numEvents = this.Get('chart.events').length
+ this.barHeight = this.graphHeight / this.numEvents;
+ this.halfBarHeight = this.barHeight / 2;
+
+ /**
+ * Draw the background
+ */
+ RGraph.background.Draw(this);
+
+ /**
+ * Draw a space for the left hand labels
+ */
+ //this.context.beginPath();
+ //this.context.lineWidth = 1;
+ //this.context.strokeStyle = this.Get('chart.background.grid.color');
+ //this.context.fillStyle = 'white';
+ //this.context.fillRect(0,gutter - 5,gutter * 3, RGraph.GetHeight(this) - (2 * gutter) + 10);
+ //this.context.moveTo(gutter * 3, gutter);
+ //this.context.lineTo(gutter * 3, RGraph.GetHeight(this) - gutter);
+ //this.context.stroke();
+ //this.context.fill();
+
+ /**
+ * Draw the labels at the top
+ */
+ this.DrawLabels();
+
+ /**
+ * Draw the events
+ */
+ this.DrawEvents();
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * This function enables adjusting
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the labels at the top and the left of the chart
+ */
+ RGraph.Gantt.prototype.DrawLabels = function ()
+ {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ /**
+ * Draw the X labels at the top of the chart.
+ */
+ var labelSpace = (this.graphArea) / this.Get('chart.labels').length;
+ var xPos = this.gutterLeft + (labelSpace / 2);
+ this.context.strokeStyle = 'black'
+
+ for (i=0; i<this.Get('chart.labels').length; ++i) {
+ RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size'),xPos + (i * labelSpace),this.gutterTop - (this.Get('chart.text.size') / 2) - 5,String(this.Get('chart.labels')[i]),'center','center');
+ }
+
+ // Draw the vertical labels
+ for (var i=0; i<this.Get('chart.events').length; ++i) {
+ var ev = this.Get('chart.events')[i];
+ var x = this.gutterLeft;
+ var y = this.gutterTop + this.halfBarHeight + (i * this.barHeight);
+
+ RGraph.Text(this.context,
+ this.Get('chart.text.font'),
+ this.Get('chart.text.size'),
+ x - 5, y,
+ RGraph.is_array(ev[0]) ? String(ev[0][3]) : String(ev[3]),
+ 'center',
+ 'right');
+ }
+ }
+
+ /**
+ * Draws the events to the canvas
+ */
+ RGraph.Gantt.prototype.DrawEvents = function ()
+ {
+ var canvas = this.canvas;
+ var context = this.context;
+ var events = this.Get('chart.events');
+
+ /**
+ * Reset the coords array to prevent it growing
+ */
+ this.coords = [];
+
+ /**
+ * First draw the vertical bars that have been added
+ */
+ if (this.Get('chart.vbars')) {
+ for (i=0; i<this.Get('chart.vbars').length; ++i) {
+ // Boundary checking
+ if (this.Get('chart.vbars')[i][0] + this.Get('chart.vbars')[i][1] > this.Get('chart.xmax')) {
+ this.Get('chart.vbars')[i][1] = 364 - this.Get('chart.vbars')[i][0];
+ }
+
+ var barX = this.gutterLeft + (( (this.Get('chart.vbars')[i][0] - this.Get('chart.xmin')) / (this.Get('chart.xmax') - this.Get('chart.xmin')) ) * this.graphArea);
+
+ var barY = this.gutterTop;
+ var width = (this.graphArea / (this.Get('chart.xmax') - this.Get('chart.xmin')) ) * this.Get('chart.vbars')[i][1];
+ var height = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+
+ // Right hand bounds checking
+ if ( (barX + width) > (RGraph.GetWidth(this) - this.gutterRight) ) {
+ width = RGraph.GetWidth(this) - this.gutterRight - barX;
+ }
+
+ context.fillStyle = this.Get('chart.vbars')[i][2];
+ context.fillRect(barX, barY, width, height);
+ }
+ }
+
+
+ /**
+ * Draw the events
+ */
+ for (i=0; i<events.length; ++i) {
+ if (typeof(events[i][0]) == 'number') {
+ this.DrawSingleEvent(events[i]);
+ } else {
+ for (var j=0; j<events[i].length; ++j) {
+ this.DrawSingleEvent(events[i][j]);
+ }
+ }
+
+ }
+
+
+ /**
+ * If tooltips are defined, handle them
+ */
+ if (this.Get('chart.tooltips')) {
+
+ // Register the object for redrawing
+ RGraph.Register(this);
+
+ /**
+ * If the cursor is over a hotspot, change the cursor to a hand
+ */
+ var canvas_onmousemove_func = function (eventObj)
+ {
+ eventObj = RGraph.FixEventObject(eventObj);
+ var canvas = eventObj.target;
+ var obj = canvas.__object__;
+ var len = obj.coords.length;
+ var mouseCoords = RGraph.getMouseXY(eventObj);
+ var bar = obj.getBar(eventObj);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ if (bar) {
+ canvas.style.cursor = 'pointer';
+ return;
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+
+
+ var canvas_onclick_func = function (eventObj)
+ {
+ eventObj = RGraph.FixEventObject(eventObj);
+
+ var canvas = eventObj.target;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(eventObj);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var bar = obj.getBar(eventObj);
+
+ if (bar) {
+
+ var idx = bar[5];
+
+ // Redraw the graph
+ RGraph.Redraw();
+
+ // Get the tooltip text
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx);
+
+ if (String(text).length && text != '') {
+
+ // SHOW THE CORRECT TOOLTIP
+ RGraph.Tooltip(canvas, text, eventObj.pageX, eventObj.pageY, idx);
+
+ /**
+ * Draw a rectangle around the correct bar, in effect highlighting it
+ */
+
+ context.lineWidth = 1;
+ context.strokeStyle = obj.Get('chart.highlight.stroke');
+ context.fillStyle = obj.Get('chart.highlight.fill');
+ context.strokeRect(bar[1], bar[2], bar[3], bar[4]);
+ context.fillRect(bar[1], bar[2], bar[3], bar[4]);
+
+ eventObj.stopPropagation();
+ }
+
+ return;
+ }
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
+ }
+ }
+
+
+ /**
+ * Retrieves the bar (if any) that has been click on or is hovered over
+ *
+ * @param object e The event object
+ */
+ RGraph.Gantt.prototype.getBar = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var coords = obj.coords;
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0; i<coords.length; i++) {
+
+ var left = coords[i][0];
+ var top = coords[i][1];
+ var width = coords[i][2];
+ var height = coords[i][3];
+
+ if ( mouseX >= left
+ && mouseX <= (left + width)
+ && mouseY >= top
+ && mouseY <= (top + height)
+ && (typeof(obj.Get('chart.tooltips')) == 'function' || obj.Get('chart.tooltips')[i]) ) {
+
+ return [obj, left, top, width, height, i];
+ }
+ }
+ }
+
+
+ /**
+ * Draws a single event
+ */
+ RGraph.Gantt.prototype.DrawSingleEvent = function ()
+ {
+ var min = this.Get('chart.xmin');
+ var context = this.context;
+ var ev = RGraph.array_clone(arguments[0]);
+
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.fillStyle = ev[4] ? ev[4] : this.Get('chart.defaultcolor');
+
+ var barStartX = this.gutterLeft + (((ev[0] - min) / (this.Get('chart.xmax') - min)) * this.graphArea);
+ //barStartX += this.margin;
+ var barStartY = this.gutterTop + (i * this.barHeight);
+ var barWidth = (ev[1] / (this.Get('chart.xmax') - min) ) * this.graphArea;
+
+ /**
+ * If the width is greater than the graph atrea, curtail it
+ */
+ if ( (barStartX + barWidth) > (RGraph.GetWidth(this) - this.gutterRight) ) {
+ barWidth = RGraph.GetWidth(this) - this.gutterRight - barStartX;
+ }
+
+ /**
+ * Draw the actual bar storing store the coordinates
+ */
+ this.coords.push([barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin'))]);
+ context.fillRect(barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin')) );
+
+ // Work out the completeage indicator
+ var complete = (ev[2] / 100) * barWidth;
+
+ // Draw the % complete indicator. If it's greater than 0
+ if (typeof(ev[2]) == 'number') {
+ context.beginPath();
+ context.fillStyle = ev[5] ? ev[5] : '#0c0';
+ context.fillRect(barStartX,
+ barStartY + this.Get('chart.margin'),
+ (ev[2] / 100) * barWidth,
+ this.barHeight - (2 * this.Get('chart.margin')) );
+
+ context.beginPath();
+ context.fillStyle = this.Get('chart.text.color');
+ RGraph.Text(context, this.Get('chart.text.font'), this.Get('chart.text.size'), barStartX + barWidth + 5, barStartY + this.halfBarHeight, String(ev[2]) + '%', 'center');
+ }
+
+ // draw the border around the bar
+ if (this.Get('chart.borders') || ev[6]) {
+ context.strokeStyle = typeof(ev[6]) == 'string' ? ev[6] : 'black';
+ context.lineWidth = (typeof(ev[7]) == 'number' ? ev[7] : 1);
+ context.beginPath();
+ context.strokeRect(barStartX, barStartY + this.Get('chart.margin'), barWidth, this.barHeight - (2 * this.Get('chart.margin')) );
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.gauge.js b/schall/static/RGraph/libraries/RGraph.gauge.js
new file mode 100644
index 0000000..80a445e
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.gauge.js
@@ -0,0 +1,464 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The line chart constructor
+ *
+ * @param object canvas The cxanvas object
+ * @param array data The chart data
+ * @param array ... Other lines to plot
+ */
+ RGraph.Gauge = function (id, min, max, value)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'gauge';
+ this.min = min;
+ this.max = max;
+ this.value = value;
+ this.isRGraph = true;
+ this.currentValue = null;
+
+ /**
+ * Range checking
+ */
+ if (this.value > this.max) {
+ this.value = max;
+ }
+
+ if (this.value < this.min) {
+ this.value = min;
+ }
+
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config type stuff
+ this.properties = {
+ 'chart.gutter.left': 5,
+ 'chart.gutter.right': 5,
+ 'chart.gutter.top': 5,
+ 'chart.gutter.bottom': 5,
+ 'chart.border.width': 10,
+ 'chart.title': '',
+ 'chart.title.font': 'Verdana',
+ 'chart.title.size': 14,
+ 'chart.title.color': '#333',
+ 'chart.title.bold': false,
+ 'chart.text.font': 'Verdana',
+ 'chart.text.color': '#666',
+ 'chart.text.size': 10,
+ 'chart.scale.decimals': 0,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.red.start': 0.9 * this.max,
+ 'chart.red.color': '#DC3912',
+ 'chart.yellow.color': '#FF9900',
+ 'chart.green.end': 0.7 * this.max,
+ 'chart.green.color': 'rgba(0,0,0,0)',
+ 'chart.needle.tail': false
+ }
+ }
+
+
+
+ /**
+ * An all encompassing accessor
+ *
+ * @param string name The name of the property
+ * @param mixed value The value of the property
+ */
+ RGraph.Gauge.prototype.Set = function (name, value)
+ {
+ this.properties[name] = value;
+ }
+
+
+ /**
+ * An all encompassing accessor
+ *
+ * @param string name The name of the property
+ */
+ RGraph.Gauge.prototype.Get = function (name)
+ {
+ return this.properties[name];
+ }
+
+
+ /**
+ * The function you call to draw the line chart
+ *
+ * @param bool An optional bool used internally to ditinguish whether the
+ * line chart is being called by the bar chart
+ */
+ RGraph.Gauge.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+ /**
+ * Store the value (for animation primarily
+ */
+ this.currentValue = this.value;
+
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
+ this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+ this.radius = Math.min(
+ ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2),
+ ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2)
+ );
+ this.startAngle = (1.57 / 3) + 1.57;
+ this.endAngle = 6.28 + 1.57 - (1.57 / 3);
+
+ // This has to be in the constructor
+ this.centerpinRadius = (15/125) * this.radius;
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+
+
+ // DRAW THE CHART HERE
+ this.DrawBackGround();
+ this.DrawColorBands();
+ this.DrawSmallTickmarks();
+ this.DrawBigTickmarks();
+ this.DrawLabels();
+ this.DrawTitle();
+ this.DrawNeedle();
+ this.DrawCenterpin();
+
+
+
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+ /**
+ * This function enables the zoom in area mode
+ */
+ if (this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ZoomArea(this);
+ }
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draw the background
+ */
+ RGraph.Gauge.prototype.DrawBackGround = function ()
+ {
+ var borderWidth = this.Get('chart.border.width');
+
+ this.context.beginPath();
+ this.context.fillStyle = 'white';
+
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0);
+ this.context.fill();
+
+ /**
+ * Draw the gray circle
+ */
+ this.context.beginPath();
+ this.context.fillStyle = '#ccc';;
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0);
+ this.context.fill();
+
+ /**
+ * Draw the light gray inner border
+ */
+ this.context.beginPath();
+ this.context.fillStyle = '#f1f1f1';
+ this.context.arc(this.centerx, this.centery, this.radius - borderWidth, 0, 6.28, 0);
+ this.context.fill();
+
+ // Draw the white circle inner border
+ this.context.beginPath();
+ this.context.fillStyle = 'white';
+ this.context.arc(this.centerx, this.centery, this.radius - borderWidth - 4, 0, 6.28, 0);
+ this.context.fill();
+
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ //this.context.moveTo(this.centerx, this.centery)
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, 0);
+ this.context.stroke();
+ }
+
+
+ /**
+ * This function draws the smaller tickmarks
+ */
+ RGraph.Gauge.prototype.DrawSmallTickmarks = function ()
+ {
+ var numTicks = 25;
+
+ for (var i=0; i<=numTicks; ++i) {
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
+ this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10, a, a + 0.00001, 0);
+ this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10 - 5, a, a + 0.00001, 0);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * This function draws the large, bold tickmarks
+ */
+ RGraph.Gauge.prototype.DrawBigTickmarks = function ()
+ {
+ var numTicks = 5;
+ this.context.lineWidth = 3;
+ this.context.lineCap = 'round';
+
+ for (var i=0; i<=numTicks; ++i) {
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ var a = (((this.endAngle - this.startAngle) / numTicks) * i) + this.startAngle;
+ this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10, a, a + 0.00001, 0);
+ this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.border.width') - 10 - 10, a, a + 0.00001, 0);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * This function draws the centerpin
+ */
+ RGraph.Gauge.prototype.DrawCenterpin = function ()
+ {
+ var offset = 6;
+
+ var grad = this.context.createRadialGradient(this.centerx + offset, this.centery - offset, 0, this.centerx + offset, this.centery - offset, 25);
+ grad.addColorStop(0, '#ddf');
+ grad.addColorStop(1, 'blue');
+
+ this.context.beginPath();
+ this.context.fillStyle = grad;
+ this.context.arc(this.centerx, this.centery, this.centerpinRadius, 0, 6.28, 0);
+ this.context.fill();
+ }
+
+
+ /**
+ * This function draws the labels
+ */
+ RGraph.Gauge.prototype.DrawLabels = function ()
+ {
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ this.context.beginPath();
+ // First label
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery + Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')), this.Get('chart.units.pre') + String(this.min.toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'bottom', 'left');
+
+ // Second label
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - this.radius + 25 + this.Get('chart.border.width'), this.centery,this.Get('chart.units.pre') + String((((this.max - this.min) * 0.2) + this.min).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post')),'center', 'left');
+
+ // Third label
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx - Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery - Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String((((this.max - this.min) * 0.4) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'top', 'center');
+
+ // Fourth label
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery - Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String((((this.max - this.min) * 0.6) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'top', 'center');
+
+ // Fifth label
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + this.radius - 25 - this.Get('chart.border.width'), this.centery, this.Get('chart.units.pre') + String((((this.max - this.min) * 0.8) + this.min).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'center', 'right');
+
+ // Sixth (last) label
+ RGraph.Text(this.context,this.Get('chart.text.font'), this.Get('chart.text.size'), this.centerx + Math.sin(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.centery + Math.cos(0.52) * (this.radius - 25 - this.Get('chart.border.width')),this.Get('chart.units.pre') + String(this.max.toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),'bottom', 'right');
+ this.context.fill();
+ }
+
+
+ /**
+ * This function draws the title
+ */
+ RGraph.Gauge.prototype.DrawTitle = function ()
+ {
+ var title = this.Get('chart.title');
+
+ if (title) {
+
+ this.context.fillStyle = this.Get('chart.title.color');
+
+ this.context.beginPath();
+ RGraph.Text(this.context,
+ this.Get('chart.title.font'),
+ this.Get('chart.title.size'),
+ this.centerx,
+ this.centery - 30,
+ String(this.Get('chart.title')),
+ 'center',
+ 'center',
+ null,
+ null,
+ null,
+ this.Get('chart.title.bold'));
+ this.context.fill();
+ }
+ }
+
+
+ /**
+ * This function draws the Needle
+ */
+ RGraph.Gauge.prototype.DrawNeedle = function ()
+ {
+ this.context.lineWidth = 0.5;
+ this.context.strokeStyle = '#983724';
+ this.context.fillStyle = '#D5604D';
+
+ var angle = (this.endAngle - this.startAngle) * ((this.value - this.min) / (this.max - this.min));
+ angle += this.startAngle;
+
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.radius - 25 - this.Get('chart.border.width'), angle, angle + 0.00001, false);
+ this.context.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle + 1.57, angle + 0.00001 + 1.57, false);
+
+ if (this.Get('chart.needle.tail')) {
+ this.context.arc(this.centerx, this.centery, this.radius * 0.2 , angle + 3.14, angle + 0.00001 + 3.14, false);
+ }
+
+ this.context.arc(this.centerx, this.centery, this.centerpinRadius * 0.5, angle - 1.57, angle - 0.00001 - 1.57, false);
+ this.context.stroke();
+ this.context.fill();
+
+
+ //this.context.strokeStyle = '#D5604D';
+
+ //this.context.beginPath();
+ // this.context.lineWidth = 5;
+ // this.context.arc(this.centerx, this.centery, 25, angle + 3.14, angle + 0.00001 + 3.14, false);
+ // this.context.lineTo(this.centerx, this.centery);
+ //this.context.stroke();
+ //this.context.fill();
+
+ /**
+ * Store the angle in an object variable
+ */
+ this.angle = angle;
+ }
+
+
+ /**
+ * This draws the green background to the tickmarks
+ */
+ RGraph.Gauge.prototype.DrawColorBands = function ()
+ {
+ /**
+ * Draw the GREEN region
+ */
+ this.context.strokeStyle = this.Get('chart.green.color');
+ this.context.fillStyle = this.Get('chart.green.color');
+
+ var greenStart = this.startAngle;
+ var greenEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.Get('chart.green.end') - this.min) / (this.max - this.min))
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), greenStart, greenEnd, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), greenEnd, greenStart, true);
+ this.context.fill();
+
+
+ /**
+ * Draw the YELLOW region
+ */
+ this.context.strokeStyle = this.Get('chart.yellow.color');
+ this.context.fillStyle = this.Get('chart.yellow.color');
+
+ var yellowStart = greenEnd;
+ var yellowEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.Get('chart.red.start') - this.min) / (this.max - this.min))
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), yellowStart, yellowEnd, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), yellowEnd, yellowStart, true);
+ this.context.fill();
+
+
+ /**
+ * Draw the RED region
+ */
+ this.context.strokeStyle = this.Get('chart.red.color');
+ this.context.fillStyle = this.Get('chart.red.color');
+
+ var redStart = yellowEnd;
+ var redEnd = this.startAngle + (this.endAngle - this.startAngle) * ((this.max - this.min) / (this.max - this.min))
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.radius - 10 - this.Get('chart.border.width'), redStart, redEnd, false);
+ this.context.arc(this.centerx, this.centery, this.radius - 20 - this.Get('chart.border.width'), redEnd, redStart, true);
+ this.context.fill();
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.hbar.js b/schall/static/RGraph/libraries/RGraph.hbar.js
new file mode 100644
index 0000000..a8f39d6
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.hbar.js
@@ -0,0 +1,966 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The horizontal bar chart constructor. The horizontal bar is a minor variant
+ * on the bar chart. If you have big labels, this may be useful as there is usually
+ * more space available for them.
+ *
+ * @param object canvas The canvas object
+ * @param array data The chart data
+ */
+ RGraph.HBar = function (id, data)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.data = data;
+ this.type = 'hbar';
+ this.coords = [];
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ this.max = 0;
+ this.stackedOrGrouped = false;
+
+ // Default properties
+ this.properties = {
+ 'chart.gutter.left': 75,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 35,
+ 'chart.gutter.bottom': 25,
+ 'chart.background.grid': true,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.hsize': 25,
+ 'chart.background.grid.vsize': 25,
+ 'chart.background.barcolor1': 'white',
+ 'chart.background.barcolor2': 'white',
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':true,
+ 'chart.background.grid.autofit.numhlines': 14,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.xaxis': '',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': 10,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.colors': ['red', 'blue', 'green', 'pink', 'yellow', 'cyan', 'navy', 'gray', 'black'],
+ 'chart.labels': [],
+ 'chart.labels.above': false,
+ 'chart.labels.above.decimals': 0,
+ 'chart.xlabels': true,
+ 'chart.contextmenu': null,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.units.ingraph': false,
+ 'chart.strokestyle': 'black',
+ 'chart.xmin': 0,
+ 'chart.xmax': 0,
+ 'chart.axis.color': 'black',
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.vmargin': 3,
+ 'chart.grouping': 'grouped',
+ 'chart.tooltips': null,
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.highlight.stroke': 'black',
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.scale.decimals': null,
+ 'chart.noredraw': false
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[HBAR] No canvas support');
+ return;
+ }
+
+ for (i=0; i<this.data.length; ++i) {
+ if (typeof(this.data[i]) == 'object') {
+ this.stackedOrGrouped = true;
+ }
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.HBar.prototype.Set = function (name, value)
+ {
+ if (name == 'chart.labels.abovebar') {
+ name = 'chart.labels.above';
+ }
+
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.HBar.prototype.Get = function (name)
+ {
+ if (name == 'chart.labels.abovebar') {
+ name = 'chart.labels.above';
+ }
+
+ return this.properties[name];
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.HBar.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Stop the coords array from growing uncontrollably
+ */
+ this.coords = [];
+ this.max = 0;
+
+ /**
+ * Check for chart.xmin in stacked charts
+ */
+ if (this.Get('chart.xmin') > 0 && this.Get('chart.grouping') == 'stacked') {
+ alert('[HBAR] Using chart.xmin is not supported with stacked charts, resetting chart.xmin to zero');
+ this.Set('chart.xmin', 0);
+ }
+
+ /**
+ * Work out a few things. They need to be here because they depend on things you can change before you
+ * call Draw() but after you instantiate the object
+ */
+ this.graphwidth = this.canvas.width - this.gutterLeft - this.gutterRight;
+ this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom;
+ this.halfgrapharea = this.grapharea / 2;
+ this.halfTextHeight = this.Get('chart.text.size') / 2;
+
+
+ // Progressively Draw the chart
+ RGraph.background.Draw(this);
+
+ this.Drawbars();
+ this.DrawAxes();
+ this.DrawLabels();
+
+
+ // Draw the key if necessary
+ if (this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+ /**
+ * Install the event handlers for tooltips
+ */
+ if (this.Get('chart.tooltips')) {
+
+ // Need to register this object for redrawing
+ RGraph.Register(this);
+
+ /**
+ * Install the window onclick handler
+ */
+ window.onclick = function ()
+ {
+ RGraph.Redraw();
+ }
+
+
+
+ /**
+ * If the cursor is over a hotspot, change the cursor to a hand
+ */
+ //this.canvas.onmousemove = function (e)
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Get the mouse X/Y coordinates
+ */
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ if (bar && (typeof(obj.Get('chart.tooltips')) == 'function' || obj.Get('chart.tooltips')[bar[4]]) ) {
+
+ canvas.style.cursor = 'pointer';
+
+ var left = bar[0];
+ var top = bar[1];
+ var width = bar[2];
+ var height = bar[3];
+ var idx = bar[4];
+
+ /**
+ * Show the tooltip if the event is onmousemove
+ */
+ if (obj.Get('chart.tooltips.event') == 'onmousemove') {
+
+ var tooltipObj = RGraph.Registry.Get('chart.tooltip');
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = obj.Get('chart.tooltips')(idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
+ var text = obj.Get('chart.tooltips')[idx](idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
+ var text = obj.Get('chart.tooltips')[idx];
+
+ } else {
+ var text = null;
+ }
+
+ if (text) {
+ if (!tooltipObj || tooltipObj.__index__ != idx) {
+
+ RGraph.HideTooltip();
+ RGraph.Redraw();
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(left, top, width, height);
+ obj.context.fillRect(left, top, width, height);
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx);
+ }
+ return;
+ }
+ }
+ }
+
+ if (!bar) {
+ canvas.style.cursor = 'default';
+ }
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ //this.canvas.onclick = function (e)
+ var canvas_onclick_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ //var canvas = document.getElementById(this.id);
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Redraw();
+
+ /**
+ * Get the mouse X/Y coordinates
+ */
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ /*******************************************************
+ * Only do this if a bar is being hovered over
+ *******************************************************/
+ if (bar) {
+
+ var left = bar[0];
+ var top = bar[1];
+ var width = bar[2];
+ var height = bar[3];
+ var idx = bar[4];
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = obj.Get('chart.tooltips')(idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[idx]) == 'function') {
+ var text = obj.Get('chart.tooltips')[idx](idx);
+
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object') {
+ var text = obj.Get('chart.tooltips')[idx];
+
+ } else {
+ var text = null;
+ }
+
+ /**
+ * Show a tooltip if it's defined
+ */
+ if (String(text).length && text != null) {
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(left, top, width, height);
+ obj.context.fillRect(left, top, width, height);
+
+ obj.context.stroke();
+ obj.context.fill();
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, idx);
+ }
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id,'click', canvas_onclick_func);
+
+ // This resets the bar graph
+ if (RGraph.Registry.Get('chart.tooltip')) {
+ RGraph.Registry.Get('chart.tooltip').style.display = 'none';
+ RGraph.Registry.Set('chart.tooltip', null)
+ }
+ }
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+
+ /**
+ * Draw "in graph" labels
+ */
+ RGraph.DrawInGraphLabels(this);
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+ /**
+ * This draws the axes
+ */
+ RGraph.HBar.prototype.DrawAxes = function ()
+ {
+ var halfway = (this.graphwidth / 2) + this.gutterLeft;
+
+ this.context.beginPath();
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.axis.color');
+
+ // Draw the Y axis
+ if (this.Get('chart.yaxispos') == 'center') {
+ this.context.moveTo(halfway, this.gutterTop);
+ this.context.lineTo(halfway, this.canvas.height - this.gutterBottom);
+ } else {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ }
+
+ // Draw the X axis
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom);
+
+ // Draw the Y tickmarks
+ var yTickGap = (this.canvas.height - this.gutterTop - this.gutterBottom) / this.data.length;
+
+ for (y=this.gutterTop; y<(this.canvas.height - this.gutterBottom - 1); y+=yTickGap) {
+ if (this.Get('chart.yaxispos') == 'center') {
+ this.context.moveTo(halfway + 3, y);
+ this.context.lineTo(halfway - 3, y);
+ } else {
+ this.context.moveTo(this.gutterLeft, y);
+ this.context.lineTo( this.gutterLeft - 3, y);
+ }
+ }
+
+
+ // Draw the X tickmarks
+ xTickGap = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight ) / 10;
+ yStart = RGraph.GetHeight(this) - this.gutterBottom;
+ yEnd = (RGraph.GetHeight(this) - this.gutterBottom) + 3;
+
+ for (x=(RGraph.GetWidth(this) - this.gutterRight), i=0; this.Get('chart.yaxispos') == 'center' ? x>=this.gutterLeft : x>this.gutterLeft; x-=xTickGap) {
+
+ if (this.Get('chart.yaxispos') != 'center' || i != 5) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+ }
+ i++;
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * This draws the labels for the graph
+ */
+ RGraph.HBar.prototype.DrawLabels = function ()
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var text_size = this.Get('chart.text.size');
+ var font = this.Get('chart.text.font');
+
+
+ /**
+ * Set the units to blank if they're to be used for ingraph labels only
+ */
+ if (this.Get('chart.units.ingraph')) {
+ units_pre = '';
+ units_post = '';
+ }
+
+
+ /**
+ * Draw the X axis labels
+ */
+ if (this.Get('chart.xlabels')) {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ if (this.Get('chart.yaxispos') == 'center') {
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (10/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (9/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (8/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (7/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (6/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (4/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (3/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (2/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (1/10)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (0)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, '-' + RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+
+ } else {
+
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (5/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (4/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (3/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (2/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+ RGraph.Text(context, font, text_size, this.gutterLeft + (this.graphwidth * (1/5)), this.gutterTop + this.halfTextHeight + this.graphheight + 2, RGraph.number_format(this, Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', 'center');
+
+ if (this.Get('chart.xmin') > 0) {
+ RGraph.Text(context,font,text_size,this.gutterLeft,this.gutterTop + this.halfTextHeight + this.graphheight + 2,RGraph.number_format(this, this.Get('chart.xmin'), units_pre, units_post),'center','center');
+ }
+ }
+
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ /**
+ * The Y axis labels
+ */
+ if (typeof(this.Get('chart.labels')) == 'object') {
+
+ var xOffset = 5;
+ var font = this.Get('chart.text.font');
+
+ // Draw the X axis labels
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ // How wide is each bar
+ var barHeight = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom ) / this.Get('chart.labels').length;
+
+ // Reset the xTickGap
+ yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / this.Get('chart.labels').length
+
+ // Draw the X tickmarks
+ var i=0;
+ for (y=this.gutterTop + (yTickGap / 2); y<=RGraph.GetHeight(this) - this.gutterBottom; y+=yTickGap) {
+ RGraph.Text(this.context, font,this.Get('chart.text.size'),this.gutterLeft - xOffset,y,String(this.Get('chart.labels')[i++]),'center','right');
+ }
+ }
+ }
+
+
+ /**
+ * This function draws the actual bars
+ */
+ RGraph.HBar.prototype.Drawbars = function ()
+ {
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+ var prevX = 0;
+ var prevY = 0;
+
+ /**
+ * Work out the max value
+ */
+ if (this.Get('chart.xmax')) {
+ this.scale = [
+ (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.2) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')),
+ (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.4) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')),
+ (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.6) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')),
+ (((this.Get('chart.xmax') - this.Get('chart.xmin')) * 0.8) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')),
+ (((this.Get('chart.xmax') - this.Get('chart.xmin')) + this.Get('chart.xmin'))).toFixed(this.Get('chart.scale.decimals'))
+ ];
+ this.max = this.scale[4];
+
+ } else {
+
+ var grouping = this.Get('chart.grouping');
+
+ for (i=0; i<this.data.length; ++i) {
+ if (typeof(this.data[i]) == 'object') {
+ var value = grouping == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;
+ } else {
+ var value = Number(Math.abs(this.data[i]));
+ }
+
+ this.max = Math.max(Math.abs(this.max), Math.abs(value));
+ }
+
+ this.scale = RGraph.getScale(this.max, this);
+
+ /**
+ * Account for chart.xmin
+ */
+ if (this.Get('chart.xmin') > 0) {
+ this.scale[0] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.2) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')));
+ this.scale[1] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.4) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')));
+ this.scale[2] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.6) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')));
+ this.scale[3] = Number((((this.scale[4] - this.Get('chart.xmin')) * 0.8) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')));
+ this.scale[4] = Number((((this.scale[4] - this.Get('chart.xmin')) * 1.0) + this.Get('chart.xmin')).toFixed(this.Get('chart.scale.decimals')));
+ }
+
+ this.max = this.scale[4];
+ }
+
+ if (this.Get('chart.scale.decimals') == null && Number(this.max) == 1) {
+ this.Set('chart.scale.decimals', 1);
+ }
+
+ /*******************************************************
+ * This is here to facilitate sequential colors
+ *******************************************************/
+ var colorIdx = 0;
+
+ /**
+ * The bars are drawn HERE
+ */
+ var graphwidth = (this.canvas.width - this.gutterLeft - this.gutterRight);
+ var halfwidth = graphwidth / 2;
+
+ for (i=0; i<this.data.length; ++i) {
+
+ // Work out the width and height
+ var width = (this.data[i] / this.max) * graphwidth;
+ var height = this.graphheight / this.data.length;
+
+ var orig_height = height;
+
+ var x = this.gutterLeft;
+ var y = this.gutterTop + (i * height);
+ var vmargin = this.Get('chart.vmargin');
+
+ // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value
+ if (width < 0) {
+ x -= width;
+ width = Math.abs(width);
+ }
+
+ /**
+ * Turn on the shadow if need be
+ */
+ if (this.Get('chart.shadow')) {
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+ }
+
+ /**
+ * Draw the bar
+ */
+ this.context.beginPath();
+ if (typeof(this.data[i]) == 'number') {
+
+ var barHeight = height - (2 * vmargin);
+ var barWidth = ((this.data[i] - this.Get('chart.xmin')) / (this.max - this.Get('chart.xmin'))) * this.graphwidth;
+ var barX = this.gutterLeft;
+
+ // Account for Y axis pos
+ if (this.Get('chart.yaxispos') == 'center') {
+ barWidth /= 2;
+ barX += halfwidth;
+ }
+
+ // Set the fill color
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+
+ // Sequential colors
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = this.Get('chart.colors')[colorIdx++];
+ }
+
+ this.context.strokeRect(barX, this.gutterTop + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight);
+ this.context.fillRect(barX, this.gutterTop + (i * height) + this.Get('chart.vmargin'), barWidth, barHeight);
+
+ this.coords.push([barX,
+ y + vmargin,
+ barWidth,
+ height - (2 * vmargin),
+ this.context.fillStyle,
+ this.data[i],
+ true]);
+
+ /**
+ * Stacked bar chart
+ */
+ } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked') {
+
+ if (this.Get('chart.yaxispos') == 'center') {
+ alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped');
+ }
+
+ var barHeight = height - (2 * vmargin);
+
+ for (j=0; j<this.data[i].length; ++j) {
+
+
+ // Set the fill/stroke colors
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[j];
+
+
+ // Sequential colors
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = this.Get('chart.colors')[colorIdx++];
+ }
+
+
+ var width = (((this.data[i][j]) / (this.max))) * this.graphwidth;
+ var totalWidth = (RGraph.array_sum(this.data[i]) / this.max) * this.graphwidth;
+
+ this.context.strokeRect(x, this.gutterTop + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
+ this.context.fillRect(x, this.gutterTop + this.Get('chart.vmargin') + (this.graphheight / this.data.length) * i, width, height - (2 * vmargin) );
+
+ /**
+ * Store the coords for tooltips
+ */
+
+ // The last property of this array is a boolean which tells you whether the value is the last or not
+ this.coords.push([x,
+ y + vmargin,
+ width,
+ height - (2 * vmargin),
+ this.context.fillStyle,
+ RGraph.array_sum(this.data[i]),
+ j == (this.data[i].length - 1)
+ ]);
+
+ x += width;
+ }
+
+ /**
+ * A grouped bar chart
+ */
+ } else if (typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped') {
+
+ for (j=0; j<this.data[i].length; ++j) {
+
+ /**
+ * Turn on the shadow if need be
+ */
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ // Set the fill/stroke colors
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[j];
+
+
+ // Sequential colors
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = this.Get('chart.colors')[colorIdx++];
+ }
+
+ var width = ((this.data[i][j] - this.Get('chart.xmin')) / (this.max - this.Get('chart.xmin'))) * (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight );
+ var individualBarHeight = (height - (2 * vmargin)) / this.data[i].length;
+
+ var startX = this.gutterLeft;
+ var startY = y + vmargin + (j * individualBarHeight);
+
+ // Account for the Y axis being in the middle
+ if (this.Get('chart.yaxispos') == 'center') {
+ width /= 2;
+ startX += halfwidth;
+ }
+
+ if (width < 0) {
+ startX += width;
+ width *= -1;
+ }
+
+ this.context.strokeRect(startX, startY, width, individualBarHeight);
+ this.context.fillRect(startX, startY, width, individualBarHeight);
+
+ this.coords.push([startX,
+ startY,
+ width,
+ individualBarHeight,
+ this.context.fillStyle,
+ this.data[i][j],
+ true]);
+ }
+ }
+
+ this.context.closePath();
+ }
+
+ this.context.fill();
+ this.context.stroke();
+
+
+
+ /**
+ * Now the bars are stroke()ed, turn off the shadow
+ */
+ RGraph.NoShadow(this);
+
+ this.RedrawBars();
+ }
+
+
+ /**
+ * This function goes over the bars after they been drawn, so that upwards shadows are underneath the bars
+ */
+ RGraph.HBar.prototype.RedrawBars = function ()
+ {
+ if (this.Get('chart.noredraw')) {
+ return;
+ }
+
+ var coords = this.coords;
+
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+ var color = this.Get('chart.text.color');
+
+ RGraph.NoShadow(this);
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ for (var i=0; i<coords.length; ++i) {
+
+ if (this.Get('chart.shadow')) {
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = coords[i][4];
+ this.context.lineWidth = 1;
+ this.context.strokeRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
+ this.context.fillRect(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ /**
+ * Draw labels "above" the bar
+ */
+ if (this.Get('chart.labels.above') && coords[i][6]) {
+
+ this.context.fillStyle = color;
+ this.context.strokeStyle = 'black';
+ RGraph.NoShadow(this);
+
+ var border = (coords[i][0] + coords[i][2] + 7 + this.context.measureText(this.Get('chart.units.pre') + this.coords[i][5] + this.Get('chart.units.post')).width) > RGraph.GetWidth(this) ? true : false;
+
+ RGraph.Text(this.context,
+ font,
+ size,
+ coords[i][0] + coords[i][2] + (border ? -5 : 5),
+ coords[i][1] + (coords[i][3] / 2),
+ RGraph.number_format(this, (this.coords[i][5]).toFixed(this.Get('chart.labels.above.decimals')), this.Get('chart.units.pre'), this.Get('chart.units.post')),
+ 'center',
+ border ? 'right' : 'left',
+ border,
+ null,
+ border ? 'rgba(255,255,255,0.9)' : null);
+ }
+ }
+ }
+
+
+ /*******************************************************
+ * This function can be used to get the appropriate bar information (if any)
+ *
+ * @param e Event object
+ * @return Appriate bar information (if any)
+ *******************************************************/
+ RGraph.HBar.prototype.getBar = function (e)
+ {
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0,len=obj.coords.length; i<len; i++) {
+
+ var mouseX = mouseCoords[0]; // In relation to the canvas
+ var mouseY = mouseCoords[1]; // In relation to the canvas
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+ var idx = i;
+
+ if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
+ return [left, top, width, height, idx];
+ }
+ }
+ }
diff --git a/schall/static/RGraph/libraries/RGraph.hprogress.js b/schall/static/RGraph/libraries/RGraph.hprogress.js
new file mode 100644
index 0000000..4e8fd59
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.hprogress.js
@@ -0,0 +1,589 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The progress bar constructor
+ *
+ * @param int id The ID of the canvas tag
+ * @param int value The indicated value of the meter.
+ * @param int max The end value (the upper most) of the meter
+ */
+ RGraph.HProgress = function (id, value, max)
+ {
+ this.id = id;
+ this.max = max;
+ this.value = value;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.canvas.__object__ = this;
+ this.type = 'hprogress';
+ this.coords = [];
+ this.isRGraph = true;
+ this.currentValue = null;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+ this.properties = {
+ 'chart.min': 0,
+ 'chart.colors': ['#0c0'],
+ 'chart.strokestyle': '#999',
+ 'chart.tickmarks': true,
+ 'chart.tickmarks.color': 'black',
+ 'chart.tickmarks.inner': false,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.numticks': 10,
+ 'chart.numticks.inner': 50,
+ 'chart.background.color': '#eee',
+ 'chart.shadow': false,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.tooltips': [],
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.background': true,
+ 'chart.zoom.thumbnail.width': 100,
+ 'chart.zoom.thumbnail.height': 100,
+ 'chart.arrows': false,
+ 'chart.margin': 0,
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.label.inner': false,
+ 'chart.adjustable': false,
+ 'chart.scale.decimals': 0,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'gutter',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': false,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.key.color.shape': 'square',
+ 'chart.labels.position': 'bottom'
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[PROGRESS] No canvas support');
+ return;
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A generic setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the poperty
+ */
+ RGraph.HProgress.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A generic getter
+ *
+ * @param string name The name of the property to get
+ */
+ RGraph.HProgress.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * Draws the progress bar
+ */
+ RGraph.HProgress.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * This is new in May 2011 and facilitates individual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Figure out the width and height
+ this.width = this.canvas.width - this.gutterLeft - this.gutterRight;
+ this.height = this.canvas.height - this.gutterTop - this.gutterBottom;
+ this.coords = [];
+
+ this.Drawbar();
+ this.DrawTickMarks();
+ this.DrawLabels();
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Alternatively, show the tooltip if requested
+ */
+ if (typeof(this.Get('chart.tooltips')) == 'function' || this.Get('chart.tooltips').length) {
+
+ // Need to register this object for redrawing
+ RGraph.Register(this);
+
+ /**
+ * Install the window onclick handler
+ */
+ var window_onclick = function () {RGraph.Redraw();}
+ window.addEventListener('click', window_onclick, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', window_onclick);
+
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ var canvas_onclick_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Redraw();
+
+ /**
+ * Was the mouse over the bar
+ */
+ if (bar) {
+
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]);
+
+ /**
+ * Show a tooltip if it's defined
+ */
+ if (text) {
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+
+ obj.context.strokeRect(bar[1], bar[2], bar[3], bar[4]);
+ obj.context.fillRect(bar[1], bar[2], bar[3], bar[4]);
+
+ obj.context.stroke();
+ obj.context.fill();
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]);
+ }
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
+
+ /**
+ * If the cursor is over a hotspot, change the cursor to a hand
+ */
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ // Is there a focused bar
+ if (bar){
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]);
+
+ if (text) {
+ canvas.style.cursor = 'pointer';
+ return;
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+
+ // Draw the key if necessary
+ if (this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Instead of using RGraph.common.adjusting.js, handle them here
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+ /**
+ * Draws the bar
+ */
+ RGraph.HProgress.prototype.Drawbar = function ()
+ {
+ // Set a shadow if requested
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ // Draw the shadow for MSIE
+ if (RGraph.isIE8() && this.Get('chart.shadow')) {
+ this.context.fillStyle = this.Get('chart.shadow.color');
+ this.context.fillRect(this.gutterLeft + this.Get('chart.shadow.offsetx'), this.gutterTop + this.Get('chart.shadow.offsety'), this.width, this.height);
+ }
+
+ // Draw the outline
+ this.context.fillStyle = this.Get('chart.background.color');
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height);
+ this.context.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height);
+
+ // Turn off any shadow
+ RGraph.NoShadow(this);
+
+ this.context.fillStyle = this.Get('chart.color');
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ var margin = this.Get('chart.margin');
+
+ // Draw the actual bar itself
+ var barWidth = Math.min(this.width, ((RGraph.array_sum(this.value) - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * this.width);
+
+ if (this.Get('chart.tickmarks.inner')) {
+
+ var spacing = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / this.Get('chart.numticks.inner');
+
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ this.context.beginPath();
+ for (var x = this.gutterLeft; x<RGraph.GetWidth(this) - this.gutterRight; x+=spacing) {
+ this.context.moveTo(x, this.gutterTop);
+ this.context.lineTo(x, this.gutterTop + 2);
+
+ this.context.moveTo(x, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(x, RGraph.GetHeight(this) - this.gutterBottom - 2);
+ }
+ this.context.stroke();
+ }
+
+ /**
+ * This bit draws the actual progress bar
+ */
+ if (typeof(this.value) == 'number') {
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+ this.context.strokeRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin);
+ this.context.fillRect(this.gutterLeft, this.gutterTop + margin, barWidth, this.height - margin - margin);
+
+ // Store the coords
+ this.coords.push([this.gutterLeft,
+ this.gutterTop + margin,
+ barWidth,
+ this.height - margin - margin]);
+
+ } else if (typeof(this.value) == 'object') {
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ var startPoint = this.gutterLeft;
+
+ for (var i=0; i<this.value.length; ++i) {
+
+ var segmentLength = (this.value[i] / RGraph.array_sum(this.value)) * barWidth;
+ this.context.fillStyle = this.Get('chart.colors')[i];
+
+ this.context.strokeRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin);
+ this.context.fillRect(startPoint, this.gutterTop + margin, segmentLength, this.height - margin - margin);
+
+
+ // Store the coords
+ this.coords.push([startPoint,
+ this.gutterTop + margin,
+ segmentLength,
+ this.height - margin - margin]);
+
+ startPoint += segmentLength;
+ }
+ }
+
+ /**
+ * Draw the arrows indicating the level if requested
+ */
+ if (this.Get('chart.arrows')) {
+ var x = this.gutterLeft + barWidth;
+ var y = this.gutterTop;
+
+ this.context.lineWidth = 1;
+ this.context.fillStyle = 'black';
+ this.context.strokeStyle = 'black';
+
+ this.context.beginPath();
+ this.context.moveTo(x, y - 3);
+ this.context.lineTo(x + 2, y - 7);
+ this.context.lineTo(x - 2, y - 7);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ this.context.beginPath();
+ this.context.moveTo(x, y + this.height + 4);
+ this.context.lineTo(x + 2, y + this.height + 9);
+ this.context.lineTo(x - 2, y + this.height + 9);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill()
+
+
+ /**
+ * Draw the "in-bar" label
+ */
+ if (this.Get('chart.label.inner')) {
+ this.context.beginPath();
+ this.context.fillStyle = 'black';
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') + 2, this.gutterLeft + barWidth + 5, RGraph.GetHeight(this) / 2, String(this.Get('chart.units.pre') + this.value + this.Get('chart.units.post')), 'center', 'left');
+ this.context.fill();
+ }
+ }
+
+ }
+
+ /**
+ * The function that draws the tick marks. Apt name...
+ */
+ RGraph.HProgress.prototype.DrawTickMarks = function ()
+ {
+ var context = this.context;
+
+ context.strokeStyle = this.Get('chart.tickmarks.color');
+
+ if (this.Get('chart.tickmarks')) {
+
+ this.context.beginPath();
+
+ // This is used by the label function below
+ this.tickInterval = this.width / this.Get('chart.numticks');
+
+ var start = this.Get('chart.tickmarks.zerostart') ? 0 : this.tickInterval;
+
+ if (this.Get('chart.labels.position') == 'top') {
+ for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) {
+ context.moveTo(i, this.gutterTop);
+ context.lineTo(i, this.gutterTop - 4);
+ }
+
+ } else {
+
+ for (var i=this.gutterLeft + start; i<=(this.width + this.gutterLeft + 0.1); i+=this.tickInterval) {
+ context.moveTo(i, this.gutterTop + this.height);
+ context.lineTo(i, this.gutterTop + this.height + 4);
+ }
+ }
+
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * The function that draws the labels
+ */
+ RGraph.HProgress.prototype.DrawLabels = function ()
+ {
+ var context = this.context;
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ var xPoints = [];
+ var yPoints = [];
+
+ for (i=0; i<this.Get('chart.numticks'); i++) {
+
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+
+ if (this.Get('chart.labels.position') == 'top') {
+ var x = this.width * (i/this.Get('chart.numticks')) + this.gutterLeft + (this.width / this.Get('chart.numticks'));
+ var y = this.gutterTop - 6;
+ var valign = 'bottom';
+ } else {
+ var x = this.width * (i/this.Get('chart.numticks')) + this.gutterLeft + (this.width / this.Get('chart.numticks'));
+ var y = this.height + this.gutterTop + 4;
+ var valign = 'top';
+ }
+
+ RGraph.Text(this.context,font,size,x,y,this.Get('chart.units.pre') + String((((this.max - this.Get('chart.min')) / this.Get('chart.numticks') ) * (i + 1) + this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals'))) + this.Get('chart.units.post'),valign,'center');
+ }
+
+ if (this.Get('chart.tickmarks.zerostart')) {
+ if (this.Get('chart.labels.position') == 'top') {
+ RGraph.Text(this.context,font,size,this.gutterLeft,this.gutterTop - 6,this.Get('chart.units.pre') + Number(this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post'),'bottom','center');
+ } else {
+ RGraph.Text(this.context,font,size,this.gutterLeft,this.canvas.height - this.gutterBottom + 5,this.Get('chart.units.pre') + Number(this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals')) + this.Get('chart.units.post'),'top','center');
+ }
+ }
+
+
+
+ // Draw the title text
+ if (this.Get('chart.title')) {
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ this.gutterTop + this.Get('chart.text.size'),
+ 0,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+ }
+ }
+
+
+ /**
+ * Returns the focused bar
+ *
+ * @param event e The event object
+ */
+ RGraph.HProgress.prototype.getBar = function (e)
+ {
+ var obj = e.target.__object__;
+ var mouseCoords = RGraph.getMouseXY(e)
+
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+ var idx = i;
+
+ if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
+ return [obj, left, top, width, height, idx];
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.led.js b/schall/static/RGraph/libraries/RGraph.led.js
new file mode 100644
index 0000000..8e7d2b7
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.led.js
@@ -0,0 +1,232 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The LED lights constructor
+ *
+ * @param object canvas The canvas object
+ * @param array data The chart data
+ */
+ RGraph.LED = function (id, text)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'led';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ /**
+ * Set the string that is to be displayed
+ */
+ this.text = text;
+
+ /**
+ * The letters and numbers
+ */
+ this.lights = {
+ 'a': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]],
+ 'b': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0]],
+ 'c': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,1],[0,1,1,0]],
+ 'd': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,0]],
+ 'e': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]],
+ 'f': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]],
+ 'g': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,0,1,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ 'h': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]],
+ 'i': [[0,1,1,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,1,1]],
+ 'j': [[0,1,1,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,0,0]],
+ 'k': [[1,0,0,1],[1,0,0,1],[1,0,1,0],[1,1,0,0],[1,0,1,0],[1,0,0,1],[1,0,0,1]],
+ 'l': [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]],
+ 'm': [[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]],
+ 'n': [[1,0,0,1],[1,1,0,1],[1,0,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1]],
+ 'o': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ 'p': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]],
+ 'q': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,1,1],[0,1,1,1]],
+ 'r': [[1,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,1,0],[1,0,1,0],[1,0,0,1],[1,0,0,1]],
+ 's': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[0,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]],
+ 't': [[1,1,1,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],
+ 'u': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ 'v': [[1,0,1],[1,0,1],[1,0,1],[1,0,1],[1,0,1],[0,1,0],[0,1,0]],
+ 'w': [[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1],[0,1,1,0]],
+ 'x': [[0,1,0,1],[0,1,0,1],[0,1,0,1],[0,0,1,0],[0,1,0,1],[0,1,0,1],[0,1,0,1]],
+ 'y': [[0,1,0,1],[0,1,0,1],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]],
+ 'z': [[1,1,1,1],[0,0,0,1],[0,0,1,0],[0,0,1,0],[0,1,0,0],[1,0,0,0],[1,1,1,1]],
+ ' ': [[],[],[],[],[], [], []],
+ '0': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ '1': [[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,1,1,1]],
+ '2': [[0,1,1,0],[1,0,0,1],[0,0,0,1],[0,0,1,0],[0,1,,0],[1,0,0,0],[1,1,1,1]],
+ '3': [[0,1,1,0],[1,0,0,1],[0,0,0,1],[0,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]],
+ '4': [[1,0,0,0],[1,0,0,0],[1,0,1,0],[1,0,1,0],[1,1,1,1],[0,0,1,0],[0,0,1,0]],
+ '5': [[1,1,1,1],[1,0,0,0],[1,0,0,0],[1,1,1,0],[0,0,0,1],[1,0,0,1],[0,1,1,0]],
+ '6': [[0,1,1,0],[1,0,0,1],[1,0,0,0],[1,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ '7': [[1,1,1,1],[0,0,0,1],[0,0,0,1],[0,0,1,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],
+ '8': [[0,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0]],
+ '9': [[0,1,1,1],[1,0,0,1],[1,0,0,1],[0,1,1,1],[0,0,0,1],[0,0,0,1],[0,0,0,1]]
+ }
+
+ // Various config type stuff
+ this.properties = {
+ 'chart.background': 'white',
+ 'chart.dark': '#eee',
+ 'chart.light': '#f66',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.width': null,
+ 'chart.height': null
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[LED] No canvas support');
+ return;
+ }
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.LED.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.LED.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * This draws the LEDs
+ */
+ RGraph.LED.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ // First clear the canvas, using the background colour
+ RGraph.Clear(this.canvas, this.Get('chart.background'));
+
+ for (var l=0; l<this.text.length; l++) {
+ this.DrawLetter(this.text.charAt(l), l);
+ }
+
+ /**
+ * Set the title attribute on the canvas
+ */
+ this.canvas.title = RGraph.rtrim(this.text);
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws a single letter
+ *
+ * @param string lights The lights to draw to draw
+ * @param int index The position of the letter
+ */
+ RGraph.LED.prototype.DrawLetter = function (letter, index)
+ {
+ var light = this.Get('chart.light');
+ var dark = this.Get('chart.dark');
+ var lights = (this.lights[letter] ? this.lights[letter] : [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]);
+ var lwidth = RGraph.GetWidth(this) / this.text.length;
+ var diameter = lwidth / 5;
+ var radius = diameter / 2;
+
+ var lheight = diameter * 7;
+ if (lheight > RGraph.GetHeight(this)) {
+ lheight = RGraph.GetHeight(this);
+ diameter = (lheight / 7);
+ radius = (diameter / 2);
+ lwidth = diameter * 5;
+ }
+
+ for (var i=0; i<7; i++) {
+ for (var j=0; j<5; j++) {
+
+ var x = (j * diameter) + (index * lwidth) + radius;
+ var y = ((i * diameter)) + radius;
+
+ // Draw a circle
+ this.context.fillStyle = (lights[i][j] ? light : dark);
+ this.context.strokeStyle = (lights[i][j] ? '#ccc' : 'rgba(0,0,0,0)');
+ this.context.beginPath();
+ this.context.arc(x, y, radius, 0, 6.28, 0);
+
+ this.context.stroke();
+ this.context.fill();
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.line.js b/schall/static/RGraph/libraries/RGraph.line.js
new file mode 100644
index 0000000..e641f87
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.line.js
@@ -0,0 +1,2217 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The line chart constructor
+ *
+ * @param object canvas The cxanvas object
+ * @param array data The chart data
+ * @param array ... Other lines to plot
+ */
+ RGraph.Line = function (id)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'line';
+ this.max = 0;
+ this.coords = [];
+ this.coords2 = [];
+ this.coordsKey = [];
+ this.hasnegativevalues = false;
+ this.isRGraph = true;
+
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config type stuff
+ this.properties = {
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
+ 'chart.background.grid': 1,
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.hsize': 25,
+ 'chart.background.grid.vsize': 25,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit': true,
+ 'chart.background.grid.autofit.align': false,
+ 'chart.background.grid.autofit.numhlines': 5,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.background.hbars': null,
+ 'chart.background.image': null,
+ 'chart.labels': null,
+ 'chart.labels.ingraph': null,
+ 'chart.labels.above': false,
+ 'chart.labels.above.size': 8,
+ 'chart.xtickgap': 20,
+ 'chart.smallxticks': 3,
+ 'chart.largexticks': 5,
+ 'chart.ytickgap': 20,
+ 'chart.smallyticks': 3,
+ 'chart.largeyticks': 5,
+ 'chart.numyticks': 10,
+ 'chart.linewidth': 1.01,
+ 'chart.colors': ['red', '#0f0', '#00f', '#f0f', '#ff0', '#0ff'],
+ 'chart.hmargin': 0,
+ 'chart.tickmarks.dot.color': 'white',
+ 'chart.tickmarks': null,
+ 'chart.tickmarks.linewidth': null,
+ 'chart.ticksize': 3,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.tickdirection': -1,
+ 'chart.yaxispoints': 5,
+ 'chart.fillstyle': null,
+ 'chart.xaxispos': 'bottom',
+ 'chart.yaxispos': 'left',
+ 'chart.xticks': null,
+ 'chart.text.size': 10,
+ 'chart.text.angle': 0,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.ymin': null,
+ 'chart.ymax': null,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': 0.5,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.title.xaxis': '',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': null,
+ 'chart.shadow': false,
+ 'chart.shadow.offsetx': 2,
+ 'chart.shadow.offsety': 2,
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.tooltips': null,
+ 'chart.tooltips.hotspot.xonly': false,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': '#999',
+ 'chart.highlight.fill': 'white',
+ 'chart.stepped': false,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': null,
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.key.interactive': false,
+ 'chart.contextmenu': null,
+ 'chart.ylabels': true,
+ 'chart.ylabels.count': 5,
+ 'chart.ylabels.inside': false,
+ 'chart.ylabels.invert': false,
+ 'chart.xlabels.inside': false,
+ 'chart.xlabels.inside.color': 'rgba(255,255,255,0.5)',
+ 'chart.noaxes': false,
+ 'chart.noyaxis': false,
+ 'chart.noxaxis': false,
+ 'chart.noendxtick': false,
+ 'chart.units.post': '',
+ 'chart.units.pre': '',
+ 'chart.scale.decimals': null,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.crosshairs': false,
+ 'chart.crosshairs.color': '#333',
+ 'chart.crosshairs.hline': true,
+ 'chart.crosshairs.vline': true,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.axesontop': false,
+ 'chart.filled': false,
+ 'chart.filled.range': false,
+ 'chart.filled.accumulative': true,
+ 'chart.variant': null,
+ 'chart.axis.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.backdrop': false,
+ 'chart.backdrop.size': 30,
+ 'chart.backdrop.alpha': 0.2,
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false,
+ 'chart.noredraw': false,
+ 'chart.outofbounds': false,
+ 'chart.chromefix': true,
+ 'chart.animation.factor': 1,
+ 'chart.animation.unfold.x': false,
+ 'chart.animation.unfold.y': true,
+ 'chart.animation.unfold.initial': 2,
+ 'chart.curvy': false,
+ 'chart.curvy.factor': 0.25,
+ 'chart.line.visible': true
+ }
+
+ /**
+ * Change null arguments to empty arrays
+ */
+ for (var i=1; i<arguments.length; ++i) {
+ if (typeof(arguments[i]) == 'null' || !arguments[i]) {
+ arguments[i] = [];
+ }
+ }
+
+
+ /**
+ * Store the original data. Thiss also allows for giving arguments as one big array.
+ */
+ this.original_data = [];
+
+ for (var i=1; i<arguments.length; ++i) {
+ if (arguments[1] && typeof(arguments[1]) == 'object' && arguments[1][0] && typeof(arguments[1][0]) == 'object' && arguments[1][0].length) {
+
+ var tmp = [];
+
+ for (var i=0; i<arguments[1].length; ++i) {
+ tmp[i] = RGraph.array_clone(arguments[1][i]);
+ }
+
+ for (var j=0; j<tmp.length; ++j) {
+ this.original_data[j] = RGraph.array_clone(tmp[j]);
+ }
+
+ } else {
+ this.original_data[i - 1] = RGraph.array_clone(arguments[i]);
+ }
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[LINE] Fatal error: no canvas support');
+ return;
+ }
+
+ /**
+ * Store the data here as one big array
+ */
+ this.data_arr = [];
+
+ for (var i=1; i<arguments.length; ++i) {
+ for (var j=0; j<arguments[i].length; ++j) {
+ this.data_arr.push(arguments[i][j]);
+ }
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getPoint;
+ }
+
+
+ /**
+ * An all encompassing accessor
+ *
+ * @param string name The name of the property
+ * @param mixed value The value of the property
+ */
+ RGraph.Line.prototype.Set = function (name, value)
+ {
+ // Consolidate the tooltips
+ if (name == 'chart.tooltips') {
+
+ var tooltips = [];
+
+ for (var i=1; i<arguments.length; i++) {
+ if (typeof(arguments[i]) == 'object' && arguments[i][0]) {
+ for (var j=0; j<arguments[i].length; j++) {
+ tooltips.push(arguments[i][j]);
+ }
+
+ } else if (typeof(arguments[i]) == 'function') {
+ tooltips = arguments[i];
+
+ } else {
+ tooltips.push(arguments[i]);
+ }
+ }
+
+ // Because "value" is used further down at the end of this function, set it to the expanded array os tooltips
+ value = tooltips;
+ }
+
+ /**
+ * Reverse the tickmarks to make them correspond to the right line
+ */
+ if (name == 'chart.tickmarks' && typeof(value) == 'object' && value) {
+ value = RGraph.array_reverse(value);
+ }
+
+ /**
+ * Inverted Y axis should show the bottom end of the scale
+ */
+ if (name == 'chart.ylabels.invert' && value && this.Get('chart.ymin') == null) {
+ this.Set('chart.ymin', 0);
+ }
+
+ /**
+ * If (buggy) Chrome and the linewidth is 1, change it to 1.01
+ */
+ if (name == 'chart.linewidth' && navigator.userAgent.match(/Chrome/)) {
+ if (value == 1) {
+ value = 1.01;
+
+ } else if (RGraph.is_array(value)) {
+ for (var i=0; i<value.length; ++i) {
+ if (typeof(value[i]) == 'number' && value[i] == 1) {
+ value[i] = 1.01;
+ }
+ }
+ }
+ }
+
+ /**
+ * Check for xaxispos
+ */
+ if (name == 'chart.xaxispos' ) {
+ if (value != 'bottom' && value != 'center' && value != 'top') {
+ alert('[LINE] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
+ value = 'center';
+ }
+ }
+
+ /**
+ * Check chart.curvy.factor - should be 0 - 0.5
+ */
+ if (name == 'chart.curvy.factor' && (value < 0 || value > 0.5)) {
+ alert('[LINE] chart.curvy.factor should be between 0 and 0.5 - setting it to 0.25');
+ value = 0.25;
+ }
+
+ this.properties[name] = value;
+ }
+
+
+ /**
+ * An all encompassing accessor
+ *
+ * @param string name The name of the property
+ */
+ RGraph.Line.prototype.Get = function (name)
+ {
+ return this.properties[name];
+ }
+
+
+ /**
+ * The function you call to draw the line chart
+ *
+ * @param bool An optional bool used internally to ditinguish whether the
+ * line chart is being called by the bar chart
+ */
+ RGraph.Line.prototype.Draw = function ()
+ {
+ // MUST be the FIRST thing done - Hand over drawing to the bar chart if it's a combo back to the Bar chart
+ if (this.__bar__ && !arguments[0]) {
+ this.__bar__.Draw();
+ return;
+ }
+
+ // MUST be the SECOND thing done!
+ if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
+ RGraph.DrawBackgroundImage(this);
+ return;
+ }
+
+ if (this.__bar__ && this.Get('chart.tooltips.highlight')) {
+ //this.Set('chart.tooltips.highlight', false);
+ }
+
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+
+ /**
+ * Check for Chrome 6 and shadow
+ *
+ * TODO Remove once it's been fixed (for a while)
+ * SEARCH TAGS: CHROME FIX SHADOW BUG
+ */
+ if ( this.Get('chart.shadow')
+ && navigator.userAgent.match(/Chrome/)
+ && this.Get('chart.linewidth') <= 1
+ && this.Get('chart.chromefix')
+ && this.Get('chart.shadow.blur') > 0) {
+ alert('[RGRAPH WARNING] Chrome has a shadow bug, meaning you should increase the linewidth to at least 1.01');
+ }
+
+
+ // Reset the data back to that which was initially supplied
+ this.data = RGraph.array_clone(this.original_data);
+
+
+ // Reset the max value
+ this.max = 0;
+
+ /**
+ * Reverse the datasets so that the data and the labels tally
+ * COMMENTED OUT 15TH AUGUST 2011
+ */
+ //this.data = RGraph.array_reverse(this.data);
+
+ if (this.Get('chart.filled') && !this.Get('chart.filled.range') && this.data.length > 1 && this.Get('chart.filled.accumulative')) {
+
+ var accumulation = [];
+
+ for (var set=0; set<this.data.length; ++set) {
+ for (var point=0; point<this.data[set].length; ++point) {
+ this.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) + this.data[set][point];
+ accumulation[point] = this.data[set][point];
+ }
+ }
+ }
+
+ /**
+ * Get the maximum Y scale value
+ */
+ if (this.Get('chart.ymax')) {
+
+ this.max = this.Get('chart.ymax');
+ this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+
+ this.scale = [
+ ( ((this.max - this.min) * (1/5)) + this.min).toFixed(this.Get('chart.scale.decimals')),
+ ( ((this.max - this.min) * (2/5)) + this.min).toFixed(this.Get('chart.scale.decimals')),
+ ( ((this.max - this.min) * (3/5)) + this.min).toFixed(this.Get('chart.scale.decimals')),
+ ( ((this.max - this.min) * (4/5)) + this.min).toFixed(this.Get('chart.scale.decimals')),
+ this.max.toFixed(this.Get('chart.scale.decimals'))
+ ];
+
+ // Check for negative values
+ if (!this.Get('chart.outofbounds')) {
+ for (dataset=0; dataset<this.data.length; ++dataset) {
+ for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
+
+ // Check for negative values
+ this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
+ }
+ }
+ }
+
+ } else {
+
+ this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+
+ // Work out the max Y value
+ for (dataset=0; dataset<this.data.length; ++dataset) {
+ for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
+
+ this.max = Math.max(this.max, this.data[dataset][datapoint] ? Math.abs(parseFloat(this.data[dataset][datapoint])) : 0);
+
+ // Check for negative values
+ if (!this.Get('chart.outofbounds')) {
+ this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
+ }
+ }
+ }
+
+ // 20th April 2009 - moved out of the above loop
+ this.scale = RGraph.getScale(Math.abs(parseFloat(this.max)), this);
+ this.max = this.scale[4] ? this.scale[4] : 0;
+
+ if (this.Get('chart.ymin')) {
+ this.scale[0] = ((this.max - this.Get('chart.ymin')) * (1/5)) + this.Get('chart.ymin');
+ this.scale[1] = ((this.max - this.Get('chart.ymin')) * (2/5)) + this.Get('chart.ymin');
+ this.scale[2] = ((this.max - this.Get('chart.ymin')) * (3/5)) + this.Get('chart.ymin');
+ this.scale[3] = ((this.max - this.Get('chart.ymin')) * (4/5)) + this.Get('chart.ymin');
+ this.scale[4] = ((this.max - this.Get('chart.ymin')) * (5/5)) + this.Get('chart.ymin');
+ }
+
+ if (typeof(this.Get('chart.scale.decimals')) == 'number') {
+ this.scale[0] = Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'));
+ this.scale[1] = Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'));
+ this.scale[2] = Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'));
+ this.scale[3] = Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'));
+ this.scale[4] = Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'));
+ }
+ }
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Reset the coords array otherwise it will keep growing
+ */
+ this.coords = [];
+
+ /**
+ * Work out a few things. They need to be here because they depend on things you can change before you
+ * call Draw() but after you instantiate the object
+ */
+ this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom;
+ this.halfgrapharea = this.grapharea / 2;
+ this.halfTextHeight = this.Get('chart.text.size') / 2;
+
+ // Check the combination of the X axis position and if there any negative values
+ //
+ // 19th Dec 2010 - removed for Opera since it can be reported incorrectly whn there
+ // are multiple graphs on the page
+ if (this.Get('chart.xaxispos') == 'bottom' && this.hasnegativevalues && navigator.userAgent.indexOf('Opera') == -1) {
+ alert('[LINE] You have negative values and the X axis is at the bottom. This is not good...');
+ }
+
+ if (this.Get('chart.variant') == '3d') {
+ RGraph.Draw3DAxes(this);
+ }
+
+ // Progressively Draw the chart
+ RGraph.background.Draw(this);
+
+ /**
+ * Draw any horizontal bars that have been defined
+ */
+ if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0) {
+ RGraph.DrawBars(this);
+ }
+
+ if (this.Get('chart.axesontop') == false) {
+ this.DrawAxes();
+ }
+
+ /**
+ * Handle the appropriate shadow color. This now facilitates an array of differing
+ * shadow colors
+ */
+ var shadowColor = this.Get('chart.shadow.color');
+
+ //if (typeof(shadowColor) == 'object') {
+ // shadowColor = RGraph.array_reverse(RGraph.array_clone(this.Get('chart.shadow.color')));
+ //}
+
+
+ for (var i=0, j=0; i<this.data.length; i++, j++) {
+
+ this.context.beginPath();
+
+ /**
+ * Turn on the shadow if required
+ */
+ if (this.Get('chart.shadow') && !this.Get('chart.filled')) {
+
+ /**
+ * Accommodate an array of shadow colors as well as a single string
+ */
+ if (typeof(shadowColor) == 'object' && shadowColor[i - 1]) {
+ this.context.shadowColor = shadowColor[i];
+ } else if (typeof(shadowColor) == 'object') {
+ this.context.shadowColor = shadowColor[0];
+ } else if (typeof(shadowColor) == 'string') {
+ this.context.shadowColor = shadowColor;
+ }
+
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+
+ } else if (this.Get('chart.filled') && this.Get('chart.shadow')) {
+ alert('[LINE] Shadows are not permitted when the line is filled');
+ }
+
+ /**
+ * Draw the line
+ */
+
+ if (this.Get('chart.fillstyle')) {
+ if (typeof(this.Get('chart.fillstyle')) == 'object' && this.Get('chart.fillstyle')[j]) {
+ var fill = this.Get('chart.fillstyle')[j];
+
+ } else if (typeof(this.Get('chart.fillstyle')) == 'string') {
+ var fill = this.Get('chart.fillstyle');
+
+ } else {
+ alert('[LINE] Warning: chart.fillstyle must be either a string or an array with the same number of elements as you have sets of data');
+ }
+ } else if (this.Get('chart.filled')) {
+ var fill = this.Get('chart.colors')[j];
+
+ } else {
+ var fill = null;
+ }
+
+ /**
+ * Figure out the tickmark to use
+ */
+ if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'object') {
+ var tickmarks = this.Get('chart.tickmarks')[i];
+ } else if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'string') {
+ var tickmarks = this.Get('chart.tickmarks');
+ } else if (this.Get('chart.tickmarks') && typeof(this.Get('chart.tickmarks')) == 'function') {
+ var tickmarks = this.Get('chart.tickmarks');
+ } else {
+ var tickmarks = null;
+ }
+
+
+ this.DrawLine(this.data[i],
+ this.Get('chart.colors')[j],
+ fill,
+ this.GetLineWidth(j),
+ tickmarks,
+ i);
+
+ this.context.stroke();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * If tooltips are defined, handle them
+ */
+ if (this.Get('chart.tooltips') && (this.Get('chart.tooltips').length || typeof(this.Get('chart.tooltips')) == 'function')) {
+
+ // Need to register this object for redrawing
+ if (this.Get('chart.tooltips.highlight')) {
+ RGraph.Register(this);
+ }
+
+ RGraph.InstallLineTooltipEventListeners(this);
+
+ /**
+ * If there's a bar, install the event listeners
+ */
+ if (this.__bar__) {
+ RGraph.InstallBarTooltipEventListeners(this.__bar__);
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * If the axes have been requested to be on top, do that
+ */
+ if (this.Get('chart.axesontop')) {
+ this.DrawAxes();
+ }
+
+ /**
+ * Draw the labels
+ */
+ this.DrawLabels();
+
+ /**
+ * Draw the range if necessary
+ */
+ this.DrawRange();
+
+ // Draw a key if necessary
+ if (this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+ /**
+ * Draw " above" labels if enabled
+ */
+ if (this.Get('chart.labels.above')) {
+ this.DrawAboveLabels();
+ }
+
+ /**
+ * Draw the "in graph" labels
+ */
+ RGraph.DrawInGraphLabels(this);
+
+ /**
+ * Draw crosschairs
+ */
+ RGraph.DrawCrosshairs(this);
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * Redraw the lines if a filled range is on the cards
+ */
+ if (this.Get('chart.filled') && this.Get('chart.filled.range') && this.data.length == 2) {
+
+ this.context.beginPath();
+ var len = this.coords.length / 2;
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.strokeStyle = this.Get('chart.colors')[0];
+
+ for (var i=0; i<len; ++i) {
+ if (i == 0) {
+ this.context.moveTo(this.coords[i][0], this.coords[i][1]);
+ } else {
+ this.context.lineTo(this.coords[i][0], this.coords[i][1]);
+ }
+ }
+
+ this.context.stroke();
+
+
+ this.context.beginPath();
+
+ if (this.Get('chart.colors')[1]) {
+ this.context.strokeStyle = this.Get('chart.colors')[1];
+ }
+
+ for (var i=this.coords.length - 1; i>=len; --i) {
+ if (i == (this.coords.length - 1) ) {
+ this.context.moveTo(this.coords[i][0], this.coords[i][1]);
+ } else {
+ this.context.lineTo(this.coords[i][0], this.coords[i][1]);
+ }
+ }
+
+ this.context.stroke();
+ } else if (this.Get('chart.filled') && this.Get('chart.filled.range')) {
+ alert('[LINE] You must have only two sets of data for a filled range chart');
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+ /**
+ * This function enables the zoom in area mode
+ */
+ if (this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ZoomArea(this);
+ }
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * This function enables adjustments
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the axes
+ */
+ RGraph.Line.prototype.DrawAxes = function ()
+ {
+ // Don't draw the axes?
+ if (this.Get('chart.noaxes')) {
+ return;
+ }
+
+ // Turn any shadow off
+ RGraph.NoShadow(this);
+
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.axis.color');
+ this.context.beginPath();
+
+ // Draw the X axis
+ if (this.Get('chart.noxaxis') == false) {
+ if (this.Get('chart.xaxispos') == 'center') {
+ this.context.moveTo(this.gutterLeft, (this.grapharea / 2) + this.gutterTop);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, (this.grapharea / 2) + this.gutterTop);
+ } else if (this.Get('chart.xaxispos') == 'top') {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop);
+ } else {
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+
+ // Draw the Y axis
+ if (this.Get('chart.noyaxis') == false) {
+ if (this.Get('chart.yaxispos') == 'left') {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom );
+ } else {
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+
+ /**
+ * Draw the X tickmarks
+ */
+ if (this.Get('chart.noxaxis') == false) {
+
+ if (this.data[0].length > 0) {
+ var xTickInterval = (this.canvas.width - this.gutterLeft - this.gutterRight) / (this.Get('chart.xticks') ? this.Get('chart.xticks') : (this.data[0].length - 1));
+ }
+
+ if (!xTickInterval || xTickInterval <= 0) {
+ xTickInterval = (this.canvas.width - this.gutterLeft - this.gutterRight) / (this.Get('chart.labels') && this.Get('chart.labels').length ? this.Get('chart.labels').length - 1 : 10);
+ }
+
+ for (x=this.gutterLeft + (this.Get('chart.yaxispos') == 'left' ? xTickInterval : 0); x<=(this.canvas.width - this.gutterRight + 1 ); x+=xTickInterval) {
+
+ if (this.Get('chart.yaxispos') == 'right' && x >= (this.canvas.width - this.gutterRight - 1) ) {
+ break;
+ }
+
+ // If the last tick is not desired...
+ if (this.Get('chart.noendxtick')) {
+ if (this.Get('chart.yaxispos') == 'left' && x >= (this.canvas.width - this.gutterRight)) {
+ break;
+ } else if (this.Get('chart.yaxispos') == 'right' && x == this.gutterLeft) {
+ continue;
+ }
+ }
+
+ var yStart = this.Get('chart.xaxispos') == 'center' ? (this.gutterTop + (this.grapharea / 2)) - 3 : RGraph.GetHeight(this) - this.gutterBottom;
+ var yEnd = this.Get('chart.xaxispos') == 'center' ? yStart + 6 : RGraph.GetHeight(this) - this.gutterBottom - (x % 60 == 0 ? this.Get('chart.largexticks') * this.Get('chart.tickdirection') : this.Get('chart.smallxticks') * this.Get('chart.tickdirection'));
+
+ if (this.Get('chart.xaxispos') == 'top') {
+ yStart = this.gutterTop - 3;
+ yEnd = this.gutterTop;
+ }
+
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+ }
+
+ // Draw an extra tickmark if there is no X axis, but there IS a Y axis
+ } else if (this.Get('chart.noyaxis') == false) {
+ if (this.Get('chart.yaxispos') == 'left') {
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft - this.Get('chart.smallyticks'), RGraph.GetHeight(this) - this.gutterBottom);
+ } else {
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks'), RGraph.GetHeight(this) - this.gutterBottom);
+ }
+ }
+
+ /**
+ * Draw the Y tickmarks
+ */
+ var numyticks = this.Get('chart.numyticks');
+
+ if (this.Get('chart.noyaxis') == false) {
+ var counter = 0;
+ var adjustment = 0;
+
+ if (this.Get('chart.yaxispos') == 'right') {
+ adjustment = (RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight);
+ }
+
+ // X axis at the center
+ if (this.Get('chart.xaxispos') == 'center') {
+ var interval = (this.grapharea / numyticks);
+ var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks'));
+
+ // Draw the upper halves Y tick marks
+ for (y=this.gutterTop; y < (this.grapharea / 2) + this.gutterTop; y+=interval) {
+ this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y);
+ this.context.lineTo(lineto, y);
+ }
+
+ // Draw the lower halves Y tick marks
+ for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) {
+ this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y);
+ this.context.lineTo(lineto, y);
+ }
+
+ // X axis at the top
+ } else if (this.Get('chart.xaxispos') == 'top') {
+ var interval = (this.grapharea / numyticks);
+ var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft : RGraph.GetWidth(this) - this.gutterRight + this.Get('chart.smallyticks'));
+
+ // Draw the Y tick marks
+ for (y=this.gutterTop + interval; y <=this.grapharea + this.gutterTop; y+=interval) {
+ this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), y);
+ this.context.lineTo(lineto, y);
+ }
+
+ // If there's no X axis draw an extra tick
+ if (this.Get('chart.noxaxis')) {
+ this.context.moveTo((this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : RGraph.GetWidth(this) - this.gutterRight), this.gutterTop);
+ this.context.lineTo(lineto, this.gutterTop);
+ }
+
+ // X axis at the bottom
+ } else {
+ var lineto = (this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - this.Get('chart.smallyticks') : this.canvas.width - this.gutterRight + this.Get('chart.smallyticks'));
+
+ for (y=this.gutterTop; y < (this.canvas.height - this.gutterBottom) && counter < numyticks; y+=( (this.canvas.height - this.gutterTop - this.gutterBottom) / numyticks) ) {
+
+ this.context.moveTo(this.gutterLeft + adjustment, y);
+ this.context.lineTo(lineto, y);
+
+ var counter = counter +1;
+ }
+ }
+
+ // Draw an extra X tickmark
+ } else if (this.Get('chart.noxaxis') == false) {
+
+ if (this.Get('chart.yaxispos') == 'left') {
+ this.context.moveTo(this.gutterLeft, this.Get('chart.xaxispos') == 'top' ? this.gutterTop : RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft, this.Get('chart.xaxispos') == 'top' ? this.gutterTop - this.Get('chart.smallxticks') : RGraph.GetHeight(this) - this.gutterBottom + this.Get('chart.smallxticks'));
+ } else {
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom + this.Get('chart.smallxticks'));
+ }
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draw the text labels for the axes
+ */
+ RGraph.Line.prototype.DrawLabels = function ()
+ {
+ this.context.strokeStyle = 'black';
+ this.context.fillStyle = this.Get('chart.text.color');
+ this.context.lineWidth = 1;
+
+ // Turn off any shadow
+ RGraph.NoShadow(this);
+
+ // This needs to be here
+ var font = this.Get('chart.text.font');
+ var text_size = this.Get('chart.text.size');
+ var context = this.context;
+ var canvas = this.canvas;
+
+ // Draw the Y axis labels
+ if (this.Get('chart.ylabels') && this.Get('chart.ylabels.specific') == null) {
+
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var xpos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
+ var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
+
+ var numYLabels = this.Get('chart.ylabels.count');
+ var bounding = false;
+ var bgcolor = this.Get('chart.ylabels.inside') ? this.Get('chart.ylabels.inside.color') : null;
+
+
+ /**
+ * If the Y labels are inside the Y axis, invert the alignment
+ */
+ if (this.Get('chart.ylabels.inside') == true && align == 'left') {
+ xpos -= 10;
+ align = 'right';
+ bounding = true;
+
+
+ } else if (this.Get('chart.ylabels.inside') == true && align == 'right') {
+ xpos += 10;
+ align = 'left';
+ bounding = true;
+ }
+
+
+
+ if (this.Get('chart.xaxispos') == 'center') {
+ var half = this.grapharea / 2;
+
+ if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
+ // Draw the upper halves labels
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (0/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, bounding, null, bgcolor);
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (1/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (3/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (2/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (4/5) * half ) + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ // Draw the lower halves labels
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (6/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (8/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (7/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (9/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + ( (10/5) * half ) + this.halfTextHeight, '-' + RGraph.number_format(this, (this.scale[4] == '1.0' ? '1.0' : this.scale[4]), units_pre, units_post), null, align, bounding, null, bgcolor);
+
+ } else if (numYLabels == 10) {
+
+ // 10 Y labels
+ var interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+ // This draws the upper halves labels
+ RGraph.Text(context,font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((i/20) * (this.grapharea) ), RGraph.number_format(this, ((this.scale[4] / numYLabels) * (numYLabels - i)).toFixed((this.Get('chart.scale.decimals'))),units_pre, units_post), null, align, bounding, null, bgcolor);
+
+ // And this draws the lower halves labels
+ RGraph.Text(context, font, text_size, xpos,
+
+ this.gutterTop + this.halfTextHeight + ((i/20) * this.grapharea) + (this.grapharea / 2) + (this.grapharea / 20),
+
+ '-' + RGraph.number_format(this, (this.scale[4] - ((this.scale[4] / numYLabels) * (numYLabels - i - 1))).toFixed((this.Get('chart.scale.decimals'))),units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ } else {
+ alert('[LINE SCALE] The number of Y labels must be 1/3/5/10');
+ }
+
+ // Draw the lower limit if chart.ymin is specified
+ if (typeof(this.Get('chart.ymin')) == 'number') {
+ RGraph.Text(context, font, text_size, xpos, RGraph.GetHeight(this) / 2, RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post), 'center', align, bounding, null, bgcolor);
+ }
+
+ // No X axis - so draw 0
+ if (this.Get('chart.noxaxis') == true) {
+ RGraph.Text(context,font,text_size,xpos,this.gutterTop + ( (5/5) * half ) + this.halfTextHeight,this.Get('chart.units.pre') + '0' + this.Get('chart.units.post'),null, align, bounding, null, bgcolor);
+ }
+
+ // X axis at the top
+ } else if (this.Get('chart.xaxispos') == 'top') {
+
+ var scale = RGraph.array_reverse(this.scale);
+
+ /**
+ * Accommodate reversing the Y labels
+ */
+ if (this.Get('chart.ylabels.invert')) {
+
+ scale = RGraph.array_reverse(scale);
+
+ this.context.translate(0, this.grapharea * -0.2);
+ if (typeof(this.Get('chart.ymin')) == null) {
+ this.Set('chart.ymin', 0);
+ }
+ }
+
+ if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((1/5) * (this.grapharea ) ), '-' + RGraph.number_format(this, scale[4], units_pre, units_post), null, align, bounding, null, bgcolor);
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((4/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[1], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((2/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[3], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((3/5) * (this.grapharea ) ), '-' + RGraph.number_format(this, scale[2], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((5/5) * (this.grapharea) ), '-' + RGraph.number_format(this, scale[0], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ } else if (numYLabels == 10) {
+
+ // 10 Y labels
+ var interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+
+ RGraph.Text(context,font,text_size,xpos,(2 * interval) + this.gutterTop + this.halfTextHeight + ((i/10) * (this.grapharea) ),'-' + RGraph.number_format(this,(scale[0] - (((scale[0] - this.min) / numYLabels) * (numYLabels - i - 1))).toFixed((this.Get('chart.scale.decimals'))),units_pre,units_post),null,align,bounding,null,bgcolor);
+ }
+
+ } else {
+ alert('[LINE SCALE] The number of Y labels must be 1/3/5/10');
+ }
+
+
+ /**
+ * Accommodate translating back after reversing the labels
+ */
+ if (this.Get('chart.ylabels.invert')) {
+ this.context.translate(0, 0 - (this.grapharea * -0.2));
+ }
+
+ // Draw the lower limit if chart.ymin is specified
+ if (typeof(this.Get('chart.ymin')) == 'number') {
+ RGraph.Text(context,font,text_size,xpos,this.Get('chart.ylabels.invert') ? this.canvas.height - this.gutterBottom : this.gutterTop,'-' + RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center',align,bounding,null,bgcolor);
+ }
+
+ } else {
+
+ /**
+ * Accommodate reversing the Y labels
+ */
+ if (this.Get('chart.ylabels.invert')) {
+ this.scale = RGraph.array_reverse(this.scale);
+ this.context.translate(0, this.grapharea * 0.2);
+ if (typeof(this.Get('chart.ymin')) == null) {
+ this.Set('chart.ymin', 0);
+ }
+ }
+
+ if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((0/5) * (this.grapharea ) ), RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, bounding, null, bgcolor);
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((3/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((1/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((2/5) * (this.grapharea ) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, bounding, null, bgcolor);
+ RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight + ((4/5) * (this.grapharea) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, bounding, null, bgcolor);
+ }
+
+ } else if (numYLabels == 10) {
+
+ // 10 Y labels
+ var interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context,font,text_size,xpos,this.gutterTop + this.halfTextHeight + ((i/10) * (this.grapharea) ),RGraph.number_format(this,((((this.scale[4] - this.min) / numYLabels) * (numYLabels - i)) + this.min).toFixed((this.Get('chart.scale.decimals'))),units_pre,units_post),null,align,bounding,null,bgcolor);
+ }
+
+ } else {
+ alert('[LINE SCALE] The number of Y labels must be 1/3/5/10');
+ }
+
+
+ /**
+ * Accommodate translating back after reversing the labels
+ */
+ if (this.Get('chart.ylabels.invert')) {
+ this.context.translate(0, 0 - (this.grapharea * 0.2));
+ }
+
+ // Draw the lower limit if chart.ymin is specified
+ if (typeof(this.Get('chart.ymin')) == 'number') {
+ RGraph.Text(context,font,text_size,xpos,this.Get('chart.ylabels.invert') ? this.gutterTop : RGraph.GetHeight(this) - this.gutterBottom,RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center',align,bounding,null,bgcolor);
+ }
+ }
+
+ // No X axis - so draw 0 - but not if the X axis is in the center
+ if ( this.Get('chart.noxaxis') == true
+ && this.Get('chart.ymin') == null
+ && this.Get('chart.xaxispos') != 'center'
+ ) {
+
+ RGraph.Text(context,font,text_size,xpos,this.Get('chart.xaxispos') == 'top' ? this.gutterTop + this.halfTextHeight: (this.canvas.height - this.gutterBottom + this.halfTextHeight),this.Get('chart.units.pre') + '0' + this.Get('chart.units.post'),null, align, bounding, null, bgcolor);
+ }
+
+ } else if (this.Get('chart.ylabels') && typeof(this.Get('chart.ylabels.specific')) == 'object') {
+
+ // A few things
+ var gap = this.grapharea / this.Get('chart.ylabels.specific').length;
+ var halign = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';
+ var bounding = false;
+ var bgcolor = null;
+ var ymin = this.Get('chart.ymin') != null && this.Get('chart.ymin');
+
+ // Figure out the X coord based on the position of the axis
+ if (this.Get('chart.yaxispos') == 'left') {
+ var x = this.gutterLeft - 5;
+
+ if (this.Get('chart.ylabels.inside')) {
+ x += 10;
+ halign = 'left';
+ bounding = true;
+ bgcolor = 'rgba(255,255,255,0.5)';
+ }
+
+ } else if (this.Get('chart.yaxispos') == 'right') {
+ var x = this.canvas.width - this.gutterRight + 5;
+
+ if (this.Get('chart.ylabels.inside')) {
+ x -= 10;
+ halign = 'right';
+ bounding = true;
+ bgcolor = 'rgba(255,255,255,0.5)';
+ }
+ }
+
+
+ // Draw the labels
+ if (this.Get('chart.xaxispos') == 'center') {
+
+ // Draw the top halfs labels
+ for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) {
+ var y = this.gutterTop + (this.grapharea / ((this.Get('chart.ylabels.specific').length ) * 2) * i);
+
+ if (ymin && ymin > 0) {
+ var y = ((this.grapharea / 2) / (this.Get('chart.ylabels.specific').length - (ymin ? 1 : 0)) ) * i;
+ y += this.gutterTop;
+ }
+
+ RGraph.Text(context, font, text_size,x,y,String(this.Get('chart.ylabels.specific')[i]), 'center', halign, bounding, 0, bgcolor);
+ }
+
+ // Now reverse the labels and draw the bottom half
+ var reversed_labels = RGraph.array_reverse(this.Get('chart.ylabels.specific'));
+
+ // Draw the bottom halfs labels
+ for (var i=0; i<reversed_labels.length; ++i) {
+ var y = (this.grapharea / 2) + this.gutterTop + ((this.grapharea / (reversed_labels.length * 2) ) * (i + 1));
+
+ if (ymin && ymin > 0) {
+ var y = ((this.grapharea / 2) / (reversed_labels.length - (ymin ? 1 : 0)) ) * i;
+ y += this.gutterTop;
+ y += (this.grapharea / 2);
+ }
+
+ RGraph.Text(context, font, text_size,x,y,String(reversed_labels[i]), 'center', halign, bounding, 0, bgcolor);
+ }
+
+ } else if (this.Get('chart.xaxispos') == 'top') {
+
+ // Reverse the labels and draw
+ var reversed_labels = RGraph.array_reverse(this.Get('chart.ylabels.specific'));
+
+ // Draw the bottom halfs labels
+ for (var i=0; i<reversed_labels.length; ++i) {
+
+ var y = ((this.grapharea / (reversed_labels.length - (ymin ? 1 : 0)) ) * (i + (ymin ? 0 : 1)));
+ y = y + this.gutterTop;
+
+ RGraph.Text(context, font, text_size,x,y,String(reversed_labels[i]), 'center', halign, bounding, 0, bgcolor);
+ }
+
+ } else {
+ for (var i=0; i<this.Get('chart.ylabels.specific').length; ++i) {
+ var y = this.gutterTop + ((this.grapharea / (this.Get('chart.ylabels.specific').length - (ymin ? 1 : 0) )) * i);
+ RGraph.Text(context, font, text_size,x,y,String(this.Get('chart.ylabels.specific')[i]), 'center', halign, bounding, 0, bgcolor);
+ }
+ }
+ }
+
+ // Draw the X axis labels
+ if (this.Get('chart.labels') && this.Get('chart.labels').length > 0) {
+
+
+ var yOffset = 13;
+ var bordered = false;
+ var bgcolor = null;
+
+ if (this.Get('chart.xlabels.inside')) {
+ yOffset = -5;
+ bordered = true;
+ bgcolor = this.Get('chart.xlabels.inside.color');
+ }
+
+ /**
+ * Text angle
+ */
+ var angle = 0;
+ var valign = null;
+ var halign = 'center';
+
+ if (typeof(this.Get('chart.text.angle')) == 'number' && this.Get('chart.text.angle') > 0) {
+ angle = -1 * this.Get('chart.text.angle');
+ valign = 'center';
+ halign = 'right';
+ yOffset = 10;
+
+ if (this.Get('chart.xaxispos') == 'top') {
+ yOffset = 10;
+ }
+ }
+
+ this.context.fillStyle = this.Get('chart.text.color');
+ var numLabels = this.Get('chart.labels').length;
+
+ for (i=0; i<numLabels; ++i) {
+
+ // Changed 8th Nov 2010 to be not reliant on the coords
+ //if (this.Get('chart.labels')[i] && this.coords && this.coords[i] && this.coords[i][0]) {
+ if (this.Get('chart.labels')[i]) {
+
+ var labelX = ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - (2 * this.Get('chart.hmargin'))) / (numLabels - 1) ) * i;
+ labelX += this.gutterLeft + this.Get('chart.hmargin');
+
+ /**
+ * Account for an unrelated number of labels
+ */
+ if (this.Get('chart.labels').length != this.data[0].length) {
+ labelX = this.gutterLeft + this.Get('chart.hmargin') + ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - (2 * this.Get('chart.hmargin'))) * (i / (this.Get('chart.labels').length - 1)));
+ }
+
+ // This accounts for there only being one point on the chart
+ if (!labelX) {
+ labelX = this.gutterLeft + this.Get('chart.hmargin');
+ }
+
+ if (this.Get('chart.xaxispos') == 'top' && this.Get('chart.text.angle') > 0) {
+ halign = 'left';
+ }
+
+ RGraph.Text(context,
+ font,
+ text_size,
+ labelX,
+ (this.Get('chart.xaxispos') == 'top') ? this.gutterTop - yOffset - (this.Get('chart.xlabels.inside') ? -22 : 0) : (RGraph.GetHeight(this) - this.gutterBottom) + yOffset,
+ String(this.Get('chart.labels')[i]),
+ valign,
+ halign,
+ bordered,
+ angle,
+ bgcolor);
+ }
+ }
+
+ }
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+
+ /**
+ * Draws the line
+ */
+ RGraph.Line.prototype.DrawLine = function (lineData, color, fill, linewidth, tickmarks, index)
+ {
+ // This facilitates the Rise animation (the Y value only)
+ if (this.Get('chart.animation.unfold.y') && this.Get('chart.animation.factor') != 1) {
+ for (var i=0; i<lineData.length; ++i) {
+ lineData[i] *= this.Get('chart.animation.factor');
+ }
+ }
+
+ var penUp = false;
+ var yPos = null;
+ var xPos = 0;
+ this.context.lineWidth = 1;
+ var lineCoords = [];
+
+ /**
+ * Get the previous line data
+ */
+ if (index > 0) {
+ var prevLineCoords = this.coords2[index - 1];
+ }
+
+ // Work out the X interval
+ var xInterval = (this.canvas.width - (2 * this.Get('chart.hmargin')) - this.gutterLeft - this.gutterRight) / (lineData.length - 1);
+
+ // Loop thru each value given, plotting the line
+ // (FORMERLY FIRST)
+ for (i=0; i<lineData.length; i++) {
+
+ var data_point = lineData[i];
+
+ yPos = this.canvas.height - (((data_point - (data_point > 0 ? this.Get('chart.ymin') : (-1 * this.Get('chart.ymin')))) / (this.max - this.min) ) * this.grapharea);
+ yPos = (this.grapharea / (this.max - this.min)) * (data_point - this.min);
+ yPos = this.canvas.height - yPos;
+
+ /**
+ * This skirts an annoying JS rounding error
+ * SEARCH TAGS: JS ROUNDING ERROR DECIMALS
+ */
+ if (data_point == this.max) {
+ yPos = Math.round(yPos);
+ }
+
+ if (this.Get('chart.ylabels.invert')) {
+ yPos -= this.gutterBottom;
+ yPos -= this.gutterTop;
+ yPos = this.canvas.height - yPos;
+ }
+
+ // Make adjustments depending on the X axis position
+ if (this.Get('chart.xaxispos') == 'center') {
+ yPos = (yPos - this.gutterBottom - this.gutterTop) / 2;
+ yPos = yPos + this.gutterTop;
+
+ // TODO Check this
+ } else if (this.Get('chart.xaxispos') == 'top') {
+
+ yPos = (this.grapharea / (this.max - this.min)) * (Math.abs(data_point) - this.min);
+ yPos += this.gutterTop;
+
+ if (this.Get('chart.ylabels.invert')) {
+ yPos -= this.gutterTop;
+ yPos = this.grapharea - yPos;
+ yPos += this.gutterTop;
+ }
+
+ } else if (this.Get('chart.xaxispos') == 'bottom') {
+ // TODO
+ yPos -= this.gutterBottom; // Without this the line is out of place due to the gutter
+ }
+
+
+
+ // Null data points, and a special case for this bug:http://dev.rgraph.net/tests/ymin.html
+ if ( lineData[i] == null
+ || (this.Get('chart.xaxispos') == 'bottom' && lineData[i] < this.min && !this.Get('chart.outofbounds'))
+ || (this.Get('chart.xaxispos') == 'center' && lineData[i] < (-1 * this.max) && !this.Get('chart.outofbounds'))) {
+
+ yPos = null;
+ }
+
+ // Not always very noticeable, but it does have an effect
+ // with thick lines
+ this.context.lineCap = 'round';
+ this.context.lineJoin = 'round';
+
+ // Plot the line if we're at least on the second iteration
+ if (i > 0) {
+ xPos = xPos + xInterval;
+ } else {
+ xPos = this.Get('chart.hmargin') + this.gutterLeft;
+ }
+
+ if (this.Get('chart.animation.unfold.x')) {
+ xPos *= this.Get('chart.animation.factor');
+
+ if (xPos < this.Get('chart.gutter.left')) {
+ xPos = this.Get('chart.gutter.left');
+ }
+ }
+
+ /**
+ * Add the coords to an array
+ */
+ this.coords.push([xPos, yPos]);
+ lineCoords.push([xPos, yPos]);
+ }
+
+ this.context.stroke();
+
+ // Store the coords in another format, indexed by line number
+ this.coords2[index] = lineCoords;
+
+ /**
+ * For IE only: Draw the shadow ourselves as ExCanvas doesn't produce shadows
+ */
+ if (RGraph.isIE8() && this.Get('chart.shadow')) {
+ this.DrawIEShadow(lineCoords, this.context.shadowColor);
+ }
+
+ /**
+ * Now draw the actual line [FORMERLY SECOND]
+ */
+ this.context.beginPath();
+ // Transparent now as of 11/19/2011
+ this.context.strokeStyle = 'rgba(0,0,0,0)';
+ //this.context.strokeStyle = fill;
+ if (fill) {
+ this.context.fillStyle = fill;
+ }
+
+ var isStepped = this.Get('chart.stepped');
+ var isFilled = this.Get('chart.filled');
+
+
+ for (var i=0; i<lineCoords.length; ++i) {
+
+ xPos = lineCoords[i][0];
+ yPos = lineCoords[i][1];
+ var set = index;
+
+ var prevY = (lineCoords[i - 1] ? lineCoords[i - 1][1] : null);
+ var isLast = (i + 1) == lineCoords.length;
+
+ /**
+ * This nullifys values which are out-of-range
+ */
+ if (prevY < this.gutterTop || prevY > (RGraph.GetHeight(this) - this.gutterBottom) ) {
+ penUp = true;
+ }
+
+ if (i == 0 || penUp || !yPos || !prevY || prevY < this.gutterTop) {
+
+ if (this.Get('chart.filled') && !this.Get('chart.filled.range')) {
+
+ this.context.moveTo(xPos + 1, this.canvas.height - this.gutterBottom - (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0) -1);
+
+ // This facilitates the X axis being at the top
+ // NOTE: Also done below
+ if (this.Get('chart.xaxispos') == 'top') {
+ this.context.moveTo(xPos + 1, this.Get('chart.gutter.top'));
+ }
+
+ this.context.lineTo(xPos + 1, yPos);
+
+ } else {
+
+ if (RGraph.isIE8() && yPos == null) {
+ // Nada
+ } else {
+ this.context.moveTo(xPos, yPos);
+ }
+ }
+
+ if (yPos == null) {
+ penUp = true;
+
+ } else {
+ penUp = false;
+ }
+
+ } else {
+
+ // Draw the stepped part of stepped lines
+ if (isStepped) {
+ this.context.lineTo(xPos, lineCoords[i - 1][1]);
+ }
+
+ if ((yPos >= this.gutterTop && yPos <= (this.canvas.height - this.gutterBottom)) || this.Get('chart.outofbounds') ) {
+
+ if (isLast && this.Get('chart.filled') && !this.Get('chart.filled.range') && this.Get('chart.yaxispos') == 'right') {
+ xPos -= 1;
+ }
+
+
+ // Added 8th September 2009
+ if (!isStepped || !isLast) {
+ this.context.lineTo(xPos, yPos);
+
+ if (isFilled && lineCoords[i+1] && lineCoords[i+1][1] == null) {
+ this.context.lineTo(xPos, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+
+ // Added August 2010
+ } else if (isStepped && isLast) {
+ this.context.lineTo(xPos,yPos);
+ }
+
+
+ penUp = false;
+ } else {
+ penUp = true;
+ }
+ }
+ }
+
+ if (this.Get('chart.filled') && !this.Get('chart.filled.range')) {
+
+ // Is this needed ??
+ var fillStyle = this.Get('chart.fillstyle');
+
+ /**
+ * Draw the bottom edge of the filled bit using either the X axis or the prevlinedata,
+ * depending on the index of the line. The first line uses the X axis, and subsequent
+ * lines use the prevLineCoords array
+ */
+ if (index > 0 && this.Get('chart.filled.accumulative')) {
+ this.context.lineTo(xPos, prevLineCoords ? prevLineCoords[i - 1][1] : (this.canvas.height - this.gutterBottom - 1 + (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0)));
+
+ for (var k=(i - 1); k>=0; --k) {
+ this.context.lineTo(prevLineCoords[k][0], prevLineCoords[k][1]);
+ }
+ } else {
+ // Draw a line down to the X axis
+ if (this.Get('chart.xaxispos') == 'top') {
+ this.context.lineTo(xPos, this.Get('chart.gutter.top') + 1);
+ this.context.lineTo(lineCoords[0][0],this.Get('chart.gutter.top') + 1);
+ } else {
+ this.context.lineTo(xPos,this.canvas.height - this.gutterBottom - 1 - + (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0));
+ this.context.lineTo(lineCoords[0][0],this.canvas.height - this.Get('chart.gutter.bottom') - (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height - this.gutterTop - this.gutterBottom) / 2 : 0));
+ }
+ }
+
+ this.context.fillStyle = fill;
+
+ this.context.fill();
+ this.context.beginPath();
+ }
+
+ /**
+ * FIXME this may need removing when Chrome is fixed
+ * SEARCH TAGS: CHROME SHADOW BUG
+ */
+ if (navigator.userAgent.match(/Chrome/) && this.Get('chart.shadow') && this.Get('chart.chromefix') && this.Get('chart.shadow.blur') > 0) {
+
+ for (var i=lineCoords.length - 1; i>=0; --i) {
+ if (
+ typeof(lineCoords[i][1]) != 'number'
+ || (typeof(lineCoords[i+1]) == 'object' && typeof(lineCoords[i+1][1]) != 'number')
+ ) {
+ this.context.moveTo(lineCoords[i][0],lineCoords[i][1]);
+ } else {
+ this.context.lineTo(lineCoords[i][0],lineCoords[i][1]);
+ }
+ }
+ }
+
+ this.context.stroke();
+
+
+ if (this.Get('chart.backdrop')) {
+ this.DrawBackdrop(lineCoords, color);
+ }
+
+ // Now redraw the lines with the correct line width
+ this.RedrawLine(lineCoords, color, linewidth);
+
+ this.context.stroke();
+
+ // Draw the tickmarks
+ for (var i=0; i<lineCoords.length; ++i) {
+
+ i = Number(i);
+
+ if (isStepped && i == (lineCoords.length - 1)) {
+ this.context.beginPath();
+ //continue;
+ }
+
+ if (
+ (
+ tickmarks != 'endcircle'
+ && tickmarks != 'endsquare'
+ && tickmarks != 'filledendsquare'
+ && tickmarks != 'endtick'
+ && tickmarks != 'endtriangle'
+ && tickmarks != 'arrow'
+ && tickmarks != 'filledarrow'
+ )
+ || (i == 0 && tickmarks != 'arrow' && tickmarks != 'filledarrow')
+ || i == (lineCoords.length - 1)
+ ) {
+
+ var prevX = (i <= 0 ? null : lineCoords[i - 1][0]);
+ var prevY = (i <= 0 ? null : lineCoords[i - 1][1]);
+
+ this.DrawTick(lineData, lineCoords[i][0], lineCoords[i][1], color, false, prevX, prevY, tickmarks, i);
+
+ // Draws tickmarks on the stepped bits of stepped charts. Takend out 14th July 2010
+ //
+ //if (this.Get('chart.stepped') && lineCoords[i + 1] && this.Get('chart.tickmarks') != 'endsquare' && this.Get('chart.tickmarks') != 'endcircle' && this.Get('chart.tickmarks') != 'endtick') {
+ // this.DrawTick(lineCoords[i + 1][0], lineCoords[i][1], color);
+ //}
+ }
+ }
+
+ // Draw something off canvas to skirt an annoying bug
+ this.context.beginPath();
+ this.context.arc(RGraph.GetWidth(this) + 50000, RGraph.GetHeight(this) + 50000, 2, 0, 6.38, 1);
+ }
+
+
+ /**
+ * This functions draws a tick mark on the line
+ *
+ * @param xPos int The x position of the tickmark
+ * @param yPos int The y position of the tickmark
+ * @param color str The color of the tickmark
+ * @param bool Whether the tick is a shadow. If it is, it gets offset by the shadow offset
+ */
+ RGraph.Line.prototype.DrawTick = function (lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index)
+ {
+ // If the yPos is null - no tick
+ if ((yPos == null || yPos > (this.canvas.height - this.gutterBottom) || yPos < this.gutterTop) && !this.Get('chart.outofbounds') || !this.Get('chart.line.visible')) {
+ return;
+ }
+
+ this.context.beginPath();
+
+ var offset = 0;
+
+ // Reset the stroke and lineWidth back to the same as what they were when the line was drawm
+ // UPDATE 28th July 2011 - the line width is now set to 1
+ this.context.lineWidth = this.Get('chart.tickmarks.linewidth') ? this.Get('chart.tickmarks.linewidth') : this.Get('chart.linewidth');
+ this.context.strokeStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle;
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle;
+
+ // Cicular tick marks
+ if ( tickmarks == 'circle'
+ || tickmarks == 'filledcircle'
+ || tickmarks == 'endcircle') {
+
+ if (tickmarks == 'circle'|| tickmarks == 'filledcircle' || (tickmarks == 'endcircle') ) {
+ this.context.beginPath();
+ this.context.arc(xPos + offset, yPos + offset, this.Get('chart.ticksize'), 0, 360 / (180 / Math.PI), false);
+
+ if (tickmarks == 'filledcircle') {
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle;
+ } else {
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : 'white';
+ }
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ // Halfheight "Line" style tick marks
+ } else if (tickmarks == 'halftick') {
+ this.context.beginPath();
+ this.context.moveTo(xPos, yPos);
+ this.context.lineTo(xPos, yPos + this.Get('chart.ticksize'));
+
+ this.context.stroke();
+
+ // Tick style tickmarks
+ } else if (tickmarks == 'tick') {
+ this.context.beginPath();
+ this.context.moveTo(xPos, yPos - this.Get('chart.ticksize'));
+ this.context.lineTo(xPos, yPos + this.Get('chart.ticksize'));
+
+ this.context.stroke();
+
+ // Endtick style tickmarks
+ } else if (tickmarks == 'endtick') {
+ this.context.beginPath();
+ this.context.moveTo(xPos, yPos - this.Get('chart.ticksize'));
+ this.context.lineTo(xPos, yPos + this.Get('chart.ticksize'));
+
+ this.context.stroke();
+
+ // "Cross" style tick marks
+ } else if (tickmarks == 'cross') {
+ this.context.beginPath();
+ this.context.moveTo(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'));
+ this.context.lineTo(xPos + this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize'));
+ this.context.moveTo(xPos + this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'));
+ this.context.lineTo(xPos - this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize'));
+
+ this.context.stroke();
+
+
+ // Triangle style tick marks
+ } else if (tickmarks == 'triangle' || tickmarks == 'filledtriangle' || tickmarks == 'endtriangle') {
+ this.context.beginPath();
+
+ if (tickmarks == 'filledtriangle') {
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle;
+ } else {
+ this.context.fillStyle = 'white';
+ }
+
+ this.context.moveTo(xPos - this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize'));
+ this.context.lineTo(xPos, yPos - this.Get('chart.ticksize'));
+ this.context.lineTo(xPos + this.Get('chart.ticksize'), yPos + this.Get('chart.ticksize'));
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+
+ // A white bordered circle
+ } else if (tickmarks == 'borderedcircle' || tickmarks == 'dot') {
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.tickmarks.dot.color');
+ this.context.fillStyle = this.Get('chart.tickmarks.dot.color');
+
+ // The outer white circle
+ this.context.beginPath();
+ this.context.arc(xPos, yPos, this.Get('chart.ticksize'), 0, 360 / (180 / Math.PI), false);
+ this.context.closePath();
+
+
+ this.context.fill();
+ this.context.stroke();
+
+ // Now do the inners
+ this.context.beginPath();
+ this.context.fillStyle = color;
+ this.context.strokeStyle = color;
+ this.context.arc(xPos, yPos, this.Get('chart.ticksize') - 2, 0, 360 / (180 / Math.PI), false);
+
+ this.context.closePath();
+
+ this.context.fill();
+ this.context.stroke();
+
+ } else if ( tickmarks == 'square'
+ || tickmarks == 'filledsquare'
+ || (tickmarks == 'endsquare')
+ || (tickmarks == 'filledendsquare') ) {
+
+ this.context.fillStyle = 'white';
+ this.context.strokeStyle = this.context.strokeStyle; // FIXME Is this correct?
+
+ this.context.beginPath();
+ this.context.strokeRect(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'), this.Get('chart.ticksize') * 2, this.Get('chart.ticksize') * 2);
+
+ // Fillrect
+ if (tickmarks == 'filledsquare' || tickmarks == 'filledendsquare') {
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : this.context.strokeStyle;
+ this.context.fillRect(xPos - this.Get('chart.ticksize'), yPos - this.Get('chart.ticksize'), this.Get('chart.ticksize') * 2, this.Get('chart.ticksize') * 2);
+
+ } else if (tickmarks == 'square' || tickmarks == 'endsquare') {
+ this.context.fillStyle = isShadow ? this.Get('chart.shadow.color') : 'white';
+ this.context.fillRect((xPos - this.Get('chart.ticksize')) + 1, (yPos - this.Get('chart.ticksize')) + 1, (this.Get('chart.ticksize') * 2) - 2, (this.Get('chart.ticksize') * 2) - 2);
+ }
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * FILLED arrowhead
+ */
+ } else if (tickmarks == 'filledarrow') {
+
+ var x = Math.abs(xPos - prevX);
+ var y = Math.abs(yPos - prevY);
+
+ if (yPos < prevY) {
+ var a = Math.atan(x / y) + 1.57;
+ } else {
+ var a = Math.atan(y / x) + 3.14;
+ }
+
+ this.context.beginPath();
+ this.context.moveTo(xPos, yPos);
+ this.context.arc(xPos, yPos, 7, a - 0.5, a + 0.5, false);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Arrow head, NOT filled
+ */
+ } else if (tickmarks == 'arrow') {
+
+ var x = Math.abs(xPos - prevX);
+ var y = Math.abs(yPos - prevY);
+
+ if (yPos < prevY) {
+ var a = Math.atan(x / y) + 1.57;
+ } else {
+ var a = Math.atan(y / x) + 3.14;
+ }
+
+ this.context.beginPath();
+ this.context.moveTo(xPos, yPos);
+ this.context.arc(xPos, yPos, 7, a - 0.5 - (document.all ? 0.1 : 0.01), a - 0.4, false);
+
+ this.context.moveTo(xPos, yPos);
+ this.context.arc(xPos, yPos, 7, a + 0.5 + (document.all ? 0.1 : 0.01), a + 0.5, true);
+
+
+ this.context.stroke();
+
+ /**
+ * Custom tick drawing function
+ */
+ } else if (typeof(tickmarks) == 'function') {
+ tickmarks(this, lineData, lineData[index], index, xPos, yPos, color, prevX, prevY);
+ }
+ }
+
+
+ /**
+ * Draws a filled range if necessary
+ */
+ RGraph.Line.prototype.DrawRange = function ()
+ {
+ /**
+ * Fill the range if necessary
+ */
+ if (this.Get('chart.filled.range') && this.Get('chart.filled')) {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.fillstyle');
+ this.context.strokeStyle = this.Get('chart.fillstyle');
+ this.context.lineWidth = 1;
+ var len = (this.coords.length / 2);
+
+ for (var i=0; i<len; ++i) {
+ if (i == 0) {
+ this.context.moveTo(this.coords[i][0], this.coords[i][1])
+ } else {
+ this.context.lineTo(this.coords[i][0], this.coords[i][1])
+ }
+ }
+
+ for (var i=this.coords.length - 1; i>=len; --i) {
+ this.context.lineTo(this.coords[i][0], this.coords[i][1])
+ }
+ this.context.stroke();
+ this.context.fill();
+ }
+ }
+
+
+ /**
+ * Redraws the line with the correct line width etc
+ *
+ * @param array coords The coordinates of the line
+ */
+ RGraph.Line.prototype.RedrawLine = function (coords, color, linewidth)
+ {
+ if (this.Get('chart.noredraw')) {
+ return;
+ }
+
+ this.context.strokeStyle = (typeof(color) == 'object' && color ? color[0] : color);
+ this.context.lineWidth = linewidth;
+
+ if (!this.Get('chart.line.visible')) {
+ this.context.strokeStyle = 'rgba(0,0,0,0)';
+ }
+
+ if (this.Get('chart.curvy')) {
+ this.DrawCurvyLine(coords, !this.Get('chart.line.visible') ? 'rgba(0,0,0,0)' : color, linewidth);
+ return
+ }
+
+ this.context.beginPath();
+
+ var len = coords.length;
+ var width = this.canvas.width
+ var height = this.canvas.height;
+ var penUp = false;
+
+ for (var i=0; i<len; ++i) {
+
+ var xPos = coords[i][0];
+ var yPos = coords[i][1];
+
+ if (i > 0) {
+ var prevX = coords[i - 1][0];
+ var prevY = coords[i - 1][1];
+ }
+
+
+ if ((
+ (i == 0 && coords[i])
+ || (yPos < this.gutterTop)
+ || (prevY < this.gutterTop)
+ || (yPos > (height - this.gutterBottom))
+ || (i > 0 && prevX > (width - this.gutterRight))
+ || (i > 0 && prevY > (height - this.gutterBottom))
+ || prevY == null
+ || penUp == true
+ ) && (!this.Get('chart.outofbounds') || yPos == null || prevY == null) ) {
+
+ if (RGraph.isIE8() && yPos == null) {
+ // ...?
+ } else {
+ this.context.moveTo(coords[i][0], coords[i][1]);
+ }
+
+ penUp = false;
+
+ } else {
+
+ if (this.Get('chart.stepped') && i > 0) {
+ this.context.lineTo(coords[i][0], coords[i - 1][1]);
+ }
+
+ // Don't draw the last bit of a stepped chart. Now DO
+ //if (!this.Get('chart.stepped') || i < (coords.length - 1)) {
+ this.context.lineTo(coords[i][0], coords[i][1]);
+ //}
+ penUp = false;
+ }
+ }
+
+ /**
+ * If two colors are specified instead of one, go over the up bits
+ */
+ if (this.Get('chart.colors.alternate') && typeof(color) == 'object' && color[0] && color[1]) {
+ for (var i=1; i<len; ++i) {
+
+ var prevX = coords[i - 1][0];
+ var prevY = coords[i - 1][1];
+
+ this.context.beginPath();
+ this.context.strokeStyle = color[coords[i][1] < prevY ? 0 : 1];
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.moveTo(prevX, prevY);
+ this.context.lineTo(coords[i][0], coords[i][1]);
+ this.context.stroke();
+ }
+ }
+ }
+
+
+ /**
+ * This function is used by MSIE only to manually draw the shadow
+ *
+ * @param array coords The coords for the line
+ */
+ RGraph.Line.prototype.DrawIEShadow = function (coords, color)
+ {
+ var offsetx = this.Get('chart.shadow.offsetx');
+ var offsety = this.Get('chart.shadow.offsety');
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.strokeStyle = color;
+ this.context.beginPath();
+
+ for (var i=0; i<coords.length; ++i) {
+ if (i == 0) {
+ this.context.moveTo(coords[i][0] + offsetx, coords[i][1] + offsety);
+ } else {
+ this.context.lineTo(coords[i][0] + offsetx, coords[i][1] + offsety);
+ }
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draw the backdrop
+ */
+ RGraph.Line.prototype.DrawBackdrop = function (coords, color)
+ {
+ var size = this.Get('chart.backdrop.size');
+ this.context.lineWidth = size;
+ this.context.globalAlpha = this.Get('chart.backdrop.alpha');
+ this.context.strokeStyle = color;
+ this.context.lineJoin = 'miter';
+
+ this.context.beginPath();
+ this.context.moveTo(coords[0][0], coords[0][1]);
+ for (var j=1; j<coords.length; ++j) {
+ this.context.lineTo(coords[j][0], coords[j][1]);
+ }
+
+ this.context.stroke();
+
+ // Reset the alpha value
+ this.context.globalAlpha = 1;
+ this.context.lineJoin = 'round';
+ RGraph.NoShadow(this);
+ }
+
+
+ /**
+ * Returns the linewidth
+ */
+ RGraph.Line.prototype.GetLineWidth = function (i)
+ {
+ var linewidth = this.Get('chart.linewidth');
+
+ if (typeof(linewidth) == 'number') {
+ return linewidth;
+
+ } else if (typeof(linewidth) == 'object') {
+ if (linewidth[i]) {
+ return linewidth[i];
+ } else {
+ return linewidth[0];
+ }
+
+ alert('[LINE] Error! chart.linewidth should be a single number or an array of one or more numbers');
+ }
+ }
+
+
+ /**
+ * The getPoint() method - used to get the point the mouse is currently over, if any
+ *
+ * @param object e The event object
+ * @param object OPTIONAL You can pass in the bar object instead of the
+ * function getting it from the canvas
+ */
+ RGraph.Line.prototype.getPoint = function (e)
+ {
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var context = obj.context;
+ var mouseXY = RGraph.getMouseXY(e);
+ var mouseX = mouseXY[0];
+ var mouseY = mouseXY[1];
+
+ // This facilitates you being able to pass in the bar object as a parameter instead of
+ // the function getting it from the object
+ if (arguments[1]) {
+ obj = arguments[1];
+ }
+
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ var xCoord = obj.coords[i][0];
+ var yCoord = obj.coords[i][1];
+
+ // Do this if the hotspot is triggered by the X coord AND the Y coord
+ if ( obj.Get('chart.tooltips.hotspot.xonly') == false
+ && mouseX <= (xCoord + 5)
+ && mouseX >= (xCoord - 5)
+ && mouseY <= (yCoord + 5)
+ && mouseY >= (yCoord - 5)
+ ) {
+
+ return [obj, xCoord, yCoord, i];
+
+ } else if ( obj.Get('chart.tooltips.hotspot.xonly') == true
+ && mouseX <= (xCoord + 5)
+ && mouseX >= (xCoord - 5)) {
+
+ return [obj, xCoord, yCoord, i];
+ }
+ }
+ }
+
+
+ /**
+ * Draws the above line labels
+ */
+ RGraph.Line.prototype.DrawAboveLabels = function ()
+ {
+ var context = this.context;
+ var size = this.Get('chart.labels.above.size');
+ var font = this.Get('chart.text.font');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+
+ context.beginPath();
+
+ // Don't need to check that chart.labels.above is enabled here, it's been done already
+ for (var i=0; i<this.coords.length; ++i) {
+ var coords = this.coords[i];
+
+ RGraph.Text(context, font, size, coords[0], coords[1] - 5 - size, RGraph.number_format(this, this.data_arr[i], units_pre, units_post), 'center', 'center', true, null, 'rgba(255, 255, 255, 0.7)');
+ }
+
+ context.fill();
+ }
+
+
+ /**
+ * Draw a curvy line. This isn't 100% accurate but may be more to your tastes
+ */
+ RGraph.Line.prototype.DrawCurvyLine = function (coords, color, linewidth)
+ {
+ var co = this.context;
+
+ // Now calculate the halfway point
+ co.beginPath();
+
+ co.strokeStyle = color;
+ co.lineWidth = linewidth;
+
+ for (var i=0; i<coords.length; ++i) {
+ if (coords[i + 1]) {
+
+ var factor = this.Get('chart.curvy.factor');
+ var coordX = coords[i][0];
+ var coordY = coords[i][1];
+ var nextX = coords[i + 1][0];
+ var nextY = coords[i + 1][1];
+ var offsetX = (coords[i + 1][0] - coords[i][0]) * factor;
+ var offsetY = (coords[i + 1][1] - coords[i][1]) * factor;
+
+ if (i == 0) {
+ co.moveTo(coordX, coordY);
+ co.lineTo(nextX - offsetX, nextY - offsetY);
+
+ } else if (nextY == null) {
+ co.lineTo(coordX, coordY);
+
+ } else if (coordY == null) {
+ co.moveTo(nextX, nextY);
+
+
+ } else {
+
+ co.quadraticCurveTo(coordX, coordY, coordX + offsetX, coordY + offsetY);
+
+ if (nextY) {
+ co.lineTo(nextX - offsetX, nextY - offsetY);
+ } else {
+ co.lineTo(coordX, coordY);
+ }
+ }
+
+ // Draw the last bit
+ } else {
+ co.lineTo(coords[i][0], coords[i][1]);
+ }
+ }
+
+ co.stroke();
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.meter.js b/schall/static/RGraph/libraries/RGraph.meter.js
new file mode 100644
index 0000000..a246cbf
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.meter.js
@@ -0,0 +1,573 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The bar chart constructor
+ *
+ * @param string canvas The canvas ID
+ * @param min integer The minimum value
+ * @param max integer The maximum value
+ * @param value integer The indicated value
+ */
+ RGraph.Meter = function (id, min, max, value)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'meter';
+ this.min = min;
+ this.max = max;
+ this.value = value;
+ this.centerx = null;
+ this.centery = null;
+ this.radius = null;
+ this.isRGraph = true;
+ this.currentValue = null;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config type stuff
+ this.properties = {
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.linewidth': 1,
+ 'chart.linewidth.segments': 1,
+ 'chart.strokestyle': null,
+ 'chart.border': true,
+ 'chart.border.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.value.label': false,
+ 'chart.value.text.decimals': 0,
+ 'chart.value.text.units.pre': '',
+ 'chart.value.text.units.post': '',
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.color': 'black',
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.green.start': ((this.max - this.min) * 0.35) + this.min,
+ 'chart.green.end': this.max,
+ 'chart.green.color': '#207A20',
+ 'chart.yellow.start': ((this.max - this.min) * 0.1) + this.min,
+ 'chart.yellow.end': ((this.max - this.min) * 0.35) + this.min,
+ 'chart.yellow.color': '#D0AC41',
+ 'chart.red.start': this.min,
+ 'chart.red.end': ((this.max - this.min) * 0.1) + this.min,
+ 'chart.red.color': '#9E1E1E',
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.contextmenu': null,
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.shadow': false,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.tickmarks.small.num': 100,
+ 'chart.tickmarks.big.num': 10,
+ 'chart.tickmarks.small.color': '#bbb',
+ 'chart.tickmarks.big.color': 'black',
+ 'chart.scale.decimals': 0,
+ 'chart.radius': null,
+ 'chart.centerx': null,
+ 'chart.centery': null,
+ 'chart.labels': true,
+ 'chart.segment.radius.start': null,
+ 'chart.needle.radius': null,
+ 'chart.needle.tail': false
+ }
+
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[METER] No canvas support');
+ return;
+ }
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Meter.prototype.Set = function (name, value)
+ {
+ if (name == 'chart.value') {
+ this.value = value;
+ return;
+ }
+
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Meter.prototype.Get = function (name)
+ {
+ if (name == 'chart.value') {
+ return this.value;
+ }
+
+ return this.properties[name];
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.Meter.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Constrain the value to be within the min and max
+ */
+ if (this.value > this.max) this.value = this.max;
+ if (this.value < this.min) this.value = this.min;
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ this.centerx = this.canvas.width / 2;
+ this.centery = this.canvas.height - this.gutterBottom;
+ this.radius = Math.min(
+ this.canvas.width - this.gutterLeft - this.gutterRight,
+ this.canvas.height - this.gutterTop - this.gutterBottom
+ );
+
+ /**
+ * Custom centerx, centery and radius
+ */
+ if (typeof(this.Get('chart.centerx')) == 'number') this.centerx = this.Get('chart.centerx');
+ if (typeof(this.Get('chart.centery')) == 'number') this.centery = this.Get('chart.centery');
+ if (typeof(this.Get('chart.radius')) == 'number') this.radius = this.Get('chart.radius');
+
+ this.DrawBackground();
+ this.DrawNeedle();
+ this.DrawLabels();
+ this.DrawReadout();
+
+ /**
+ * Draw the title
+ */
+ RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * For MSIE only, to cover the spurious lower ends of the circle
+ */
+ if (RGraph.isIE8()) {
+ // Cover the left tail
+ this.context.beginPath();
+ this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ this.context.fillStyle = 'white';
+ this.context.fillRect(this.centerx - this.radius - 5, RGraph.GetHeight(this) - this.gutterBottom + 1, 10, this.gutterBottom);
+ this.context.fill();
+
+ // Cover the right tail
+ this.context.beginPath();
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.fillStyle = 'white';
+ this.context.fillRect(this.centerx + this.radius - 5, RGraph.GetHeight(this) - this.gutterBottom + 1, 10, this.gutterBottom);
+ this.context.fill();
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the background of the chart
+ */
+ RGraph.Meter.prototype.DrawBackground = function ()
+ {
+ // Draw the shadow
+ if (this.Get('chart.shadow')) {
+ this.context.beginPath();
+ this.context.fillStyle = 'white';
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+
+ this.context.arc(this.centerx, this.centery, this.radius, 3.14, 6.28, false);
+ //this.context.arc(this.centerx, this.centery, , 0, 6.28, false);
+ this.context.fill();
+
+
+ this.context.beginPath();
+ var r = (this.radius * 0.06) > 40 ? 40 : (this.radius * 0.06);
+ this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0);
+ this.context.fill();
+
+ RGraph.NoShadow(this);
+ }
+
+ // First, draw the grey tickmarks
+ if (this.Get('chart.tickmarks.small.num')) {
+ for (var i=0; i<3.14; i+=(3.14 / this.Get('chart.tickmarks.small.num'))) {
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.tickmarks.small.color');
+ this.context.arc(this.centerx, this.centery, this.radius, 3.14 + i, 3.1415 + i, 0);
+ this.context.arc(this.centerx, this.centery, this.radius - 5, 3.14 + i, 3.1415 + i, 0);
+ this.context.stroke();
+ }
+
+ // Draw the white semi-circle that makes the tickmarks
+ this.context.beginPath();
+ this.context.fillStyle = 'white'
+ this.context.arc(this.centerx, this.centery, this.radius - 4, 3.14, 6.28, false);
+ this.context.closePath();
+ this.context.fill();
+ }
+
+
+
+ // Second, draw the darker tickmarks. First run draws them in white to get rid of the existing tickmark,
+ // then the second run draws them in the requested color
+ if (this.Get('chart.tickmarks.big.num')) {
+ var colors = ['white','white',this.Get('chart.tickmarks.big.color')];
+ for (var j=0; j<colors.length; ++j) {
+ for (var i=0; i<3.14; i+=(3.1415927 / this.Get('chart.tickmarks.big.num'))) {
+ this.context.beginPath();
+ this.context.strokeStyle = colors[j];
+ this.context.arc(this.centerx, this.centery, this.radius, 3.14 + i, 3.1415 + i, 0);
+ this.context.arc(this.centerx, this.centery, this.radius - 5, 3.14 + i, 3.1415 + i, 0);
+ //this.context.lineTo(this.centerx, this.centery);
+ this.context.stroke();
+ }
+ }
+ }
+
+ // Draw the white circle that makes the tickmarks
+ this.context.beginPath();
+ this.context.fillStyle = 'white';
+ this.context.arc(this.centerx, this.centery, this.radius - 7, 3.1415927, 6.28, false);
+ this.context.closePath();
+ this.context.fill();
+
+ /**
+ * Color ranges - either green/yellow/red or an arbitrary number of ranges
+ */
+ var ranges = this.Get('chart.colors.ranges');
+
+ if (RGraph.is_array(this.Get('chart.colors.ranges'))) {
+
+ var ranges = this.Get('chart.colors.ranges');
+
+ for (var i=0; i<ranges.length; ++i) {
+
+ this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : ranges[i][2];
+ this.context.fillStyle = ranges[i][2];
+ this.context.lineWidth = this.Get('chart.linewidth.segments');
+
+ this.context.beginPath();
+ this.context.moveTo(this.centerx,this.centery);
+ this.context.arc(this.centerx,
+ this.centery,
+ this.radius * 0.85,
+ (((ranges[i][0] - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,
+ (((ranges[i][1] - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,
+ false);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ // Stops the last line from being changed to a big linewidth. So it seems.
+ this.context.beginPath();
+
+ } else {
+ // Draw the green area
+ this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.green.color');
+ this.context.fillStyle = this.Get('chart.green.color');
+ this.context.lineWidth = this.Get('chart.linewidth.segments');
+ this.context.beginPath();
+ this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.green.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.green.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false);
+
+ if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) {
+ this.context.arc(this.centerx,this.centery,this.Get('chart.segment.radius.start'),(((this.Get('chart.green.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.green.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true);
+ } else {
+ this.context.lineTo(this.centerx, this.centery);
+ }
+
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the yellow area
+ this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.yellow.color');
+ this.context.fillStyle = this.Get('chart.yellow.color');
+ this.context.lineWidth = this.Get('chart.linewidth.segments');
+ this.context.beginPath();
+ this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.yellow.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.yellow.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false);
+
+ if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) {
+ this.context.arc(this.centerx,this.centery,this.Get('chart.segment.radius.start'),(((this.Get('chart.yellow.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927, (((this.Get('chart.yellow.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true);
+ } else {
+ this.context.lineTo(this.centerx, this.centery);
+ }
+
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the red area
+ this.context.strokeStyle = this.Get('chart.strokestyle') ? this.Get('chart.strokestyle') : this.Get('chart.red.color');
+ this.context.fillStyle = this.Get('chart.red.color');
+ this.context.lineWidth = this.Get('chart.linewidth.segments');
+ this.context.beginPath();
+ this.context.arc(this.centerx,this.centery,this.radius * 0.85,(((this.Get('chart.red.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.red.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,false);
+
+ if (typeof(this.Get('chart.segment.radius.start')) && this.Get('chart.segment.radius.start')) {
+ this.context.arc(this.centerx,this.centery, this.Get('chart.segment.radius.start'),(((this.Get('chart.red.end') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,(((this.Get('chart.red.start') - this.min) / (this.max - this.min)) * 3.1415927) + 3.1415927,true);
+ } else {
+ this.context.lineTo(this.centerx, this.centery);
+ }
+
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ // Revert the linewidth
+ this.context.lineWidth = 1;
+ }
+
+ // Draw the outline
+ if (this.Get('chart.border')) {
+ this.context.strokeStyle = this.Get('chart.border.color');
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+ this.context.beginPath();
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery, this.radius, 3.1415927, 6.2831854, false);
+ this.context.closePath();
+ }
+
+ this.context.stroke();
+
+ // Reset the linewidth back to 1
+ this.context.lineWidth = 1;
+ }
+
+
+ /**
+ * Draws the pointer
+ */
+ RGraph.Meter.prototype.DrawNeedle = function ()
+ {
+ // Allow customising the needle radius
+ var needleRadius = typeof(this.Get('chart.needle.radius')) == 'number' ? this.Get('chart.needle.radius') : this.radius * 0.7;
+
+ // First draw the circle at the bottom
+ this.context.fillStyle = 'black';
+ this.context.lineWidth = this.radius >= 200 ? 7 : 3;
+ this.context.lineCap = 'round';
+
+ // Now, draw the arm of the needle
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ if (typeof(this.Get('chart.needle.linewidth')) == 'number') this.context.lineWidth = this.Get('chart.needle.linewidth');
+ var a = (((this.value - this.min) / (this.max - this.min)) * 3.14) + 3.14;
+ this.context.arc(this.centerx, this.centery, needleRadius, a, a + 0.001, false);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.stroke();
+
+ // Draw the triangular needle head
+ this.context.beginPath();
+ this.context.lineWidth = 1;
+ //this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery, needleRadius + 15, a, a + 0.001, 0);
+ this.context.arc(this.centerx, this.centery, needleRadius - 15, a + 0.087, a + 0.087999, 0);
+ this.context.arc(this.centerx, this.centery, needleRadius - 15, a - 0.087, a - 0.087999, 1);
+ this.context.fill();
+
+ // Draw the tail if requested
+ if (this.Get('chart.needle.tail')) {
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ if (typeof(this.Get('chart.needle.linewidth')) == 'number') this.context.lineWidth = this.Get('chart.needle.linewidth');
+ var a = (((this.value - this.min) / (this.max - this.min)) * 3.14) + 6.28;
+ this.context.arc(this.centerx, this.centery, 25, a, a + 0.001, false);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.stroke();
+ }
+
+ // Draw the center circle
+ var r = (this.radius * 0.06) > 40 ? 40 : (this.radius * 0.06);
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0);
+ this.context.fill();
+
+ // Draw the centre bit of the circle
+ this.context.fillStyle = 'white';
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, r - 2, 0, 6.28, 0);
+ this.context.fill();
+ }
+
+
+ /**
+ * Draws the labels
+ */
+ RGraph.Meter.prototype.DrawLabels = function ()
+ {
+ if (!this.Get('chart.labels')) {
+ return;
+ }
+
+ var context = this.context;
+ var radius = this.radius;
+ var text_size = this.Get('chart.text.size');
+ var text_font = this.Get('chart.text.font');
+ var units_post = this.Get('chart.units.post');
+ var units_pre = this.Get('chart.units.pre');
+ var centerx = this.centerx;
+ var centery = this.centery;
+ var min = this.min;
+ var max = this.max;
+ var decimals = this.Get('chart.scale.decimals');
+
+ context.fillStyle = this.Get('chart.text.color');
+ context.lineWidth = 1;
+
+ context.beginPath();
+
+
+ RGraph.Text(context,text_font,text_size,centerx - radius + (0.075 * radius),centery - (this.Get('chart.border') ? 10 : -5),units_pre + (min).toFixed(decimals) + units_post,'center', 'left', false, 270);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(0.62819 / 2) * (radius - (0.075 * radius)) ),centery - (Math.sin(0.682819 / 2) * (radius - (0.1587 * radius)) ),units_pre + (((max - min) * (1/10)) + min).toFixed(decimals) + units_post,'center','center', false, 288);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(0.62819) * (radius - (0.07 * radius)) ),centery - (Math.sin(0.682819) * (radius - (0.15 * radius)) ),units_pre + (((max - min) * (2/10)) + min).toFixed(decimals) + units_post,'center','center', false, 306);
+ RGraph.Text(context, text_font, text_size,centerx - (Math.cos(0.95) * (radius - (0.085 * radius)) ),centery - (Math.sin(0.95) * (radius - (0.0785 * radius)) ),units_pre + (((max - min) * (3/10)) + min).toFixed(decimals) + units_post,'center', 'center', false, 320);
+ RGraph.Text(context, text_font, text_size,centerx - (Math.cos(1.2566) * (radius - (0.085 * radius)) ),centery - (Math.sin(1.2566) * (radius - (0.0785 * radius)) ),units_pre + (((max - min) * (4/10)) + min).toFixed(decimals) + units_post,'center', 'center', false, 342);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(1.57) * (radius - (0.075 * radius)) ),centery - (Math.sin(1.57) * (radius - (0.075 * radius)) ),units_pre + (((max - min) * (5/10)) + min).toFixed(decimals) + units_post,'center','center', false, 0);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(1.88495562) * (radius - (0.075 * radius)) ),centery - (Math.sin(1.88495562) * (radius - (0.075 * radius)) ),units_pre + (((max - min)* (6/10)) + min).toFixed(decimals) + units_post,'center','center', false, 18);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.1989) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.1989) * (radius - (0.075 * radius)) ),units_pre + (((max - min)* (7/10)) + min).toFixed(decimals) + units_post,'center','center', false, 36);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.51327416) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.51327416) * (radius - (0.075 * radius)) ), units_pre + (((max - min) * (8/10)) + min).toFixed(decimals) + units_post,'center','center', false, 54);
+ RGraph.Text(context,text_font,text_size,centerx - (Math.cos(2.82764832) * (radius - (0.075 * radius)) ),centery - (Math.sin(2.82764832) * (radius - (0.075 * radius)) ),units_pre + (((max - min) * (9/10)) + min).toFixed(decimals) + units_post,'center','center', false, 72);
+ RGraph.Text(context,text_font,text_size,centerx + radius - (0.075 * radius),centery - (!this.Get('chart.border') ? -5 : 10),units_pre + (max).toFixed(decimals) + units_post,'center', 'right', false, 90);
+
+ context.fill();
+ context.stroke();
+ }
+
+
+ /**
+ * This function draws the text readout if specified
+ */
+ RGraph.Meter.prototype.DrawReadout = function ()
+ {
+ if (this.Get('chart.value.text')) {
+ this.context.beginPath();
+ RGraph.Text(this.context,
+ this.Get('chart.text.font'),
+ this.Get('chart.text.size'),
+ this.centerx,
+ this.centery - this.Get('chart.text.size') - 15,
+ this.Get('chart.value.text.units.pre') + (this.value).toFixed(this.Get('chart.value.text.decimals')) + this.Get('chart.value.text.units.post'),
+ 'center',
+ 'center',
+ true,
+ null,
+ 'white');
+
+ this.context.stroke();
+ this.context.fill();
+ }
+ }
diff --git a/schall/static/RGraph/libraries/RGraph.modaldialog.js b/schall/static/RGraph/libraries/RGraph.modaldialog.js
new file mode 100644
index 0000000..2d4cc88
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.modaldialog.js
@@ -0,0 +1,244 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ ModalDialog = {}
+ ModalDialog.dialog = null;
+ ModalDialog.background = null;
+ ModalDialog.offset = 50;
+ ModalDialog.events = [];
+
+ /**
+ * Shows the dialog with the supplied DIV acting as the contents
+ *
+ * @param string id The ID of the DIV to use as the dialogs contents
+ * @param int width The width of the dialog
+ */
+ ModalDialog.Show = function (id, width)
+ {
+ ModalDialog.id = id;
+ ModalDialog.width = width;
+
+ ModalDialog.ShowBackground();
+ ModalDialog.ShowDialog();
+
+ // Install the event handlers
+ window.onresize = ModalDialog.Resize;
+
+
+ // Call them initially
+ ModalDialog.Resize();
+
+ ModalDialog.FireCustomEvent('onmodaldialog');
+ }
+
+
+ /**
+ * Shows the background semi-transparent darkened DIV
+ */
+ ModalDialog.ShowBackground = function ()
+ {
+ // Create the background if neccessary
+ ModalDialog.background = document.createElement('DIV');
+ ModalDialog.background.className = 'ModalDialog_background';
+ ModalDialog.background.style.position = 'fixed';
+ ModalDialog.background.style.top = 0;
+ ModalDialog.background.style.left = 0;
+ ModalDialog.background.style.width = (screen.width + 100) + 'px';
+ ModalDialog.background.style.height = (screen.height + 100) + 'px';
+ ModalDialog.background.style.backgroundColor = 'rgb(204,204,204)';
+ ModalDialog.background.style.opacity = 0;
+ ModalDialog.background.style.zIndex = 3276;
+ ModalDialog.background.style.filter = "Alpha(opacity=50)";
+
+ document.body.appendChild(ModalDialog.background);
+
+ ModalDialog.background.style.visibility = 'visible';
+ }
+
+
+ /**
+ * Shows the dialog itself
+ */
+ ModalDialog.ShowDialog = function ()
+ {
+ // Create the DIV if necessary
+ if (!ModalDialog.dialog) {
+ ModalDialog.dialog = document.createElement('DIV');
+
+ ModalDialog.dialog.id = 'ModalDialog_dialog';
+ ModalDialog.dialog.className = 'ModalDialog_dialog';
+
+ var borderRadius = '15px';
+
+ ModalDialog.dialog.style.borderRadius = borderRadius;
+ ModalDialog.dialog.style.MozBorderRadius = borderRadius;
+ ModalDialog.dialog.style.WebkitBorderRadius = borderRadius;
+
+ ModalDialog.dialog.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ ModalDialog.dialog.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
+ ModalDialog.dialog.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';
+
+ ModalDialog.dialog.style.position = 'fixed';
+ ModalDialog.dialog.style.backgroundColor = 'white';
+ ModalDialog.dialog.style.width = parseInt(ModalDialog.width) + 'px';
+ ModalDialog.dialog.style.border = '2px solid #999';
+ ModalDialog.dialog.style.zIndex = 32767;
+ ModalDialog.dialog.style.padding = '5px';
+ ModalDialog.dialog.style.paddingTop = '25px';
+ ModalDialog.dialog.style.opacity = 0;
+
+ if (document.all) {
+ ModalDialog.dialog.style.zIndex = 32767;
+ }
+
+
+
+ // Accomodate various browsers
+ if (navigator.userAgent.indexOf('Opera') != -1) {
+ ModalDialog.dialog.style.paddingTop = '25px';
+
+ } else if (navigator.userAgent.indexOf('MSIE') != -1) {
+ ModalDialog.dialog.style.paddingTop = '25px';
+
+ } else if (navigator.userAgent.indexOf('Safari') != -1) {
+ ModalDialog.dialog.style.paddingTop = '25px';
+ }
+
+ document.body.appendChild(ModalDialog.dialog);
+
+
+ // Now create the grey bar at the top of the dialog
+ var bar = document.createElement('DIV');
+ bar.className = 'ModalDialog_topbar';
+ bar.style.top = 0;
+ bar.style.left = 0;
+ bar.style.width = '100%';//(ModalDialog.dialog.offsetWidth - 4) + 'px';
+ bar.style.height = '20px';
+ bar.style.backgroundColor = '#bbb';
+ bar.style.borderBottom = '2px solid #999';
+ //bar.style.zIndex = 50000;
+ bar.style.position = 'absolute';
+ var borderRadius = '11px';
+ bar.style.WebkitBorderTopLeftRadius = borderRadius;
+ bar.style.WebkitBorderTopRightRadius = borderRadius;
+ bar.style.MozBorderRadiusTopleft = borderRadius;
+ bar.style.MozBorderRadiusTopright = borderRadius;
+ bar.style.borderTopRightRadius = borderRadius;
+ bar.style.borderTopLeftRadius = borderRadius;
+ ModalDialog.dialog.appendChild(bar);
+
+ // Add the content div
+ var content = document.createElement('DIV');
+ //content.style.paddingTop = '20px';
+ content.style.width = '100%';
+ content.style.height = '100%';
+ ModalDialog.dialog.appendChild(content);
+
+ content.innerHTML = document.getElementById(ModalDialog.id).innerHTML;
+
+ // Now reposition the dialog in the center
+ ModalDialog.dialog.style.left = (document.body.offsetWidth / 2) - (ModalDialog.dialog.offsetWidth / 2) + 'px';
+ ModalDialog.dialog.style.top = '30%';
+ }
+
+ // Show the dialog
+ ModalDialog.dialog.style.visibility = 'visible';
+
+ // A simple fade-in effect
+ setTimeout('ModalDialog.dialog.style.opacity = 0.2', 50);
+ setTimeout('ModalDialog.dialog.style.opacity = 0.4', 100);
+ setTimeout('ModalDialog.dialog.style.opacity = 0.6', 150);
+ setTimeout('ModalDialog.dialog.style.opacity = 0.8', 200);
+ setTimeout('ModalDialog.dialog.style.opacity = 1', 250);
+
+ setTimeout('ModalDialog.background.style.opacity = 0.1', 50);
+ setTimeout('ModalDialog.background.style.opacity = 0.2', 100);
+ setTimeout('ModalDialog.background.style.opacity = 0.3', 150);
+ setTimeout('ModalDialog.background.style.opacity = 0.4', 200);
+ setTimeout('ModalDialog.background.style.opacity = 0.5', 250);
+ }
+
+
+ /**
+ * Hides everything
+ */
+ ModalDialog.Close = function ()
+ {
+ if (ModalDialog.dialog) {
+ ModalDialog.dialog.style.visibility = 'hidden';
+ ModalDialog.dialog.style.opacity = 0;
+ }
+
+ if (ModalDialog.background) {
+ ModalDialog.background.style.visibility = 'hidden';
+ ModalDialog.background.style.opacity = 0;
+ }
+ }
+
+ // An alias
+ ModalDialog.Hide = ModalDialog.Close
+
+
+ /**
+ * Accommodate the window being resized
+ */
+ ModalDialog.Resize = function ()
+ {
+ if (ModalDialog.dialog) {
+ ModalDialog.dialog.style.left = (document.body.offsetWidth / 2) - (ModalDialog.dialog.offsetWidth / 2) + 'px';
+ }
+
+ ModalDialog.background.style.width = '2500px';
+ ModalDialog.background.style.height = '2500px';
+ }
+
+
+ /**
+ * Returns the page height
+ *
+ * @return int The page height
+ */
+ ModalDialog.AddCustomEventListener = function (name, func)
+ {
+ if (typeof(ModalDialog.events) == 'undefined') {
+ ModalDialog.events = [];
+ }
+
+ ModalDialog.events.push([name, func]);
+ }
+
+
+ /**
+ * Used to fire the ModalDialog custom event
+ *
+ * @param object obj The graph object that fires the event
+ * @param string event The name of the event to fire
+ */
+ ModalDialog.FireCustomEvent = function (name)
+ {
+ for (var i=0; i<ModalDialog.events.length; ++i) {
+ if (typeof(ModalDialog.events[i][0]) == 'string' && ModalDialog.events[i][0] == name && typeof(ModalDialog.events[i][1]) == 'function') {
+ ModalDialog.events[i][1]();
+ }
+ }
+ }
+
+
+ /**
+ * Returns true if the browser is IE8
+ */
+ ModalDialog.isIE8 = function ()
+ {
+ return document.all && (navigatot.userAgent.indexOf('MSIE 8') > 0);
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.odo.js b/schall/static/RGraph/libraries/RGraph.odo.js
new file mode 100644
index 0000000..2da7fcd
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.odo.js
@@ -0,0 +1,815 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The odometer constructor. Pass it the ID of the canvas tag, the start value of the odo,
+ * the end value, and the value that the pointer should point to.
+ *
+ * @param string id The ID of the canvas tag
+ * @param int start The start value of the Odo
+ * @param int end The end value of the odo
+ * @param int value The indicated value (what the needle points to)
+ */
+ RGraph.Odometer = function (id, start, end, value)
+ {
+ this.id = id
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.canvas.__object__ = this;
+ this.type = 'odo';
+ this.isRGraph = true;
+ this.start = start;
+ this.end = end;
+ this.value = value;
+ this.currentValue = null;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ this.properties = {
+ 'chart.radius': null,
+ 'chart.value.text': false,
+ 'chart.value.text.decimals': 0,
+ 'chart.needle.color': 'black',
+ 'chart.needle.width': 2,
+ 'chart.needle.head': true,
+ 'chart.needle.tail': true,
+ 'chart.needle.type': 'pointer',
+ 'chart.needle.extra': [],
+ 'chart.needle.triangle.border': '#aaa',
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.green.max': end * 0.75,
+ 'chart.red.min': end * 0.9,
+ 'chart.green.color': 'green',
+ 'chart.yellow.color': 'yellow',
+ 'chart.red.color': 'red',
+ 'chart.green.solid': false,
+ 'chart.yellow.solid': false,
+ 'chart.red.solid': false,
+ 'chart.label.area': 35,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.font': null,
+ 'chart.title.bold': true,
+ 'chart.contextmenu': null,
+ 'chart.linewidth': 1,
+ 'chart.shadow.inner': false,
+ 'chart.shadow.inner.color': 'black',
+ 'chart.shadow.inner.offsetx': 3,
+ 'chart.shadow.inner.offsety': 3,
+ 'chart.shadow.inner.blur': 6,
+ 'chart.shadow.outer': false,
+ 'chart.shadow.outer.color': '#666',
+ 'chart.shadow.outer.offsetx': 0,
+ 'chart.shadow.outer.offsety': 0,
+ 'chart.shadow.outer.blur': 15,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.scale.decimals': 0,
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.border': false,
+ 'chart.border.color1': '#BEBCB0',
+ 'chart.border.color2': '#F0EFEA',
+ 'chart.border.color3': '#BEBCB0',
+ 'chart.tickmarks.highlighted': false,
+ 'chart.zerostart': false,
+ 'chart.labels': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.value.units.pre': '',
+ 'chart.value.units.post': '',
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.halign': 'right',
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.text.size': 10,
+ }
+ }
+
+
+ /**
+ * A peudo setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Odometer.prototype.Set = function (name, value)
+ {
+ if (name == 'chart.needle.style') {
+ alert('[RGRAPH] The RGraph property chart.needle.style has changed to chart.needle.color');
+ }
+
+ if (name == 'chart.needle.thickness') {
+ name = 'chart.needle.width';
+ }
+
+ if (name == 'chart.value') {
+ this.value = value;
+ return;
+ }
+
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Odometer.prototype.Get = function (name)
+ {
+ if (name == 'chart.value') {
+ return this.value;
+ }
+
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * Draws the odometer
+ */
+ RGraph.Odometer.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * No longer allow values outside the range of the Odo
+ */
+ if (this.value > this.end) {
+ this.value = this.end;
+ }
+
+ if (this.value < this.start) {
+ this.value = this.start;
+ }
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Work out a few things
+ this.radius = Math.min((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / 2, (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2) - (this.Get('chart.border') ? 25 : 0);
+ this.diameter = 2 * this.radius;
+ this.centerx = this.canvas.width / 2;
+ this.centery = this.canvas.height / 2;
+ this.range = this.end - this.start;
+
+
+ if (typeof(this.Get('chart.radius')) == 'number') {
+ this.radius = this.Get('chart.radius');
+ }
+
+ /**
+ * Move the centerx if the key is defined
+ */
+ if (this.Get('chart.key').length > 0 && this.canvas.width > this.canvas.height) {
+ this.centerx = 5 + this.radius;
+ }
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+ // Draw the background
+ this.DrawBackground();
+
+ // And lastly, draw the labels
+ this.DrawLabels();
+
+ // Draw the needle
+ this.DrawNeedle(this.value, this.Get('chart.needle.color'));
+
+ /**
+ * Draw any extra needles
+ */
+ if (this.Get('chart.needle.extra').length > 0) {
+ for (var i=0; i<this.Get('chart.needle.extra').length; ++i) {
+ var needle = this.Get('chart.needle.extra')[i];
+ this.DrawNeedle(needle[0], needle[1], needle[2]);
+ }
+ }
+
+ /**
+ * Draw the key if requested
+ */
+ if (this.Get('chart.key').length > 0) {
+ // Build a colors array out of the needle colors
+ var colors = [this.Get('chart.needle.color')];
+
+ if (this.Get('chart.needle.extra').length > 0) {
+ for (var i=0; i<this.Get('chart.needle.extra').length; ++i) {
+ var needle = this.Get('chart.needle.extra')[i];
+ colors.push(needle[1]);
+ }
+ }
+
+ RGraph.DrawKey(this, this.Get('chart.key'), colors);
+ }
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+ /**
+ * Draws the background
+ */
+ RGraph.Odometer.prototype.DrawBackground = function ()
+ {
+ this.context.beginPath();
+
+ /**
+ * Turn on the shadow if need be
+ */
+ if (this.Get('chart.shadow.outer')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.outer.color'), this.Get('chart.shadow.outer.offsetx'), this.Get('chart.shadow.outer.offsety'), this.Get('chart.shadow.outer.blur'));
+ }
+
+ var backgroundColor = '#eee';
+
+ // Draw the grey border
+ this.context.fillStyle = backgroundColor;
+ this.context.arc(this.centerx, this.centery, this.radius, 0.0001, 6.28, false);
+ this.context.fill();
+
+ /**
+ * Turn off the shadow
+ */
+ RGraph.NoShadow(this);
+
+
+ // Draw a circle
+ this.context.strokeStyle = '#666';
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, false);
+
+ // Now draw a big white circle to make the lines appear as tick marks
+ // This is solely for Chrome
+ this.context.fillStyle = backgroundColor;
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.28, false);
+ this.context.fill();
+
+ /**
+ * Draw more tickmarks
+ */
+ this.context.beginPath();
+ this.context.strokeStyle = '#bbb';
+
+ for (var i=0; i<=360; i+=3) {
+ this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.degrees2Radians(i), false);
+ this.context.lineTo(this.centerx, this.centery);
+ }
+ this.context.stroke();
+
+ this.context.beginPath();
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = 'black';
+
+ // Now draw a big white circle to make the lines appear as tick marks
+ this.context.fillStyle = backgroundColor;
+ this.context.strokeStyle = backgroundColor;
+ this.context.arc(this.centerx, this.centery, this.radius - 5, 0, 6.28, false);
+ this.context.fill();
+ this.context.stroke();
+
+ // Gray lines at 18 degree intervals
+ this.context.beginPath();
+ this.context.strokeStyle = '#ddd';
+ for (var i=0; i<360; i+=18) {
+ this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.degrees2Radians(i), false);
+ this.context.lineTo(this.centerx, this.centery);
+ }
+ this.context.stroke();
+
+ // Redraw the outer circle
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ this.context.arc(this.centerx, this.centery, this.radius, 0, 6.2830, false);
+ this.context.stroke();
+
+ /**
+ * Now draw the center bits shadow if need be
+ */
+ if (this.Get('chart.shadow.inner')) {
+ this.context.beginPath();
+ RGraph.SetShadow(this, this.Get('chart.shadow.inner.color'), this.Get('chart.shadow.inner.offsetx'), this.Get('chart.shadow.inner.offsety'), this.Get('chart.shadow.inner.blur'));
+ this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.label.area'), 0, 6.28, 0);
+ this.context.fill();
+ this.context.stroke();
+
+ /**
+ * Turn off the shadow
+ */
+ RGraph.NoShadow(this);
+ }
+
+ /*******************************************************
+ * Draw the green area
+ *******************************************************/
+ if (this.Get('chart.green.solid')) {
+ var greengrad = this.Get('chart.green.color');
+
+ } else {
+ var greengrad = this.context.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius);
+ greengrad.addColorStop(0, 'white');
+ greengrad.addColorStop(1, this.Get('chart.green.color'));
+ }
+
+ // Draw the "tick highlight"
+ if (this.Get('chart.tickmarks.highlighted')) {
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.strokeStyle = greengrad;
+ this.context.arc(this.centerx, this.centery, this.radius - 2.5,
+
+ -1.57,
+ (((this.Get('chart.green.max') - this.start)/ (this.end - this.start)) * 6.2830) - 1.57,
+ 0);
+
+ this.context.stroke();
+
+ this.context.lineWidth = 1;
+ }
+
+ this.context.beginPath();
+ this.context.fillStyle = greengrad;
+ this.context.arc(
+ this.centerx,
+ this.centery,
+ this.radius - this.Get('chart.label.area'),
+ -1.57,
+ (((this.Get('chart.green.max') - this.start)/ (this.end - this.start)) * 6.2830) - 1.57,
+ false
+ );
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath();
+ this.context.fill();
+
+
+ /*******************************************************
+ * Draw the yellow area
+ *******************************************************/
+ if (this.Get('chart.yellow.solid')) {
+ var yellowgrad = this.Get('chart.yellow.color');
+
+ } else {
+ var yellowgrad = this.context.createRadialGradient(this.centerx,this.centery,0,this.centerx,this.centery,this.radius);
+ yellowgrad.addColorStop(0, 'white');
+ yellowgrad.addColorStop(1, this.Get('chart.yellow.color'));
+ }
+
+ // Draw the "tick highlight"
+ if (this.Get('chart.tickmarks.highlighted')) {
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.strokeStyle = yellowgrad;
+ this.context.arc(this.centerx, this.centery, this.radius - 2.5, (
+
+ ((this.Get('chart.green.max') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,
+ (((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,
+ 0);
+
+ this.context.stroke();
+
+ this.context.lineWidth = 1;
+ }
+
+ this.context.beginPath();
+ this.context.fillStyle = yellowgrad;
+ this.context.arc(
+ this.centerx,
+ this.centery,
+ this.radius - this.Get('chart.label.area'),
+ ( ((this.Get('chart.green.max') -this.start) / (this.end - this.start)) * 6.2830) - 1.57,
+ ( ((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,
+ false
+ );
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath();
+ this.context.fill();
+
+ /*******************************************************
+ * Draw the red area
+ *******************************************************/
+ if (this.Get('chart.red.solid')) {
+ var redgrad = this.Get('chart.red.color');
+
+ } else {
+ var redgrad = this.context.createRadialGradient(this.centerx,
+ this.centery,
+ 0,
+ this.centerx,
+ this.centery,
+ this.radius);
+ redgrad.addColorStop(0, 'white');
+ redgrad.addColorStop(1, this.Get('chart.red.color'));
+ }
+
+
+ // Draw the "tick highlight"
+ if (this.Get('chart.tickmarks.highlighted')) {
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.strokeStyle = redgrad;
+ this.context.arc(this.centerx, this.centery, this.radius - 2.5,(((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,(2 * Math.PI) - (0.5 * Math.PI),0);
+ this.context.stroke();
+
+ this.context.lineWidth = 1;
+ }
+
+ this.context.beginPath();
+ this.context.fillStyle = redgrad;
+ this.context.strokeStyle = redgrad;
+ this.context.arc(
+ this.centerx,
+ this.centery,
+ this.radius - this.Get('chart.label.area'),
+ (((this.Get('chart.red.min') - this.start) / (this.end - this.start)) * 6.2830) - 1.57,
+ 6.2830 - (0.25 * 6.2830),
+ false
+ );
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath();
+ this.context.fill();
+
+
+ /**
+ * Draw the thick border
+ */
+
+ if (this.Get('chart.border')) {
+
+
+ var grad = this.context.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius + 20);
+ grad.addColorStop(0, this.Get('chart.border.color1'));
+ grad.addColorStop(0.5, this.Get('chart.border.color2'));
+ grad.addColorStop(1, this.Get('chart.border.color3'));
+
+
+ this.context.beginPath();
+ this.context.fillStyle = grad;
+ this.context.strokeStyle = 'rgba(0,0,0,0)'
+ this.context.lineWidth = 0.001;
+ this.context.arc(this.centerx, this.centery, this.radius + 20, 0, 6.2830, 0);
+ this.context.arc(this.centerx, this.centery, this.radius - 2, 6.2830, 0, 1);
+ this.context.fill();
+ }
+
+ // Put the linewidth back to what it was
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+
+ /**
+ * Draw the title if specified
+ */
+ if (this.Get('chart.title')) {
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ this.centery - this.radius,
+ null,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+ }
+
+
+ // Draw the big tick marks
+ if (!this.Get('chart.tickmarks.highlighted')) {
+ for (var i=18; i<=360; i+=36) {
+ this.context.beginPath();
+ this.context.strokeStyle = '#999';
+ this.context.lineWidth = 2;
+ this.context.arc(this.centerx, this.centery, this.radius - 1, RGraph.degrees2Radians(i), RGraph.degrees2Radians(i+0.01), false);
+ this.context.arc(this.centerx, this.centery, this.radius - 7, RGraph.degrees2Radians(i), RGraph.degrees2Radians(i+0.01), false);
+ this.context.stroke();
+ }
+ }
+ }
+
+
+ /**
+ * Draws the needle of the odometer
+ *
+ * @param number value The value to represent
+ * @param string color The color of the needle
+ * @param number The OPTIONAL length of the needle
+ */
+ RGraph.Odometer.prototype.DrawNeedle = function (value, color)
+ {
+ // The optional length of the needle
+ var length = arguments[2] ? arguments[2] : this.radius - this.Get('chart.label.area');
+
+ // ===== First draw a grey background circle =====
+
+ this.context.fillStyle = '#999';
+
+ this.context.beginPath();
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery, 10, 0, 6.28, false);
+ this.context.fill();
+ this.context.closePath();
+
+ this.context.fill();
+
+ // ===============================================
+
+ this.context.fillStyle = color
+ this.context.strokeStyle = '#666';
+
+ // Draw the centre bit
+ this.context.beginPath();
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery, 8, 0, 6.28, false);
+ this.context.fill();
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ if (this.Get('chart.needle.type') == 'pointer') {
+
+ this.context.strokeStyle = color;
+ this.context.lineWidth = this.Get('chart.needle.width');
+ this.context.lineCap = 'round';
+ this.context.lineJoin = 'round';
+
+ // Draw the needle
+ this.context.beginPath();
+ // The trailing bit on the opposite side of the dial
+ this.context.beginPath();
+ this.context.moveTo(this.centerx, this.centery);
+
+ if (this.Get('chart.needle.tail')) {
+
+ this.context.arc(this.centerx,
+ this.centery,
+ 20,
+ (((value / this.range) * 360) + 90) / 57.3,
+ (((value / this.range) * 360) + 90 + 0.01) / 57.3, // The 0.01 avoids a bug in ExCanvas and Chrome 6
+ false
+ );
+ }
+
+ // Draw the long bit on the opposite side
+ this.context.arc(this.centerx,
+ this.centery,
+ length - 10,
+ (((value / this.range) * 360) - 90) / 57.3,
+ (((value / this.range) * 360) - 90 + 0.1 ) / 57.3, // The 0.1 avoids a bug in ExCanvas and Chrome 6
+ false
+ );
+ this.context.closePath();
+
+ //this.context.stroke();
+ //this.context.fill();
+
+
+ } else if (this.Get('chart.needle.type') == 'triangle') {
+
+ this.context.lineWidth = 0.01;
+ this.context.lineEnd = 'square';
+ this.context.lineJoin = 'miter';
+
+ /**
+ * This draws the version of the pointer that becomes the border
+ */
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.needle.triangle.border');
+ this.context.arc(this.centerx, this.centery, 11, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0);
+ this.context.arc(this.centerx, this.centery, 11, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0);
+ this.context.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
+ this.context.closePath();
+ this.context.fill();
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, 15, 0, 6.28, 0);
+ this.context.closePath();
+ this.context.fill();
+
+ // This draws the pointer
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+ this.context.fillStyle = color;
+ this.context.arc(this.centerx, this.centery, 7, (((value / this.range) * 360)) / 57.3, ((((value / this.range) * 360)) + 0.01) / 57.3, 0);
+ this.context.arc(this.centerx, this.centery, 7, (((value / this.range) * 360) + 180) / 57.3, ((((value / this.range) * 360) + 180) + 0.01)/ 57.3, 0);
+ this.context.arc(this.centerx, this.centery, length - 13, (((value / this.range) * 360) - 90) / 57.3, ((((value / this.range) * 360) - 90) / 57.3) + 0.01, 0);
+ this.context.closePath();
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * This is here to accomodate the MSIE/ExCanvas combo
+ */
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, 7, 0, 6.28, 0);
+ this.context.closePath();
+ this.context.fill();
+ }
+
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Draw the mini center circle
+ this.context.beginPath();
+ this.context.fillStyle = color;
+ this.context.arc(this.centerx, this.centery, this.Get('chart.needle.type') == 'pointer' ? 7 : 12, 0.01, 6.2830, false);
+ this.context.fill();
+
+ // This draws the arrow at the end of the line
+ if (this.Get('chart.needle.head') && this.Get('chart.needle.type') == 'pointer') {
+ this.context.lineWidth = 1;
+ this.context.fillStyle = color;
+
+ // round, bevel, miter
+ this.context.lineJoin = 'miter';
+ this.context.lineCap = 'butt';
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, length - 5, (((value / this.range) * 360) - 90) / 57.3, (((value / this.range) * 360) - 90 + 0.1) / 57.3, false);
+
+ this.context.arc(this.centerx,
+ this.centery,
+ length - 20,
+ RGraph.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 80 : 85) ),
+ RGraph.degrees2Radians( ((value / this.range) * 360) - (length < 60 ? 100 : 95) ),
+ 1);
+ this.context.closePath();
+
+ this.context.fill();
+ //this.context.stroke();
+ }
+
+ /**
+ * Draw a white circle at the centre
+ */
+ this.context.beginPath();
+ this.context.fillStyle = 'gray';
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx,this.centery,2,0,6.2795,false);
+ this.context.closePath();
+
+ this.context.fill();
+ }
+
+ /**
+ * Draws the labels for the Odo
+ */
+ RGraph.Odometer.prototype.DrawLabels = function ()
+ {
+ var context = this.context;
+ var size = this.Get('chart.text.size');
+ var font = this.Get('chart.text.font');
+ var centerx = this.centerx;
+ var centery = this.centery;
+ var r = this.radius - (this.Get('chart.label.area') / 2);
+ var start = this.start;
+ var end = this.end;
+ var decimals = this.Get('chart.scale.decimals');
+ var labels = this.Get('chart.labels');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+
+ context.beginPath();
+ context.fillStyle = this.Get('chart.text.color');
+
+ /**
+ * If label are specified, use those
+ */
+ if (labels) {
+ for (var i=0; i<labels.length; ++i) {
+
+ RGraph.Text(context,
+ font,
+ size,
+ centerx + (Math.cos(((i / labels.length) * 6.28) - 1.57) * (this.radius - (this.Get('chart.label.area') / 2) ) ), // Sin A = Opp / Hyp
+ centery + (Math.sin(((i / labels.length) * 6.28) - 1.57) * (this.radius - (this.Get('chart.label.area') / 2) ) ), // Cos A = Adj / Hyp
+ String(units_pre + labels[i] + units_post),
+ 'center',
+ 'center');
+ }
+
+ /**
+ * If not, use the maximum value
+ */
+ } else {
+ RGraph.Text(context, font, size, centerx + (0.588 * r ), centery - (0.809 * r ), String(units_pre + (((end - start) * (1/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 36);
+ RGraph.Text(context, font, size, centerx + (0.951 * r ), centery - (0.309 * r), String(units_pre + (((end - start) * (2/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 72);
+ RGraph.Text(context, font, size, centerx + (0.949 * r), centery + (0.287 * r), String(units_pre + (((end - start) * (3/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 108);
+ RGraph.Text(context, font, size, centerx + (0.588 * r ), centery + (0.809 * r ), String(units_pre + (((end - start) * (4/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 144);
+ RGraph.Text(context, font, size, centerx, centery + r, String(units_pre + (((end - start) * (5/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 180);
+ RGraph.Text(context, font, size, centerx - (0.588 * r ), centery + (0.809 * r ), String(units_pre + (((end - start) * (6/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 216);
+ RGraph.Text(context, font, size, centerx - (0.949 * r), centery + (0.300 * r), a = String(units_pre + (((end - start) * (7/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 252);
+ RGraph.Text(context, font, size, centerx - (0.951 * r), centery - (0.309 * r), String(units_pre + (((end - start) * (8/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 288);
+ RGraph.Text(context, font, size, centerx - (0.588 * r ), centery - (0.809 * r ), String(units_pre + (((end - start) * (9/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 324);
+ RGraph.Text(context, font, size, centerx, centery - r, this.Get('chart.zerostart') ? units_pre + String(this.start) + units_post : String(units_pre + (((end - start) * (10/10)) + start).toFixed(decimals) + units_post), 'center', 'center', false, 360);
+ }
+
+ this.context.fill();
+
+ /**
+ * Draw the text label below the center point
+ */
+ if (this.Get('chart.value.text')) {
+ context.strokeStyle = 'black';
+ RGraph.Text(context, font, size + 2, centerx, centery + size + 2 + 10, String(this.Get('chart.value.units.pre') + this.value.toFixed(this.Get('chart.value.text.decimals')) + this.Get('chart.value.units.post')), 'center', 'center', true, null, 'white');
+ }
+ }
diff --git a/schall/static/RGraph/libraries/RGraph.pie.js b/schall/static/RGraph/libraries/RGraph.pie.js
new file mode 100644
index 0000000..26f688e
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.pie.js
@@ -0,0 +1,1042 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The pie chart constructor
+ *
+ * @param data array The data to be represented on the pie chart
+ */
+ RGraph.Pie = function (id, data)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext("2d");
+ this.canvas.__object__ = this;
+ this.total = 0;
+ this.subTotal = 0;
+ this.angles = [];
+ this.data = data;
+ this.properties = [];
+ this.type = 'pie';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+ this.properties = {
+ 'chart.colors': ['rgb(255,0,0)', '#ddd', 'rgb(0,255,0)', 'rgb(0,0,255)', 'pink', 'yellow', '#000'],
+ 'chart.strokestyle': '#999',
+ 'chart.linewidth': 1,
+ 'chart.labels': [],
+ 'chart.labels.sticks': false,
+ 'chart.labels.sticks.length': 7,
+ 'chart.labels.sticks.color': '#aaa',
+ 'chart.segments': [],
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': 0.5,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.shadow': false,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.tooltips': [],
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.style': '3d',
+ 'chart.highlight.style.2d.fill': 'rgba(255,255,255,0.5)',
+ 'chart.highlight.style.2d.stroke': 'rgba(255,255,255,0)',
+ 'chart.centerx': null,
+ 'chart.centery': null,
+ 'chart.radius': null,
+ 'chart.border': false,
+ 'chart.border.color': 'rgba(255,255,255,0.5)',
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.align': 'center',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.variant': 'pie',
+ 'chart.variant.donut.color': 'white',
+ 'chart.exploded': [],
+ 'chart.effect.roundrobin.multiplier': 1
+ }
+
+ /**
+ * Calculate the total
+ */
+ for (var i=0,len=data.length; i<len; i++) {
+ this.total += data[i];
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getSegment;
+ }
+
+
+ /**
+ * A generic setter
+ */
+ RGraph.Pie.prototype.Set = function (name, value)
+ {
+ if (name == 'chart.highlight.style.2d.color') {
+ name = 'chart.highlight.style.2d.fill';
+ }
+
+ this.properties[name] = value;
+ }
+
+
+ /**
+ * A generic getter
+ */
+ RGraph.Pie.prototype.Get = function (name)
+ {
+ if (name == 'chart.highlight.style.2d.color') {
+ name = 'chart.highlight.style.2d.fill';
+ }
+
+ return this.properties[name];
+ }
+
+
+ /**
+ * This draws the pie chart
+ */
+ RGraph.Pie.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Reset this to an empty array
+ */
+ this.Set('chart.segments', []);
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+ this.radius = this.Get('chart.radius') ? this.Get('chart.radius') : this.getRadius();
+ // this.centerx now defined below
+ this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+ this.subTotal = 0;
+ this.angles = [];
+
+ /**
+ * Allow the specification of a custom centery
+ */
+ if (typeof(this.Get('chart.centery')) == 'number') {
+ this.centery = this.Get('chart.centery');
+ }
+
+ /**
+ * Alignment (Pie is center aligned by default) Only if centerx is not defined - donut defines the centerx
+ */
+ if (this.Get('chart.align') == 'left') {
+ this.centerx = this.radius + this.gutterLeft;
+
+ } else if (this.Get('chart.align') == 'right') {
+ this.centerx = this.canvas.width - this.radius - this.gutterRight;
+
+ } else {
+ this.centerx = this.canvas.width / 2;
+ }
+
+ /**
+ * Allow the specification of a custom centerx
+ */
+ if (typeof(this.Get('chart.centerx')) == 'number') {
+ this.centerx = this.Get('chart.centerx');
+ }
+
+ /**
+ * Draw the shadow if required
+ */
+ if (this.Get('chart.shadow') && 0) {
+
+ var offsetx = document.all ? this.Get('chart.shadow.offsetx') : 0;
+ var offsety = document.all ? this.Get('chart.shadow.offsety') : 0;
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.shadow.color');
+
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+
+ this.context.arc(this.centerx + offsetx, this.centery + offsety, this.radius, 0, 6.28, 0);
+
+ this.context.fill();
+
+ // Now turn off the shadow
+ RGraph.NoShadow(this);
+ }
+
+ /**
+ * The total of the array of values
+ */
+ this.total = RGraph.array_sum(this.data);
+
+ for (var i=0,len=this.data.length; i<len; i++) {
+
+ var angle = ((this.data[i] / this.total) * 360);
+
+ // Draw the segment
+ this.DrawSegment(angle,this.Get('chart.colors')[i],i == (this.data.length - 1), i);
+ }
+
+ RGraph.NoShadow(this);
+
+
+ /**
+ * Redraw the seperating lines
+ */
+ this.DrawBorders();
+
+ /**
+ * Now draw the segments again with shadow turned off. This is always performed,
+ * not just if the shadow is on.
+ */
+
+ for (var i=0; i<this.angles.length; i++) {
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ this.context.moveTo(this.angles[i][2], this.angles[i][3]);
+ this.context.arc(this.angles[i][2],
+ this.angles[i][3],
+ this.radius,
+ (this.angles[i][0] / 57.3),
+ (this.angles[i][1] / 57.3),
+ false);
+ this.context.lineTo(this.angles[i][2], this.angles[i][3]);
+ this.context.closePath();
+ this.context.fill();
+ this.context.stroke();
+ }
+
+
+
+ /**
+ * Draw label sticks
+ */
+ if (this.Get('chart.labels.sticks')) {
+
+ this.DrawSticks();
+
+ // Redraw the border going around the Pie chart if the stroke style is NOT white
+ var strokeStyle = this.Get('chart.strokestyle');
+ var isWhite = strokeStyle == 'white' || strokeStyle == '#fff' || strokeStyle == '#fffffff' || strokeStyle == 'rgb(255,255,255)' || strokeStyle == 'rgba(255,255,255,0)';
+
+ if (!isWhite || (isWhite && this.Get('chart.shadow'))) {
+ // Again (?)
+ this.DrawBorders();
+ }
+ }
+
+ /**
+ * Draw the labels
+ */
+ this.DrawLabels();
+
+ /**
+ * Draw the title
+ */
+ if (this.Get('chart.align') == 'left') {
+ var centerx = this.radius + this.Get('chart.gutter.left');
+
+ } else if (this.Get('chart.align') == 'right') {
+ var centerx = RGraph.GetWidth(this) - (this.radius + this.gutterRight);
+
+ } else {
+ var centerx = null;
+ }
+
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ (this.canvas.height / 2) - this.radius - 5,
+ centerx,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Tooltips
+ */
+ if (this.Get('chart.tooltips').length) {
+
+ /**
+ * Register this object for redrawing
+ */
+ RGraph.Register(this);
+
+ /**
+ * The onclick event
+ */
+ //this.canvas.onclick = function (e)
+ var canvas_onclick_func = function (e)
+ {
+ RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = e.target.__object__;
+
+
+
+ /**
+ * If it's actually a donut make sure the hyp is bigger
+ * than the size of the hole in the middle
+ */
+ if (obj.Get('chart.variant') == 'donut' && Math.abs(hyp) < (obj.radius / 2)) {
+ return;
+ }
+
+ /**
+ * The angles for each segment are stored in "angles",
+ * so go through that checking if the mouse position corresponds
+ */
+ var isDonut = obj.Get('chart.variant') == 'donut';
+ var hStyle = obj.Get('chart.highlight.style');
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+
+ var x = mouseCoords[0] - segment[0];
+ var y = mouseCoords[1] - segment[1];
+ var theta = Math.atan(y / x); // RADIANS
+ var hyp = y / Math.sin(theta);
+
+
+ if ( RGraph.Registry.Get('chart.tooltip')
+ && segment[5] == RGraph.Registry.Get('chart.tooltip').__index__
+ && RGraph.Registry.Get('chart.tooltip').__canvas__.__object__.id == obj.id) {
+
+ return;
+
+ } else {
+ RGraph.Redraw();
+ }
+
+
+ if (isDonut || hStyle == '2d') {
+
+ context.beginPath();
+
+ context.strokeStyle = obj.Get('chart.highlight.style.2d.stroke');
+ context.fillStyle = obj.Get('chart.highlight.style.2d.fill');
+
+ //context.moveTo(obj.centerx, obj.centery);
+
+ context.moveTo(segment[0], segment[1]);
+ context.arc(segment[0], segment[1], segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0);
+ context.lineTo(segment[0], segment[1]);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ //Removed 7th December 2010
+ //context.stroke();
+
+ } else if (hStyle == 'explode') {
+
+ var exploded = [];
+
+ exploded[segment[5]] = 0;
+
+ RGraph.Registry.Set('chart.pie.exploded', obj);
+
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 25);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 50);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 75);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 100);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 125);
+
+ setTimeout(function () {RGraph.Registry.Get('chart.pie.exploded').Set('chart.exploded', []);}, 150);
+
+ } else {
+
+ context.lineWidth = 2;
+
+ /**
+ * Draw a white segment where the one that has been clicked on was
+ */
+ context.fillStyle = 'white';
+ context.strokeStyle = 'white';
+ context.beginPath();
+ context.moveTo(segment[0], segment[1]);
+ context.arc(segment[0], segment[1], segment[2], obj.angles[segment[5]][0] / 57.3, obj.angles[segment[5]][1] / 57.3, 0);
+ context.stroke();
+ context.fill();
+
+ context.lineWidth = 1;
+
+ context.shadowColor = '#666';
+ context.shadowBlur = 3;
+ context.shadowOffsetX = 3;
+ context.shadowOffsetY = 3;
+
+ // Draw the new segment
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.colors')[segment[5]];
+ context.strokeStyle = obj.Get('chart.strokestyle');
+ context.moveTo(segment[0] - 3, segment[1] - 3);
+ context.arc(segment[0] - 3, segment[1] - 3, segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0);
+ context.lineTo(segment[0] - 3, segment[1] - 3);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ // Turn off the shadow
+ RGraph.NoShadow(obj);
+
+ /**
+ * If a border is defined, redraw that
+ */
+ if (obj.Get('chart.border')) {
+ context.beginPath();
+ context.strokeStyle = obj.Get('chart.border.color');
+ context.lineWidth = 5;
+ context.arc(segment[0] - 3, segment[1] - 3, obj.radius - 2, RGraph.degrees2Radians(obj.angles[i][0]), RGraph.degrees2Radians(obj.angles[i][1]), 0);
+ context.stroke();
+ }
+ }
+
+ /**
+ * If a tooltip is defined, show it
+ */
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), segment[5]);
+
+ if (text) {
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[5]);
+ }
+
+ /**
+ * Need to redraw the key?
+ */
+ if (obj.Get('chart.key') && obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') {
+ RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
+ }
+
+ e.stopPropagation();
+
+ return;
+ } else if (obj.Get('chart.tooltips.event') == 'onclick') {
+ RGraph.Redraw();
+ }
+ }
+ var event_name = this.Get('chart.tooltips.event') == 'onmousemove' ? 'mousemove' : 'click';
+
+ this.canvas.addEventListener(event_name, canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, event_name, canvas_onclick_func);
+
+
+
+
+
+
+ /**
+ * The onmousemove event for changing the cursor
+ */
+ //this.canvas.onmousemove = function (e)
+ var canvas_onmousemove_func = function (e)
+ {
+ RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+ e.target.style.cursor = 'pointer';
+
+ return;
+ }
+
+ /**
+ * Put the cursor back to null
+ */
+ e.target.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+
+
+
+
+
+
+
+
+
+ /**
+ * The window onclick function
+ */
+ var window_onclick_func = function (e)
+ {
+ // Taken out on 02/10/11
+ //RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ RGraph.Redraw();
+
+ /**
+ * Put the cursor back to null
+ */
+ //e.target.style.cursor = 'default';
+ }
+ window.addEventListener('click', window_onclick_func, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func);
+ }
+
+
+ /**
+ * If a border is pecified, draw it
+ */
+ if (this.Get('chart.border')) {
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.strokeStyle = this.Get('chart.border.color');
+
+ this.context.arc(this.centerx,
+ this.centery,
+ this.radius - 2,
+ 0,
+ 6.28,
+ 0);
+
+ this.context.stroke();
+ }
+
+ /**
+ * Draw the kay if desired
+ */
+ if (this.Get('chart.key') != null) {
+ //this.Set('chart.key.position', 'graph');
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+
+ /**
+ * If this is actually a donut, draw a big circle in the middle
+ */
+ if (this.Get('chart.variant') == 'donut') {
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.variant.donut.color');
+ this.context.arc(this.centerx, this.centery, this.radius / 2, 0, 6.28, 0);
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ RGraph.NoShadow(this);
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws a single segment of the pie chart
+ *
+ * @param int degrees The number of degrees for this segment
+ */
+ RGraph.Pie.prototype.DrawSegment = function (degrees, color, last, index)
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+ var subTotal = this.subTotal;
+ degrees = degrees * this.Get('chart.effect.roundrobin.multiplier');
+
+ context.beginPath();
+
+ context.fillStyle = color;
+ context.strokeStyle = this.Get('chart.strokestyle');
+ context.lineWidth = 0;
+
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'),this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ /**
+ * Exploded segments
+ */
+ if ( (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[index] > 0) || typeof(this.Get('chart.exploded')) == 'number') {
+ var explosion = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[index];
+ var x = 0;
+ var y = 0;
+ var h = explosion;
+ var t = ((subTotal + (degrees / 2)) / (360/6.2830)) - 1.57;
+ var x = (Math.cos(t) * explosion);
+ var y = (Math.sin(t) * explosion);
+
+ this.context.moveTo(this.centerx + x, this.centery + y);
+ } else {
+ var x = 0;
+ var y = 0;
+ }
+
+ /**
+ * Calculate the angles
+ */
+ var startAngle = ((subTotal / 57.3)) - 1.57;
+ var endAngle = (((subTotal + degrees) / 57.3)) - 1.57;
+
+ context.arc(this.centerx + x,
+ this.centery + y,
+ this.radius,
+ startAngle,
+ endAngle,
+ 0);
+
+ context.lineTo(this.centerx + x, this.centery + y);
+
+ // Keep hold of the angles
+ this.angles.push([subTotal - 90, subTotal + degrees - 90, this.centerx + x, this.centery + y])
+ this.context.closePath();
+
+
+
+ //this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Calculate the segment angle
+ */
+ this.Get('chart.segments').push([subTotal, subTotal + degrees]);
+ this.subTotal += degrees;
+ }
+
+ /**
+ * Draws the graphs labels
+ */
+ RGraph.Pie.prototype.DrawLabels = function ()
+ {
+ var hAlignment = 'left';
+ var vAlignment = 'center';
+ var labels = this.Get('chart.labels');
+ var context = this.context;
+
+ /**
+ * Turn the shadow off
+ */
+ RGraph.NoShadow(this);
+
+ context.fillStyle = 'black';
+ context.beginPath();
+
+ /**
+ * Draw the key (ie. the labels)
+ */
+ if (labels && labels.length) {
+
+ var text_size = this.Get('chart.text.size');
+
+ for (i=0; i<labels.length; ++i) {
+
+ /**
+ * T|his ensures that if we're given too many labels, that we don't get an error
+ */
+ if (typeof(this.Get('chart.segments')[i]) == 'undefined') {
+ continue;
+ }
+
+ // Move to the centre
+ context.moveTo(this.centerx,this.centery);
+
+ var a = this.Get('chart.segments')[i][0] + ((this.Get('chart.segments')[i][1] - this.Get('chart.segments')[i][0]) / 2) - 90;
+
+ /**
+ * Alignment
+ */
+ if (a < 90) {
+ hAlignment = 'left';
+ vAlignment = 'center';
+ } else if (a < 180) {
+ hAlignment = 'right';
+ vAlignment = 'center';
+ } else if (a < 270) {
+ hAlignment = 'right';
+ vAlignment = 'center';
+ } else if (a < 360) {
+ hAlignment = 'left';
+ vAlignment = 'center';
+ }
+
+var angle = ((this.angles[i][1] - this.angles[i][0]) / 2) + this.angles[i][0];
+ /**
+ * Handle the additional "explosion" offset
+ */
+ if (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[i] || typeof(this.Get('chart.exploded')) == 'number') {
+
+ var t = ((this.angles[i][1] - this.angles[i][0]) / 2) / (360/6.2830);
+ var seperation = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[i];
+
+ // Adjust the angles
+ var explosion_offsetx = (Math.cos(angle / 57.29) * seperation);
+ var explosion_offsety = (Math.sin(angle / 57.29) * seperation);
+ } else {
+ var explosion_offsetx = 0;
+ var explosion_offsety = 0;
+ }
+
+ /**
+ * Allow for the label sticks
+ */
+ if (this.Get('chart.labels.sticks')) {
+ explosion_offsetx += (Math.cos(angle / 57.29) * this.Get('chart.labels.sticks.length'));
+ explosion_offsety += (Math.sin(angle / 57.29) * this.Get('chart.labels.sticks.length'));
+ }
+
+
+ context.fillStyle = this.Get('chart.text.color');
+
+ RGraph.Text(context,
+ this.Get('chart.text.font'),
+ text_size,
+ this.centerx + explosion_offsetx + ((this.radius + 10)* Math.cos(a / 57.3)) + (this.Get('chart.labels.sticks') ? (a < 90 || a > 270 ? 2 : -2) : 0),
+ this.centery + explosion_offsety + (((this.radius + 10) * Math.sin(a / 57.3))),
+ labels[i],
+ vAlignment,
+ hAlignment);
+ }
+
+ context.fill();
+ }
+ }
+
+
+ /**
+ * This function draws the pie chart sticks (for the labels)
+ */
+ RGraph.Pie.prototype.DrawSticks = function ()
+ {
+ var context = this.context;
+ var offset = this.Get('chart.linewidth') / 2;
+ var exploded = this.Get('chart.exploded');
+ var sticks = this.Get('chart.labels.sticks');
+
+ for (var i=0; i<this.angles.length; ++i) {
+
+ // This allows the chart.labels.sticks to be an array as well as a boolean
+ if (typeof(sticks) == 'object' && !sticks[i]) {
+ continue;
+ }
+
+ var degrees = this.angles[i][1] - this.angles[i][0];
+
+ context.beginPath();
+ context.strokeStyle = this.Get('chart.labels.sticks.color');
+ context.lineWidth = 1;
+
+ var midpoint = (this.angles[i][0] + (degrees / 2)) / 57.3;
+
+ if (typeof(exploded) == 'object' && exploded[i]) {
+ var extra = exploded[i];
+ } else if (typeof(exploded) == 'number') {
+ var extra = exploded;
+ } else {
+ var extra = 0;
+ }
+
+ context.lineJoin = 'round';
+ context.lineWidth = 1;
+
+ context.arc(this.centerx,
+ this.centery,
+ this.radius + this.Get('chart.labels.sticks.length') + extra,
+ midpoint,
+ midpoint + 0.001,
+ 0);
+ context.arc(this.centerx,
+ this.centery,
+ this.radius + extra,
+ midpoint,
+ midpoint + 0.001,
+ 0);
+
+ context.stroke();
+ }
+ }
+
+
+ /**
+ * The (now Pie chart specific) getSegment function
+ *
+ * @param object e The event object
+ */
+ RGraph.Pie.prototype.getSegment = function (e)
+ {
+ RGraph.FixEventObject(e);
+
+ // The optional arg provides a way of allowing some accuracy (pixels)
+ var accuracy = arguments[1] ? arguments[1] : 0;
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var r = obj.radius;
+ var angles = obj.angles;
+ var ret = [];
+
+ for (var i=0; i<angles.length; ++i) {
+
+ var x = mouseCoords[0] - angles[i][2];
+ var y = mouseCoords[1] - angles[i][3];
+ var theta = Math.atan(y / x); // RADIANS
+ var hyp = y / Math.sin(theta);
+ var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy;
+ // Put theta in DEGREES
+ theta *= 57.3
+
+ /**
+ * Account for the correct quadrant
+ */
+ if (x < 0 && y >= 0) {
+ theta += 180;
+ } else if (x < 0 && y < 0) {
+ theta += 180;
+ }
+
+ if (theta > 360) {
+ theta -= 360;
+ }
+
+ if (theta >= angles[i][0] && theta < angles[i][1]) {
+
+ hyp = Math.abs(hyp);
+
+ if (!hyp || (obj.radius && hyp > obj.radius) ) {
+ return null;
+ }
+
+ if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) {
+ return null;
+ }
+
+ ret[0] = angles[i][2];
+ ret[1] = angles[i][3];
+ ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius;
+ ret[3] = angles[i][0];
+ ret[4] = angles[i][1];
+ ret[5] = i;
+
+
+
+ if (ret[3] < 0) ret[3] += 360;
+ if (ret[4] > 360) ret[4] -= 360;
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+
+ RGraph.Pie.prototype.DrawBorders = function ()
+ {
+ if (this.Get('chart.linewidth') > 0) {
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ for (var i=0,len=this.angles.length; i<len; ++i) {
+ this.context.beginPath();
+ this.context.moveTo(this.angles[i][2], this.angles[i][3]);
+ this.context.arc(this.angles[i][2],
+ this.angles[i][3],
+ this.radius,
+ ((this.angles[i][0] / 57.3)),
+ ((this.angles[i][0] + 0.01) / 57.3),
+ 0);
+ this.context.arc(this.angles[i][2],
+ this.angles[i][3],
+ this.radius,
+ ((this.angles[i][0] / 57.3)),
+ this.angles[i][1] / 57.3,
+ 0);
+ this.context.closePath();
+
+ this.context.stroke();
+ }
+ }
+ }
+
+
+ /**
+ * Returns the radius of the pie chart
+ */
+ RGraph.Pie.prototype.getRadius = function ()
+ {
+ return Math.min(this.canvas.height - this.gutterTop - this.gutterBottom, this.canvas.width - this.gutterLeft - this.gutterRight) / 2;
+ }
+
+
+ /**
+ * A programmatic explode function
+ *
+ * @param object obj The chart object
+ * @param number index The zero-indexed number of the segment
+ * @param number size The size (in pixels) of the explosion
+ */
+ RGraph.Pie.prototype.Explode = function (index, size)
+ {
+ if (this.Get('chart.exploded') == 0) {
+ this.Set('chart.exploded', []);
+ }
+
+ var obj = this;
+
+ for (var i=0; i<size; ++i) {
+ setTimeout(
+ function ()
+ {
+ var exploded = obj.Get('chart.exploded');
+ var value = typeof(exploded[index]) == 'number' ? exploded[index] : 0;
+ exploded[index] = value + 1;
+ obj.Set('chart.exploded', exploded);
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+ }, i * 16.666);
+ }
+
+ /**
+ * Now set the property accordingly
+ */
+ setTimeout(
+ function ()
+ {
+ obj.Set('chart.exploded', exploded);
+ }, i * 16.666
+ )
+
+ //var Reset = function ()
+ //{
+ // pie.Set('chart.exploded', []);
+ // RGraph.Clear(pie.canvas);
+ // pie.Draw();
+ //}
+ //window.addEventListener('click', Reset, false);
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.pie.js.old b/schall/static/RGraph/libraries/RGraph.pie.js.old
new file mode 100644
index 0000000..8b9dc5b
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.pie.js.old
@@ -0,0 +1,975 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The pie chart constructor
+ *
+ * @param data array The data to be represented on the pie chart
+ */
+ RGraph.Pie = function (id, data)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext("2d");
+ this.canvas.__object__ = this;
+ this.total = 0;
+ this.subTotal = 0;
+ this.angles = [];
+ this.data = data;
+ this.properties = [];
+ this.type = 'pie';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+ this.properties = {
+ 'chart.colors': ['rgb(255,0,0)', '#ddd', 'rgb(0,255,0)', 'rgb(0,0,255)', 'pink', 'yellow', '#000'],
+ 'chart.strokestyle': '#999',
+ 'chart.linewidth': 1,
+ 'chart.labels': [],
+ 'chart.labels.sticks': false,
+ 'chart.labels.sticks.color': '#aaa',
+ 'chart.segments': [],
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': 0.5,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.shadow': false,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.tooltips': [],
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.style': '3d',
+ 'chart.highlight.style.2d.fill': 'rgba(255,255,255,0.5)',
+ 'chart.highlight.style.2d.stroke': 'rgba(255,255,255,0)',
+ 'chart.centerx': null,
+ 'chart.centery': null,
+ 'chart.radius': null,
+ 'chart.border': false,
+ 'chart.border.color': 'rgba(255,255,255,0.5)',
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.align': 'center',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.variant': 'pie',
+ 'chart.variant.donut.color': 'white',
+ 'chart.exploded': [],
+ 'chart.effect.roundrobin.multiplier': 1
+ }
+
+ /**
+ * Calculate the total
+ */
+ for (var i=0,len=data.length; i<len; i++) {
+ this.total += data[i];
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getSegment;
+ }
+
+
+ /**
+ * A generic setter
+ */
+ RGraph.Pie.prototype.Set = function (name, value)
+ {
+ if (name == 'chart.highlight.style.2d.color') {
+ name = 'chart.highlight.style.2d.fill';
+ }
+
+ this.properties[name] = value;
+ }
+
+
+ /**
+ * A generic getter
+ */
+ RGraph.Pie.prototype.Get = function (name)
+ {
+ if (name == 'chart.highlight.style.2d.color') {
+ name = 'chart.highlight.style.2d.fill';
+ }
+
+ return this.properties[name];
+ }
+
+
+ /**
+ * This draws the pie chart
+ */
+ RGraph.Pie.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Reset this to an empty array
+ */
+ this.Set('chart.segments', []);
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+ this.radius = this.Get('chart.radius') ? this.Get('chart.radius') : this.getRadius();
+ // this.centerx now defined below
+ this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+ this.subTotal = 0;
+ this.angles = [];
+
+ /**
+ * Allow the specification of a custom centery
+ */
+ if (typeof(this.Get('chart.centery')) == 'number') {
+ this.centery = this.Get('chart.centery');
+ }
+
+ /**
+ * Alignment (Pie is center aligned by default) Only if centerx is not defined - donut defines the centerx
+ */
+ if (this.Get('chart.align') == 'left') {
+ this.centerx = this.radius + this.gutterLeft;
+
+ } else if (this.Get('chart.align') == 'right') {
+ this.centerx = this.canvas.width - this.radius - this.gutterRight;
+
+ } else {
+ this.centerx = this.canvas.width / 2;
+ }
+
+ /**
+ * Allow the specification of a custom centerx
+ */
+ if (typeof(this.Get('chart.centerx')) == 'number') {
+ this.centerx = this.Get('chart.centerx');
+ }
+
+ /**
+ * Draw the shadow if required
+ */
+ if (this.Get('chart.shadow') && 0) {
+
+ var offsetx = document.all ? this.Get('chart.shadow.offsetx') : 0;
+ var offsety = document.all ? this.Get('chart.shadow.offsety') : 0;
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.shadow.color');
+
+ this.context.shadowColor = this.Get('chart.shadow.color');
+ this.context.shadowBlur = this.Get('chart.shadow.blur');
+ this.context.shadowOffsetX = this.Get('chart.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.shadow.offsety');
+
+ this.context.arc(this.centerx + offsetx, this.centery + offsety, this.radius, 0, 6.28, 0);
+
+ this.context.fill();
+
+ // Now turn off the shadow
+ RGraph.NoShadow(this);
+ }
+
+ /**
+ * The total of the array of values
+ */
+ this.total = RGraph.array_sum(this.data);
+
+ for (var i=0,len=this.data.length; i<len; i++) {
+ var angle = (this.data[i] / this.total) * 360;
+
+ // Draw the segment
+ this.DrawSegment(angle,this.Get('chart.colors')[i],i == (this.data.length - 1), i);
+ }
+
+ RGraph.NoShadow(this);
+
+
+ /**
+ * Redraw the seperating lines
+ */
+ this.DrawBorders();
+
+ /**
+ * Now draw the segments again with shadow turned off. This is always performed,
+ * not just if the shadow is on.
+ */
+ for (var i=0; i<this.angles.length; i++) {
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ this.context.moveTo(this.angles[i][2], this.angles[i][3]);
+ this.context.arc(this.angles[i][2],
+ this.angles[i][3],
+ this.radius,
+ (this.angles[i][0] / 57.3) * this.Get('chart.effect.roundrobin.multiplier'),
+ (this.angles[i][1] / 57.3) * this.Get('chart.effect.roundrobin.multiplier'),
+ false);
+ this.context.lineTo(this.angles[i][2], this.angles[i][3]);
+ this.context.closePath();
+ this.context.fill();
+ this.context.stroke();
+ }
+
+ /**
+ * Draw label sticks
+ */
+ if (this.Get('chart.labels.sticks')) {
+ this.DrawSticks();
+
+ // Redraw the border going around the Pie chart if the stroke style is NOT white
+ if (
+ this.Get('chart.strokestyle') != 'white'
+ && this.Get('chart.strokestyle') != '#fff'
+ && this.Get('chart.strokestyle') != '#fffffff'
+ && this.Get('chart.strokestyle') != 'rgb(255,255,255)'
+ && this.Get('chart.strokestyle') != 'rgba(255,255,255,0)'
+ ) {
+
+ // Again (?)
+ this.DrawBorders();
+ }
+ }
+
+ /**
+ * Draw the labels
+ */
+ this.DrawLabels();
+
+ /**
+ * Draw the title
+ */
+ if (this.Get('chart.align') == 'left') {
+ var centerx = this.radius + this.Get('chart.gutter.left');
+
+ } else if (this.Get('chart.align') == 'right') {
+ var centerx = RGraph.GetWidth(this) - (this.radius + this.gutterRight);
+
+ } else {
+ var centerx = null;
+ }
+
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ (this.canvas.height / 2) - this.radius - 5,
+ centerx,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Tooltips
+ */
+ if (this.Get('chart.tooltips').length) {
+
+ /**
+ * Register this object for redrawing
+ */
+ RGraph.Register(this);
+
+ /**
+ * The onclick event
+ */
+ //this.canvas.onclick = function (e)
+ var canvas_onclick_func = function (e)
+ {
+ RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+ var obj = e.target.__object__;
+
+
+
+ /**
+ * If it's actually a donut make sure the hyp is bigger
+ * than the size of the hole in the middle
+ */
+ if (obj.Get('chart.variant') == 'donut' && Math.abs(hyp) < (obj.radius / 2)) {
+ return;
+ }
+
+ /**
+ * The angles for each segment are stored in "angles",
+ * so go through that checking if the mouse position corresponds
+ */
+ var isDonut = obj.Get('chart.variant') == 'donut';
+ var hStyle = obj.Get('chart.highlight.style');
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+
+ var x = mouseCoords[0] - segment[0];
+ var y = mouseCoords[1] - segment[1];
+ var theta = Math.atan(y / x); // RADIANS
+ var hyp = y / Math.sin(theta);
+
+
+ if ( RGraph.Registry.Get('chart.tooltip')
+ && segment[5] == RGraph.Registry.Get('chart.tooltip').__index__
+ && RGraph.Registry.Get('chart.tooltip').__canvas__.__object__.id == obj.id) {
+
+ return;
+
+ } else {
+ RGraph.Redraw();
+ }
+
+
+ if (isDonut || hStyle == '2d') {
+
+ context.beginPath();
+
+ context.strokeStyle = obj.Get('chart.highlight.style.2d.stroke');
+ context.fillStyle = obj.Get('chart.highlight.style.2d.fill');
+
+ //context.moveTo(obj.centerx, obj.centery);
+
+ context.moveTo(segment[0], segment[1]);
+ context.arc(segment[0], segment[1], segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0);
+ context.lineTo(segment[0], segment[1]);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ //Removed 7th December 2010
+ //context.stroke();
+
+ } else if (hStyle == 'explode') {
+
+ var exploded = [];
+
+ exploded[segment[5]] = 0;
+
+ RGraph.Registry.Set('chart.pie.exploded', obj);
+
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 25);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 50);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 75);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 100);
+ setTimeout(function () {var pie = RGraph.Registry.Get('chart.pie.exploded'); pie.Set('chart.exploded', exploded);RGraph.Clear(pie.canvas);pie.Draw(); exploded[segment[5]] += 7;}, 125);
+
+ setTimeout(function () {RGraph.Registry.Get('chart.pie.exploded').Set('chart.exploded', []);}, 150);
+
+ } else {
+
+ context.lineWidth = 2;
+
+ /**
+ * Draw a white segment where the one that has been clicked on was
+ */
+ context.fillStyle = 'white';
+ context.strokeStyle = 'white';
+ context.beginPath();
+ context.moveTo(segment[0], segment[1]);
+ context.arc(segment[0], segment[1], segment[2], obj.angles[segment[5]][0] / 57.3, obj.angles[segment[5]][1] / 57.3, 0);
+ context.stroke();
+ context.fill();
+
+ context.lineWidth = 1;
+
+ context.shadowColor = '#666';
+ context.shadowBlur = 3;
+ context.shadowOffsetX = 3;
+ context.shadowOffsetY = 3;
+
+ // Draw the new segment
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.colors')[segment[5]];
+ context.strokeStyle = obj.Get('chart.strokestyle');
+ context.moveTo(segment[0] - 3, segment[1] - 3);
+ context.arc(segment[0] - 3, segment[1] - 3, segment[2], RGraph.degrees2Radians(obj.angles[segment[5]][0]), RGraph.degrees2Radians(obj.angles[segment[5]][1]), 0);
+ context.lineTo(segment[0] - 3, segment[1] - 3);
+ context.closePath();
+
+ context.stroke();
+ context.fill();
+
+ // Turn off the shadow
+ RGraph.NoShadow(obj);
+
+ /**
+ * If a border is defined, redraw that
+ */
+ if (obj.Get('chart.border')) {
+ context.beginPath();
+ context.strokeStyle = obj.Get('chart.border.color');
+ context.lineWidth = 5;
+ context.arc(segment[0] - 3, segment[1] - 3, obj.radius - 2, RGraph.degrees2Radians(obj.angles[i][0]), RGraph.degrees2Radians(obj.angles[i][1]), 0);
+ context.stroke();
+ }
+ }
+
+ /**
+ * If a tooltip is defined, show it
+ */
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), segment[5]);
+
+ if (text) {
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[5]);
+ }
+
+ /**
+ * Need to redraw the key?
+ */
+ if (obj.Get('chart.key') && obj.Get('chart.key').length && obj.Get('chart.key.position') == 'graph') {
+ RGraph.DrawKey(obj, obj.Get('chart.key'), obj.Get('chart.colors'));
+ }
+
+ e.stopPropagation();
+
+ return;
+ } else if (obj.Get('chart.tooltips.event') == 'onclick') {
+ RGraph.Redraw();
+ }
+ }
+ var event_name = this.Get('chart.tooltips.event') == 'onmousemove' ? 'mousemove' : 'click';
+
+ this.canvas.addEventListener(event_name, canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, event_name, canvas_onclick_func);
+
+
+
+
+
+
+ /**
+ * The onmousemove event for changing the cursor
+ */
+ //this.canvas.onmousemove = function (e)
+ var canvas_onmousemove_func = function (e)
+ {
+ RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ var obj = e.target.__object__;
+ var segment = obj.getSegment(e);
+
+ if (segment) {
+ e.target.style.cursor = 'pointer';
+
+ return;
+ }
+
+ /**
+ * Put the cursor back to null
+ */
+ e.target.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+
+
+
+
+
+
+
+
+
+ /**
+ * The window onclick function
+ */
+ var window_onclick_func = function (e)
+ {
+ // Taken out on 02/10/11
+ //RGraph.HideZoomedCanvas();
+
+ e = RGraph.FixEventObject(e);
+
+ RGraph.Redraw();
+
+ /**
+ * Put the cursor back to null
+ */
+ //e.target.style.cursor = 'default';
+ }
+ window.addEventListener('click', window_onclick_func, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func);
+ }
+
+
+ /**
+ * If a border is pecified, draw it
+ */
+ if (this.Get('chart.border')) {
+ this.context.beginPath();
+ this.context.lineWidth = 5;
+ this.context.strokeStyle = this.Get('chart.border.color');
+
+ this.context.arc(this.centerx,
+ this.centery,
+ this.radius - 2,
+ 0,
+ 6.28,
+ 0);
+
+ this.context.stroke();
+ }
+
+ /**
+ * Draw the kay if desired
+ */
+ if (this.Get('chart.key') != null) {
+ //this.Set('chart.key.position', 'graph');
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+
+ /**
+ * If this is actually a donut, draw a big circle in the middle
+ */
+ if (this.Get('chart.variant') == 'donut') {
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.variant.donut.color');
+ this.context.arc(this.centerx, this.centery, this.radius / 2, 0, 6.28, 0);
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ RGraph.NoShadow(this);
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws a single segment of the pie chart
+ *
+ * @param int degrees The number of degrees for this segment
+ */
+ RGraph.Pie.prototype.DrawSegment = function (degrees, color, last, index)
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+ var subTotal = this.subTotal;
+
+ context.beginPath();
+
+ context.fillStyle = color;
+ context.strokeStyle = this.Get('chart.strokestyle');
+ context.lineWidth = 0;
+
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'),this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ /**
+ * Exploded segments
+ */
+ if ( (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[index] > 0) || typeof(this.Get('chart.exploded')) == 'number') {
+ var explosion = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[index];
+ var x = 0;
+ var y = 0;
+ var h = explosion;
+ var t = (subTotal + (degrees / 2)) / (360/6.2830);
+ var x = (Math.cos(t) * explosion);
+ var y = (Math.sin(t) * explosion);
+
+ this.context.moveTo(this.centerx + x, this.centery + y);
+ } else {
+ var x = 0;
+ var y = 0;
+ }
+
+ /**
+ * Calculate the angles
+ */
+ var startAngle = subTotal / 57.3;
+ var endAngle = (last ? 360 : subTotal + degrees) / 57.3;
+
+ context.arc(this.centerx + x,
+ this.centery + y,
+ this.radius,
+ startAngle * this.Get('chart.effect.roundrobin.multiplier'),
+ endAngle * this.Get('chart.effect.roundrobin.multiplier'),
+ 0);
+
+ context.lineTo(this.centerx + x, this.centery + y);
+
+ // Keep hold of the angles
+ this.angles.push([subTotal, subTotal + degrees, this.centerx + x, this.centery + y])
+ this.context.closePath();
+
+
+
+ //this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Calculate the segment angle
+ */
+ this.Get('chart.segments').push([subTotal, subTotal + degrees]);
+ this.subTotal += degrees;
+ }
+
+ /**
+ * Draws the graphs labels
+ */
+ RGraph.Pie.prototype.DrawLabels = function ()
+ {
+ var hAlignment = 'left';
+ var vAlignment = 'center';
+ var labels = this.Get('chart.labels');
+ var context = this.context;
+
+ /**
+ * Turn the shadow off
+ */
+ RGraph.NoShadow(this);
+
+ context.fillStyle = 'black';
+ context.beginPath();
+
+ /**
+ * Draw the key (ie. the labels)
+ */
+ if (labels && labels.length) {
+
+ var text_size = this.Get('chart.text.size');
+
+ for (i=0; i<labels.length; ++i) {
+
+ /**
+ * T|his ensures that if we're given too many labels, that we don't get an error
+ */
+ if (typeof(this.Get('chart.segments')[i]) == 'undefined') {
+ continue;
+ }
+
+ // Move to the centre
+ context.moveTo(this.centerx,this.centery);
+
+ var a = this.Get('chart.segments')[i][0] + ((this.Get('chart.segments')[i][1] - this.Get('chart.segments')[i][0]) / 2);
+
+ /**
+ * Alignment
+ */
+ if (a < 90) {
+ hAlignment = 'left';
+ vAlignment = 'center';
+ } else if (a < 180) {
+ hAlignment = 'right';
+ vAlignment = 'center';
+ } else if (a < 270) {
+ hAlignment = 'right';
+ vAlignment = 'center';
+ } else if (a < 360) {
+ hAlignment = 'left';
+ vAlignment = 'center';
+ }
+
+
+ /**
+ * Handle the additional "explosion" offset
+ */
+ if (typeof(this.Get('chart.exploded')) == 'object' && this.Get('chart.exploded')[i] || typeof(this.Get('chart.exploded')) == 'number') {
+
+ var t = ((this.angles[i][1] - this.angles[i][0]) / 2) / (360/6.2830);
+ var seperation = typeof(this.Get('chart.exploded')) == 'number' ? this.Get('chart.exploded') : this.Get('chart.exploded')[i];
+ var angle = ((this.angles[i][1] - this.angles[i][0]) / 2) + this.angles[i][0];
+
+ // Adjust the angles
+ var explosion_offsetx = (Math.cos(angle / 57.29) * seperation);
+ var explosion_offsety = (Math.sin(angle / 57.29) * seperation);
+ } else {
+ var explosion_offsetx = 0;
+ var explosion_offsety = 0;
+ }
+
+ context.fillStyle = this.Get('chart.text.color');
+
+ RGraph.Text(context,
+ this.Get('chart.text.font'),
+ text_size,
+ this.centerx + explosion_offsetx + ((this.radius + 10)* Math.cos(a / 57.3)) + (this.Get('chart.labels.sticks') ? (a < 90 || a > 270 ? 2 : -2) : 0),
+ this.centery + explosion_offsety + (((this.radius + 10) * Math.sin(a / 57.3))),
+ labels[i],
+ vAlignment,
+ hAlignment);
+ }
+
+ context.fill();
+ }
+ }
+
+
+ /**
+ * This function draws the pie chart sticks (for the labels)
+ */
+ RGraph.Pie.prototype.DrawSticks = function ()
+ {
+ var context = this.context;
+ var segments = this.Get('chart.segments');
+ var offset = this.Get('chart.linewidth') / 2;
+ var exploded = this.Get('chart.exploded');
+ var sticks = this.Get('chart.labels.sticks');
+
+ for (var i=0; i<segments.length; ++i) {
+
+ // This allows the chart.labels.sticks to be an array as well as a boolean
+ if (typeof(sticks) == 'object' && !sticks[i]) {
+ continue;
+ }
+
+ var degrees = segments[i][1] - segments[i][0];
+
+ context.beginPath();
+ context.strokeStyle = this.Get('chart.labels.sticks.color');
+ context.lineWidth = 1;
+
+ var midpoint = (segments[i][0] + (degrees / 2)) / 57.3;
+
+ if (exploded && exploded[i]) {
+ var extra = exploded[i];
+ } else {
+ var extra = 0;
+ }
+
+ context.lineJoin = 'round';
+ context.lineWidth = 1;
+
+ context.arc(this.centerx,
+ this.centery,
+ this.radius + 7 + extra,
+ midpoint,
+ midpoint + 0.01,
+ 0);
+
+ context.arc(this.centerx,
+ this.centery,
+ this.radius - offset + extra,
+ midpoint,
+ midpoint + 0.01,
+ 0);
+
+ context.stroke();
+ }
+ }
+
+
+ /**
+ * The (now Pie chart specific) getSegment function
+ *
+ * @param object e The event object
+ */
+ RGraph.Pie.prototype.getSegment = function (e)
+ {
+ RGraph.FixEventObject(e);
+
+ // The optional arg provides a way of allowing some accuracy (pixels)
+ var accuracy = arguments[1] ? arguments[1] : 0;
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var r = obj.radius;
+ var angles = obj.angles;
+ var ret = [];
+
+ for (var i=0; i<angles.length; ++i) {
+
+ var x = mouseCoords[0] - angles[i][2];
+ var y = mouseCoords[1] - angles[i][3];
+ var theta = Math.atan(y / x); // RADIANS
+ var hyp = y / Math.sin(theta);
+ var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy;
+ // Put theta in DEGREES
+ theta *= 57.3
+
+ /**
+ * Account for the correct quadrant
+ */
+ if (x < 0 && y >= 0) {
+ theta += 180;
+ } else if (x < 0 && y < 0) {
+ theta += 180;
+ } else if (x > 0 && y < 0) {
+ theta += 360;
+ }
+
+ if (theta > 360) {
+ theta -= 360;
+ }
+
+ if (theta >= angles[i][0] && theta < angles[i][1]) {
+
+ hyp = Math.abs(hyp);
+
+ if (!hyp || (obj.radius && hyp > obj.radius) ) {
+ return null;
+ }
+
+ if (obj.type == 'pie' && obj.Get('chart.variant') == 'donut' && (hyp > obj.radius || hyp < (obj.radius / 2) ) ) {
+ return null;
+ }
+
+ ret[0] = angles[i][2];
+ ret[1] = angles[i][3];
+ ret[2] = (obj.type == 'rose') ? angles[i][2] : obj.radius;
+ ret[3] = angles[i][0];
+ ret[4] = angles[i][1];
+ ret[5] = i;
+
+
+
+ if (ret[3] < 0) ret[3] += 360;
+ if (ret[4] > 360) ret[4] -= 360;
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+
+ RGraph.Pie.prototype.DrawBorders = function ()
+ {
+ if (this.Get('chart.linewidth') > 0) {
+
+ this.context.lineWidth = this.Get('chart.linewidth');
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ for (var i=0,len=this.angles.length; i<len; ++i) {
+ this.context.beginPath();
+ this.context.moveTo(this.angles[i][2], this.angles[i][3]);
+ this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][0] + 0.01) / 57.3, 0);
+ this.context.arc(this.angles[i][2], this.angles[i][3], this.radius, this.angles[i][0] / 57.3, (this.angles[i][1]) / 57.3, 0);
+ this.context.closePath();
+
+ this.context.stroke();
+ }
+ }
+ }
+
+
+ /**
+ * Returns the radius of the pie chart
+ */
+ RGraph.Pie.prototype.getRadius = function ()
+ {
+ return Math.min(this.canvas.height - this.gutterTop - this.gutterBottom, this.canvas.width - this.gutterLeft - this.gutterRight) / 2;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.radar.js b/schall/static/RGraph/libraries/RGraph.radar.js
new file mode 100644
index 0000000..fd63e8f
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.radar.js
@@ -0,0 +1,739 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The traditional radar chart constructor
+ *
+ * @param string id The ID of the canvas
+ * @param array data An array of data to represent
+ */
+ RGraph.Radar = function (id, data)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.canvas.__object__ = this;
+ this.size = null;// Set in the .Draw() method
+ this.type = 'radar';
+ this.coords = [];
+ this.isRGraph = true;
+ this.data = [];
+ this.max = 0;
+
+ for (var i=1; i<arguments.length; ++i) {
+ this.data.push(RGraph.array_clone(arguments[i]));
+ this.max = Math.max(this.max, RGraph.array_max(arguments[i]));
+ }
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ this.properties = {
+ 'chart.strokestyle': 'black',
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.linewidth': 1,
+ 'chart.colors': ['red', 'green', 'blue'],
+ 'chart.colors.alpha': null,
+ 'chart.circle': 0,
+ 'chart.circle.fill': 'red',
+ 'chart.circle.stroke': 'black',
+ 'chart.labels': [],
+ 'chart.labels.offsetx': 10,
+ 'chart.labels.offsety': 10,
+ 'chart.background.circles': true,
+ 'chart.text.size': 10,
+ 'chart.text.font': 'Verdana',
+ 'chart.text.color': 'black',
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.color': 'black',
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.linewidth': 1,
+
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+
+ 'chart.contextmenu': null,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'gray',
+ 'chart.highlight.fill': 'white',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.labels.axes': 'nsew',
+ 'chart.ymax': null
+ }
+
+ // Must have at least 3 points
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
+ if (this.data[dataset].length < 3) {
+ alert('[RADAR] You must specify at least 3 data points');
+ return;
+ }
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getPoint;
+ }
+
+
+ /**
+ * A simple setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the property
+ */
+ RGraph.Radar.prototype.Set = function (name, value)
+ {
+ this.properties[name] = value;
+
+ /**
+ * If the name is chart.color, set chart.colors too
+ */
+ if (name == 'chart.color') {
+ this.properties['chart.colors'] = [value];
+ }
+ }
+
+
+ /**
+ * A simple hetter
+ *
+ * @param string name The name of the property to get
+ */
+ RGraph.Radar.prototype.Get = function (name)
+ {
+ return this.properties[name];
+ }
+
+
+ /**
+ * The draw method which does all the brunt of the work
+ */
+ RGraph.Radar.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ this.centerx = ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2) + this.gutterLeft;
+ this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+ this.size = Math.min(this.canvas.width - this.gutterLeft - this.gutterRight, this.canvas.height - this.gutterTop - this.gutterBottom);
+
+ // Work out the maximum value and the sum
+ if (!this.Get('chart.ymax')) {
+
+ // this.max is calculated in the constructor
+
+ this.scale = RGraph.getScale(this.max, this);
+ this.max = this.scale[4];
+ } else {
+ var ymax = this.Get('chart.ymax');
+
+ this.scale = [
+ ymax * 0.2,
+ ymax * 0.4,
+ ymax * 0.6,
+ ymax * 0.8,
+ ymax * 1
+ ];
+ this.max = this.scale[4];
+ }
+
+ this.DrawBackground();
+ this.DrawAxes();
+ this.DrawCircle();
+ this.DrawAxisLabels();
+ this.DrawChart();
+ this.DrawLabels();
+
+ // Draw the title
+ if (this.Get('chart.title')) {
+ RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.gutterTop, null, this.Get('chart.title.size') ? this.Get('chart.title.size') : null)
+ }
+
+ // Draw the key if necessary
+ // obj, key, colors
+ if (this.Get('chart.key')) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+ /**
+ * Show the context menu
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * This function enables adjusting
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the background circles
+ */
+ RGraph.Radar.prototype.DrawBackground = function ()
+ {
+ var color = '#ddd';
+
+ /**
+ * Draws the background circles
+ */
+ if (this.Get('chart.background.circles')) {
+
+ this.context.strokeStyle = color;
+ this.context.beginPath();
+
+ for (var r=5; r<(this.size / 2); r+=15) {
+
+ this.context.moveTo(this.centerx, this.centery);
+ this.context.arc(this.centerx, this.centery,r, 0, 6.28, 0);
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draw diagonals
+ */
+ this.context.strokeStyle = color;
+ for (var i=0; i<360; i+=15) {
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, this.size / 2, (i / 360) * (2 * Math.PI), ((i+0.01) / 360) * (2 * Math.PI), 0); // The 0.01 avoids a bug in Chrome 6
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * Draws the axes
+ */
+ RGraph.Radar.prototype.DrawAxes = function ()
+ {
+ this.context.strokeStyle = 'black';
+
+ var halfsize = this.size / 2;
+
+ this.context.beginPath();
+
+ /**
+ * The Y axis
+ */
+ this.context.moveTo(this.centerx, this.centery + halfsize);
+ this.context.lineTo(this.centerx, this.centery - halfsize);
+
+
+ // Draw the bits at either end of the Y axis
+ this.context.moveTo(this.centerx - 5, this.centery + halfsize);
+ this.context.lineTo(this.centerx + 5, this.centery + halfsize);
+ this.context.moveTo(this.centerx - 5, this.centery - halfsize);
+ this.context.lineTo(this.centerx + 5, this.centery - halfsize);
+
+ // Draw X axis tick marks
+ for (var y=(this.centery - halfsize); y<(this.centery + halfsize); y+=15) {
+ this.context.moveTo(this.centerx - 3, y);
+ this.context.lineTo(this.centerx + 3, y);
+ }
+
+ /**
+ * The X axis
+ */
+ this.context.moveTo(this.centerx - halfsize, this.centery);
+ this.context.lineTo(this.centerx + halfsize, this.centery);
+
+ // Draw the bits at the end of the X axis
+ this.context.moveTo(this.centerx - halfsize, this.centery - 5);
+ this.context.lineTo(this.centerx - halfsize, this.centery + 5);
+ this.context.moveTo(this.centerx + halfsize, this.centery - 5);
+ this.context.lineTo(this.centerx + halfsize, this.centery + 5);
+
+ // Draw X axis tick marks
+ for (var x=(this.centerx - halfsize); x<(this.centerx + halfsize); x+=15) {
+ this.context.moveTo(x, this.centery - 3);
+ this.context.lineTo(x, this.centery + 3);
+ }
+
+ /**
+ * Finally draw it to the canvas
+ */
+ this.context.stroke();
+ }
+
+
+ /**
+ * The function which actually draws the radar chart
+ */
+ RGraph.Radar.prototype.DrawChart = function ()
+ {
+ var alpha = this.Get('chart.colors.alpha');
+
+ if (typeof(alpha) == 'number') {
+ var oldAlpha = this.context.globalAlpha;
+ this.context.globalAlpha = alpha;
+ }
+
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
+
+ this.context.beginPath();
+
+ this.coords[dataset] = [];
+
+ for (var i=0; i<this.data[dataset].length; ++i) {
+ this.coords[dataset][i] = this.GetCoordinates(dataset, i);
+ }
+
+ /**
+ * Now go through the coords and draw the chart itself
+ */
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[dataset];
+ this.context.lineWidth = this.Get('chart.linewidth');
+
+ for (i=0; i<this.coords[dataset].length; ++i) {
+ if (i == 0) {
+ this.context.moveTo(this.coords[dataset][i][0], this.coords[dataset][i][1]);
+ } else {
+ this.context.lineTo(this.coords[dataset][i][0], this.coords[dataset][i][1]);
+ }
+ }
+
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Can now handletooltips
+ */
+ if (this.Get('chart.tooltips')) {
+
+ RGraph.Register(this);
+
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var overHotspot = false;
+ var point = obj.getPoint(e);
+
+
+ if (point) {
+
+ var dataset = point[3];
+ var idx = point[4];
+
+ if ( !RGraph.Registry.Get('chart.tooltip')
+ || (RGraph.Registry.Get('chart.tooltip').__index__ != idx && RGraph.Registry.Get('chart.tooltip').__dataset__ != dataset)
+ || (RGraph.Registry.Get('chart.tooltip').__index__ != idx && RGraph.Registry.Get('chart.tooltip').__dataset__ == dataset)
+ ) {
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), idx);
+
+ if (typeof(text) == 'string' && text.length) {
+
+ overHotspot = true;
+ obj.canvas.style.cursor = 'pointer';
+
+ RGraph.Clear(obj.canvas);
+ obj.Draw();
+
+ if (obj.Get('chart.tooltips.highlight')) {
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.arc(point[1], point[2], 2, 0, 6.28, 0);
+ obj.context.fill();
+ obj.context.stroke();
+ }
+
+ RGraph.Tooltip(obj.canvas, text, e.pageX, e.pageY, idx);
+
+ // Set the data set value on the tooltip
+ RGraph.Registry.Get('chart.tooltip').__index__ = idx;
+ RGraph.Registry.Get('chart.tooltip').__dataset__ = dataset;
+
+
+ }
+ //} else if (RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ == idx && RGraph.Registry.Get('chart.tooltip').__dataset__ == dataset) {
+ } else {
+ overHotspot = true;
+ obj.canvas.style.cursor = 'pointer';
+ }
+ }
+
+ if (!overHotspot) {
+ obj.canvas.style.cursor = 'default';
+ }
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+ }
+
+ // Reset the globalAlpha
+ if (typeof(alpha) == 'number') {
+ this.context.globalAlpha = oldAlpha;
+ }
+ }
+
+
+ /**
+ * Gets the coordinates for a particular mark
+ *
+ * @param number i The index of the data (ie which one it is)
+ * @return array A two element array of the coordinates
+ */
+ RGraph.Radar.prototype.GetCoordinates = function (dataset, index)
+ {
+ // The number of data points
+ var len = this.data[dataset].length;
+
+ // The magnitude of the data (NOT the x/y coords)
+ var mag = (this.data[dataset][index] / this.max) * (this.size / 2);
+
+ /**
+ * Get the angle
+ */
+ var angle = (6.28 / len) * index; // In radians
+
+ /**
+ * Work out the X/Y coordinates
+ */
+ var x = Math.cos(angle) * mag;
+ var y = Math.sin(angle) * mag;
+
+ /**
+ * Put the coordinate in the right quadrant
+ */
+ x = this.centerx + x;
+ y = this.centery + (index == 0 ? 0 : y);
+
+ return [x,y];
+ }
+
+
+ /**
+ * This function adds the labels to the chart
+ */
+ RGraph.Radar.prototype.DrawLabels = function ()
+ {
+ var labels = this.Get('chart.labels');
+
+ if (labels && labels.length > 0) {
+
+ this.context.lineWidth = 1;
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ var offsetx = this.Get('chart.labels.offsetx');
+ var offsety = this.Get('chart.labels.offsety');
+
+ for (var i=0; i<labels.length; ++i) {
+
+ var ds = 0;
+
+ for (var dataset=0; dataset<this.data.length; ++dataset) {
+ if (this.data[dataset][i] > this.data[ds][i]) {
+ ds = dataset;
+ }
+ }
+
+ var x = this.coords[ds][i][0];
+ var y = this.coords[ds][i][1];
+ var text = labels[i];
+ var hAlign = 'center';
+ var vAlign = 'center';
+ var quartile = (i / this.coords.length);
+
+ // ~Manually do labels on the right middle axis
+ if (i == 0) {
+ hAlign = 'left';
+ vAlign = 'center';
+ x += offsetx;
+
+ } else {
+
+ hAlign = (x < this.centerx) ? 'right' : 'left';
+ vAlign = (y < this.centery) ? 'bottom' : 'top';
+ x += (x < this.centerx) ? (-1 * offsetx) : offsetx;
+ y += (y < this.centery) ? (-1 * offsety) : offsety;
+
+ if (i / this.data.length == 0.25) { x -= offsetx; hAlign = 'center';
+ } else if (i / this.data.length == 0.5) { y -= offsety; vAlign = 'center';
+ } else if (i / this.data.length == 0.75) { x += offsetx; hAlign = 'center'; }
+ }
+
+ // context, font, size, x, y, text
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size'), x, y, text, vAlign, hAlign, true, null, 'white');
+ }
+ }
+ }
+
+
+ /**
+ * Draws the circle. No arguments as it gets the information from the object properties.
+ */
+ RGraph.Radar.prototype.DrawCircle = function ()
+ {
+ var circle = {};
+ circle.limit = this.Get('chart.circle');
+ circle.fill = this.Get('chart.circle.fill');
+ circle.stroke = this.Get('chart.circle.stroke');
+
+ if (circle.limit) {
+
+ var r = (circle.limit / this.max) * (this.size / 2);
+
+ this.context.fillStyle = circle.fill;
+ this.context.strokeStyle = circle.stroke;
+
+ this.context.beginPath();
+ this.context.arc(this.centerx, this.centery, r, 0, 6.28, 0);
+ this.context.fill();
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * Unsuprisingly, draws the labels
+ */
+ RGraph.Radar.prototype.DrawAxisLabels = function ()
+ {
+ /**
+ * Draw specific axis labels
+ */
+ if (this.Get('chart.labels.specific')) {
+ this.DrawSpecificAxisLabels();
+ return;
+ }
+
+ this.context.lineWidth = 1;
+
+ // Set the color to black
+ this.context.fillStyle = 'black';
+ this.context.strokeStyle = 'black';
+
+ var r = (this.size/ 2);
+ var font_face = this.Get('chart.text.font');
+ var font_size = this.Get('chart.text.size');
+ var context = this.context;
+ var axes = this.Get('chart.labels.axes').toLowerCase();
+ var color = 'rgba(255,255,255,0.8)';
+
+ // The "North" axis labels
+ if (axes.indexOf('n') > -1) {
+ RGraph.Text(context,font_face,font_size,this.centerx,this.centery - (r * 0.2),String(this.scale[0]),'center','center',true,false,color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "South" axis labels
+ if (axes.indexOf('s') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "East" axis labels
+ if (axes.indexOf('e') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "West" axis labels
+ if (axes.indexOf('w') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery, '0', 'center', 'center', true, false, color);
+ }
+
+
+ /**
+ * Draws specific axis labels
+ */
+ RGraph.Radar.prototype.DrawSpecificAxisLabels = function ()
+ {
+ /**
+ * Specific Y labels
+ */
+ var labels = this.Get('chart.labels.specific');
+ var context = this.context;
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+ var axes = this.Get('chart.labels.axes').toLowerCase();
+ var interval = this.size / (labels.length * 2);
+
+ for (var i=0; i<labels.length; ++i) {
+
+ if (axes.indexOf('n') > -1) RGraph.Text(context,font,size,this.gutterLeft + (this.size / 2),this.gutterTop + (i * interval),labels[i],'center','center', true, false, 'rgba(255,255,255,0.8)');
+ if (axes.indexOf('s') > -1) RGraph.Text(context,font,size,this.gutterLeft + (this.size / 2),this.gutterTop + (this.size / 2) + (i * interval) + interval,RGraph.array_reverse(labels)[i],'center','center', true, false, 'rgba(255,255,255,0.8)');
+ if (axes.indexOf('w') > -1) RGraph.Text(context,font,size,this.gutterLeft + (i * interval),this.gutterTop + (this.size / 2),labels[i],'center','center', true, false, 'rgba(255,255,255,0.8)');
+ if (axes.indexOf('e') > -1) RGraph.Text(context,font,size,this.gutterLeft + (i * interval) + interval + (this.size / 2),this.gutterTop + (this.size / 2),RGraph.array_reverse(labels)[i],'center','center', true, false, 'rgba(255,255,255,0.8)');
+ }
+ }
+
+
+ /**
+ * This method eases getting the focussed point (if any)
+ *
+ * @param event e The event object
+ */
+ RGraph.Radar.prototype.getPoint = function (e)
+ {
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+
+ for(var dataset=0; dataset<obj.coords.length; ++dataset) {
+ for (var i=0; i<obj.coords[dataset].length; ++i) {
+
+ var x = obj.coords[dataset][i][0];
+ var y = obj.coords[dataset][i][1];
+ var tooltips = obj.Get('chart.tooltips');
+ var idx = Number(i);
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ if (
+ (tooltips[i] || tooltips) // The order here is important due to short circuiting
+ && mouseX < (x + 5)
+ && mouseX > (x - 5)
+ && mouseY > (y - 5)
+ && mouseY < (y + 5)
+ ) {
+
+ // This accounts for the datasets and increases the index accordingly
+ for(var j=0; j<dataset; j++) {
+ if (typeof(obj.data[j]) == 'object') {
+ i += obj.data[j].length;
+ }
+ }
+
+ return [obj, x, y, dataset, i];
+ }
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.rose.js b/schall/static/RGraph/libraries/RGraph.rose.js
new file mode 100644
index 0000000..99bf353
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.rose.js
@@ -0,0 +1,894 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The rose chart constuctor
+ *
+ * @param object canvas
+ * @param array data
+ */
+ RGraph.Rose = function (id, data)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.data = data;
+ this.canvas.__object__ = this;
+ this.type = 'rose';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ this.centerx = 0;
+ this.centery = 0;
+ this.radius = 0;
+ this.max = 0;
+
+ this.properties = {
+ 'chart.radius': null,
+ 'chart.colors': ['red', 'rgb(0,255,255)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'],
+ 'chart.colors.sequential': false,
+ 'chart.colors.alpha': null,
+ 'chart.margin': 0,
+ 'chart.strokestyle': 'rgba(0,0,0,0.5)',
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.labels': null,
+ 'chart.labels.position': 'center',
+ 'chart.labels.axes': 'nsew',
+ 'chart.labels.offset': 0,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.text.size': 10,
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.contextmenu': null,
+ 'chart.tooltips': null,
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false,
+ 'chart.ymax': null,
+ 'chart.ymin': 0,
+ 'chart.scale.decimals': null,
+ 'chart.variant': 'stacked',
+ 'chart.animation.grow.factor': 1
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getSegment;
+ }
+
+
+ /**
+ * A simple setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the property
+ */
+ RGraph.Rose.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A simple getter
+ *
+ * @param string name The name of the property to get
+ */
+ RGraph.Rose.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * This method draws the rose chart
+ */
+ RGraph.Rose.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+ /**
+ * This doesn't affect the chart, but is used for compatibility
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Calculate the radius
+ this.radius = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2);
+ this.centerx = RGraph.GetWidth(this) / 2;
+ this.centery = RGraph.GetHeight(this) / 2;
+ this.angles = [];
+ this.total = 0;
+ this.startRadians = 0;
+
+ // User specified radius
+ if (typeof(this.Get('chart.radius')) == 'number') {
+ this.radius = this.Get('chart.radius');
+ }
+
+ /**
+ * Change the centerx marginally if the key is defined
+ */
+ if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
+ this.centerx = this.centerx - this.Get('chart.gutter.right') + 5;
+ }
+
+ this.DrawBackground();
+ this.DrawRose();
+ this.DrawLabels();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Tooltips
+ */
+ if (this.Get('chart.tooltips')) {
+
+ /**
+ * Register this object for redrawing
+ */
+ RGraph.Register(this);
+
+ /**
+ * The onclick event
+ */
+ var canvas_onclick_func = function (e)
+ {
+ var obj = e.target.__object__;
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+
+ e = RGraph.FixEventObject(e);
+
+ RGraph.Redraw();
+
+ var segment = obj.getSegment(e);
+
+ if (segment && obj.Get('chart.tooltips')) {
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = String(obj.Get('chart.tooltips')(segment[6]));
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') {
+ var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6]));
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) {
+ var text = String(obj.Get('chart.tooltips')[segment[6]]);
+ } else {
+ var text = null;
+ }
+
+ if (text) {
+ context.beginPath();
+ context.strokeStyle = obj.Get('chart.highlight.stroke');
+ context.fillStyle = obj.Get('chart.highlight.fill');
+
+ // This highlights the chart
+ context.arc(obj.centerx, obj.centery, segment[3], segment[4] / 57.3, segment[5] / 57.3, false);
+ context.arc(obj.centerx, obj.centery, segment[2] + 0.01, segment[5] / 57.3, segment[4] / 57.3, true);
+
+ context.closePath();
+
+ context.fill();
+ context.stroke();
+
+ context.strokeStyle = 'rgba(0,0,0,0)';
+
+ // Taken out on 12th June 2011
+ //obj.DrawLabels();
+
+ /**
+ * Show the tooltip
+ */
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[6]);
+
+ e.stopPropagation();
+ }
+
+ return;
+ }
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
+
+
+ /**
+ * The onmousemove event
+ */
+ var canvas_onmousemove_func = function (e)
+ {
+
+ var obj = e.target.__object__;
+ var canvas = e.target;
+ var context = canvas.getContext('2d');
+
+ e = RGraph.FixEventObject(e);
+
+ var segment = obj.getSegment(e);
+
+ if (segment && obj.Get('chart.tooltips')) {
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(obj.Get('chart.tooltips')) == 'function') {
+ var text = String(obj.Get('chart.tooltips')(segment[6]));
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') {
+ var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6]));
+ } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) {
+ var text = String(obj.Get('chart.tooltips')[segment[6]]);
+ } else {
+ var text = null;
+ }
+
+ if (text) {
+ canvas.style.cursor = 'pointer';
+
+ /*******************************************************
+ * This is here in case tooltips are using the
+ * onmousemove event
+ *******************************************************/
+ if (obj.Get('chart.tooltips.event') == 'onmousemove') {
+ if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__index__ != segment[6]) {
+ canvas_onclick_func(e);
+ }
+ }
+
+ } else {
+ canvas.style.cursor = 'default';
+ }
+
+ return;
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * This function enables adjusting
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+ /**
+ * This method draws the rose charts background
+ */
+ RGraph.Rose.prototype.DrawBackground = function ()
+ {
+ this.context.lineWidth = 1;
+
+ // Draw the background grey circles
+ this.context.beginPath();
+ this.context.strokeStyle = '#ccc';
+ for (var i=15; i<this.radius - (RGraph.isIE8() ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
+
+ //this.context.moveTo(this.centerx + i, this.centery);
+
+ // Radius must be greater than 0 for Opera to work
+ this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
+ }
+ this.context.stroke();
+
+ // Draw the background lines that go from the center outwards
+ this.context.beginPath();
+ for (var i=15; i<360; i+=15) {
+
+ // Radius must be greater than 0 for Opera to work
+ this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.1) / 57.3, 0); // The 0.1 avoids a bug in Chrome 6
+
+ this.context.lineTo(this.centerx, this.centery);
+ }
+ this.context.stroke();
+
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+
+ // Draw the X axis
+ this.context.moveTo(this.centerx - this.radius, this.centery);
+ this.context.lineTo(this.centerx + this.radius, this.centery);
+
+ // Draw the X ends
+ this.context.moveTo(this.centerx - this.radius, this.centery - 5);
+ this.context.lineTo(this.centerx - this.radius, this.centery + 5);
+ this.context.moveTo(this.centerx + this.radius, this.centery - 5);
+ this.context.lineTo(this.centerx + this.radius, this.centery + 5);
+
+ // Draw the X check marks
+ for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) {
+ this.context.moveTo(i, this.centery - 3);
+ this.context.lineTo(i, this.centery + 3);
+ }
+
+ // Draw the Y check marks
+ for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) {
+ this.context.moveTo(this.centerx - 3, i);
+ this.context.lineTo(this.centerx + 3, i);
+ }
+
+ // Draw the Y axis
+ this.context.moveTo(this.centerx, this.centery - this.radius);
+ this.context.lineTo(this.centerx, this.centery + this.radius);
+
+ // Draw the Y ends
+ this.context.moveTo(this.centerx - 5, this.centery - this.radius);
+ this.context.lineTo(this.centerx + 5, this.centery - this.radius);
+
+ this.context.moveTo(this.centerx - 5, this.centery + this.radius);
+ this.context.lineTo(this.centerx + 5, this.centery + this.radius);
+
+ // Stroke it
+ this.context.closePath();
+ this.context.stroke();
+ }
+
+
+ /**
+ * This method draws the data on the graph
+ */
+ RGraph.Rose.prototype.DrawRose = function ()
+ {
+ var max = 0;
+ var data = this.data;
+ var margin = RGraph.degrees2Radians(this.Get('chart.margin'));
+
+ // Must be at least two data points
+ //if (data.length < 2) {
+ // alert('[ROSE] Must be at least two data points! [' + data + ']');
+ // return;
+ //}
+
+ // Work out the maximum value and the sum
+ if (!this.Get('chart.ymax')) {
+ // Work out the max
+ for (var i=0; i<data.length; ++i) {
+ if (typeof(data[i]) == 'number') {
+ max = Math.max(max, data[i]);
+ } else if (typeof(data[i]) == 'object' && this.Get('chart.variant') == 'non-equi-angular') {
+ max = Math.max(max, data[i][0]);
+
+ // Fallback is stacked
+ } else {
+ max = Math.max(max, RGraph.array_sum(data[i]));
+ }
+ }
+
+ this.scale = RGraph.getScale(max, this);
+ this.max = this.scale[4];
+ } else {
+ var ymax = this.Get('chart.ymax');
+ var ymin = this.Get('chart.ymin');
+
+ this.scale = [
+ ((ymax - ymin) * 0.2) + ymin,
+ ((ymax - ymin) * 0.4) + ymin,
+ ((ymax - ymin) * 0.6) + ymin,
+ ((ymax - ymin) * 0.8) + ymin,
+ ((ymax - ymin) * 1.0) + ymin
+ ];
+ this.max = this.scale[4];
+ }
+
+ this.sum = RGraph.array_sum(data);
+
+ // Move to the centre
+ this.context.moveTo(this.centerx, this.centery);
+
+ this.context.stroke(); // Stroke the background so it stays grey
+
+ // Transparency
+ if (this.Get('chart.colors.alpha')) {
+ this.context.globalAlpha = this.Get('chart.colors.alpha');
+ }
+
+ /*******************************************************
+ * A non-equi-angular Rose chart
+ *******************************************************/
+ if (typeof(this.Get('chart.variant')) == 'string' && this.Get('chart.variant') == 'non-equi-angular') {
+ /*******************************************************
+ * NON-EQUI-ANGULAR GOES HERE
+ *******************************************************/
+ var total=0;
+ for (var i=0; i<data.length; ++i) {
+ total += data[i][1];
+ }
+
+
+ for (var i=0; i<this.data.length; ++i) {
+
+ var segmentRadians = (this.data[i][1] / total) * (2 * Math.PI);
+ var radius = ((this.data[i][0] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10);
+
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ }
+
+ this.context.beginPath(); // Begin the segment
+ this.context.arc(this.centerx,
+ this.centery,
+ radius,
+ this.startRadians - (Math.PI / 2) + margin,
+ this.startRadians + segmentRadians - (Math.PI / 2) - margin,
+ 0);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath(); // End the segment
+
+ this.context.stroke();
+ this.context.fill();
+
+ // Store the start and end angles
+ this.angles.push([
+ ((this.startRadians - (Math.PI / 2) + margin) * 57.3) + 90,
+ (((this.startRadians + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90,
+ 0,
+ radius
+ ]);
+
+ this.startRadians += segmentRadians;
+ }
+ } else {
+ /*******************************************************
+ * Draw regular segments here
+ *******************************************************/
+ for (var i=0; i<this.data.length; ++i) {
+
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+
+ /*******************************************************
+ * This allows sequential colors
+ *******************************************************/
+ if (this.Get('chart.colors.sequential')) {
+ this.context.fillStyle = this.Get('chart.colors')[i];
+ }
+
+ var segmentRadians = (1 / this.data.length) * (2 * Math.PI);
+
+ if (typeof(this.data[i]) == 'number') {
+ this.context.beginPath(); // Begin the segment
+
+ var radius = ((this.data[i] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10);
+
+ this.context.arc(this.centerx,
+ this.centery,
+ radius * this.Get('chart.animation.grow.factor'),
+ (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin,
+ (this.startRadians * this.Get('chart.animation.grow.factor')) + (segmentRadians) - (Math.PI / 2) - margin,
+ 0);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath(); // End the segment
+ this.context.stroke();
+ this.context.fill();
+
+ // Store the start and end angles
+ this.angles.push([
+ (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90,
+ ((((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90,
+ 0,
+ radius * this.Get('chart.animation.grow.factor')
+ ]);
+ /*******************************************************
+ * Draw a stacked segment
+ *******************************************************/
+ } else if (typeof(this.data[i]) == 'object') {
+
+ var margin = this.Get('chart.margin') / (180 / Math.PI);
+
+ for (var j=0; j<this.data[i].length; ++j) {
+
+ this.context.fillStyle = this.Get('chart.colors')[j];
+ if (j == 0) {
+ this.context.beginPath(); // Begin the segment
+ var startRadius = 0;
+ var endRadius = ((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10);
+
+ this.context.arc(this.centerx,
+ this.centery,
+ endRadius * this.Get('chart.animation.grow.factor'),
+ (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin,
+ (this.startRadians * this.Get('chart.animation.grow.factor'))+ segmentRadians - (Math.PI / 2) - margin,
+ 0);
+ this.context.lineTo(this.centerx, this.centery);
+ this.context.closePath(); // End the segment
+ this.context.stroke();
+ this.context.fill();
+
+ this.angles.push([
+ (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90 ,
+ ((((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians) - (Math.PI / 2) - margin) * 57.3) + 90,
+ 0,
+ endRadius * this.Get('chart.animation.grow.factor')
+ ]);
+
+ } else {
+
+ this.context.beginPath(); // Begin the segment
+ var startRadius = endRadius; // This comes from the prior iteration of this loop
+ var endRadius = (((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10)) + startRadius;
+
+ this.context.arc(this.centerx,
+ this.centery,
+ startRadius * this.Get('chart.animation.grow.factor'),
+ (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin,
+ (this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin,
+ 0);
+
+ this.context.arc(this.centerx,
+ this.centery,
+ endRadius * this.Get('chart.animation.grow.factor'),
+ (this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin,
+ (this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin,
+ true);
+
+ this.context.closePath(); // End the segment
+ this.context.stroke();
+ this.context.fill();
+
+ this.angles.push([
+ (((this.startRadians * this.Get('chart.animation.grow.factor')) - (Math.PI / 2) + margin) * 57.3) + 90,
+ (((this.startRadians * this.Get('chart.animation.grow.factor')) + segmentRadians - (Math.PI / 2) - margin) * 57.3) + 90,
+ startRadius * this.Get('chart.animation.grow.factor'),
+ endRadius * this.Get('chart.animation.grow.factor')
+ ]);
+ }
+ }
+ }
+
+ this.startRadians += segmentRadians;
+ }
+ }
+
+ // Turn off the transparency
+ if (this.Get('chart.colors.alpha')) {
+ this.context.globalAlpha = 1;
+ }
+
+ // Draw the title if any has been set
+ if (this.Get('chart.title')) {
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ (this.canvas.height / 2) - this.radius,
+ this.centerx,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+ }
+ }
+
+
+ /**
+ * Unsuprisingly, draws the labels
+ */
+ RGraph.Rose.prototype.DrawLabels = function ()
+ {
+ this.context.lineWidth = 1;
+ var key = this.Get('chart.key');
+
+ if (key && key.length) {
+ RGraph.DrawKey(this, key, this.Get('chart.colors'));
+ }
+
+ // Set the color to black
+ this.context.fillStyle = 'black';
+ this.context.strokeStyle = 'black';
+
+ var r = this.radius - 10;
+ var font_face = this.Get('chart.text.font');
+ var font_size = this.Get('chart.text.size');
+ var context = this.context;
+ var axes = this.Get('chart.labels.axes').toLowerCase();
+
+ // Draw any labels
+
+ if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
+ this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r + 10);
+ }
+
+
+ var color = 'rgba(255,255,255,0.8)';
+
+ // The "North" axis labels
+ if (axes.indexOf('n') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ }
+
+ // The "South" axis labels
+ if (axes.indexOf('s') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ }
+
+ // The "East" axis labels
+ if (axes.indexOf('e') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ }
+
+ // The "West" axis labels
+ if (axes.indexOf('w') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
+ }
+
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery, typeof(this.Get('chart.ymin')) == 'number' ? String(Number(this.Get('chart.ymin')).toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color);
+ }
+
+
+ /**
+ * Draws the circular labels that go around the charts
+ *
+ * @param labels array The labels that go around the chart
+ */
+ RGraph.Rose.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
+ {
+ var variant = this.Get('chart.variant');
+ var position = this.Get('chart.labels.position');
+ var r = r + 10 + this.Get('chart.labels.offset');
+
+ for (var i=0; i<labels.length; ++i) {
+
+ if (typeof(variant) == 'string' && variant == 'non-equi-angular') {
+
+ var a = Number(this.angles[i][0]) + ((this.angles[i][1] - this.angles[i][0]) / 2);
+ a -= 90;
+ var halign = 'center'; // Default halign
+
+ var x = Math.cos(a / 57.29577866666) * (r + 10);
+ var y = Math.sin(a / 57.29577866666) * (r + 10);
+
+ RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign);
+
+ } else {
+
+ var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
+ var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
+ var halign = 'center'; // Default halign
+
+ // Horizontal alignment
+ //if (a == 0) {
+ // var halign = 'left';
+ //} else if (a == 180) {
+ // var halign = 'right';
+ //}
+
+ var x = Math.cos(a / 57.29577866666) * (r + 10);
+ var y = Math.sin(a / 57.29577866666) * (r + 10);
+
+ RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign);
+ }
+ }
+ }
+
+
+ /**
+ * This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object
+ * and it will pass you back the corresponding segment details as an array:
+ *
+ * [x, y, r, startAngle, endAngle]
+ *
+ * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas).
+ *
+ * @param object e Your event object
+ */
+ RGraph.Rose.prototype.getSegment = function (e)
+ {
+ RGraph.FixEventObject(e);
+
+ // The optional arg provides a way of allowing some accuracy (pixels)
+ var accuracy = arguments[1] ? arguments[1] : 0;
+
+ var obj = e.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var x = mouseCoords[0] - obj.centerx;
+ var y = mouseCoords[1] - obj.centery;
+ var r = obj.radius;
+ var theta = Math.atan(y / x); // RADIANS
+ var hyp = y / Math.sin(theta);
+ var angles = obj.angles;
+ var ret = [];
+ var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy;
+
+
+
+ // Put theta in DEGREES
+ theta *= 57.3
+
+ // hyp should not be greater than radius if it's a Rose chart
+ if (obj.type == 'rose') {
+ if ( (isNaN(hyp) && Math.abs(mouseCoords[0]) < (obj.centerx - r) )
+ || (isNaN(hyp) && Math.abs(mouseCoords[0]) > (obj.centerx + r))
+ || (!isNaN(hyp) && Math.abs(hyp) > r)) {
+ return;
+ }
+ }
+
+ /**
+ * Account for the correct quadrant
+ */
+ if (x < 0 && y >= 0) {
+ theta += 180;
+ } else if (x < 0 && y < 0) {
+ theta += 180;
+ } else if (x > 0 && y < 0) {
+ theta += 360;
+ }
+
+ /**
+ * Account for the rose chart angle displacement
+ */
+ theta += 90;
+
+ if (theta > 360) {
+ theta -= 360;
+ }
+
+ hyp = Math.abs(hyp);
+
+ for (var i=0; i<angles.length; ++i) {
+ if (theta >= angles[i][0] && theta < angles[i][1] && hyp > angles[i][2] && hyp < angles[i][3]) {
+
+ if (!(hyp > angles[i][2] && hyp < angles[i][3])) {
+ return null;
+ }
+
+ if (!hyp) {
+ return null;
+ }
+
+ ret[0] = obj.centerx;
+ ret[1] = obj.centery;
+ ret[2] = angles[i][2]; // Start angle
+ ret[3] = angles[i][3]; // End angle
+
+ ret[4] = angles[i][0]; // Start radius
+ ret[5] = angles[i][1]; // End radius
+ ret[6] = i;
+
+ ret[4] -= 90;
+ ret[5] -= 90;
+
+ if (x > 0 && y < 0) {
+ ret[4] += 360;
+ ret[5] += 360;
+ }
+
+ if (ret[4] < 0) ret[4] += 360;
+ if (ret[5] > 360) ret[5] -= 360;
+
+ return ret;
+ }
+ }
+
+ return null;
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.rscatter.js b/schall/static/RGraph/libraries/RGraph.rscatter.js
new file mode 100644
index 0000000..cc7949d
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.rscatter.js
@@ -0,0 +1,653 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The chart constuctor
+ *
+ * @param object canvas
+ * @param array data
+ */
+ RGraph.Rscatter = function (id, data)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.data = data;
+ this.canvas.__object__ = this;
+ this.type = 'rscatter';
+ this.hasTooltips = false;
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ this.centerx = 0;
+ this.centery = 0;
+ this.radius = 0;
+ this.max = 0;
+
+ this.properties = {
+ 'chart.radius': null,
+ 'chart.colors': [], // This is used internally for the key
+ 'chart.colors.default': 'black',
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.labels': null,
+ 'chart.labels.position': 'center',
+ 'chart.labels.axes': 'nsew',
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.text.size': 10,
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.contextmenu': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.tooltips.hotspot': 3,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.resizable': false,
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false,
+ 'chart.ymax': null,
+ 'chart.ymin': 0,
+ 'chart.tickmarks': 'cross',
+ 'chart.ticksize': 3,
+ 'chart.scale.decimals': null,
+ 'chart.scale.round': false
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getPoint;
+ }
+
+
+ /**
+ * A simple setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the property
+ */
+ RGraph.Rscatter.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A simple getter
+ *
+ * @param string name The name of the property to get
+ */
+ RGraph.Rscatter.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * This method draws the rose chart
+ */
+ RGraph.Rscatter.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+ /**
+ * This doesn't affect the chart, but is used for compatibility
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Calculate the radius
+ this.radius = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2);
+ this.centerx = RGraph.GetWidth(this) / 2;
+ this.centery = RGraph.GetHeight(this) / 2;
+ this.coords = [];
+
+ /**
+ * If there's a user specified radius, use that
+ */
+ if (typeof(this.Get('chart.radius')) == 'number') {
+ this.radius = this.Get('chart.radius');
+ }
+
+ /**
+ * Work out the scale
+ */
+ var max = this.Get('chart.ymax');
+ var min = this.Get('chart.ymin');
+
+ if (typeof(max) == 'number') {
+ this.max = max;
+ this.scale = [((max - min) * 0.2) + min,((max - min) * 0.4) + min,((max - min) * 0.6) + min,((max - min) * 0.8) + min,((max - min) * 1.0) + min,];
+
+ } else {
+ for (var i=0; i<this.data.length; ++i) {
+ this.max = Math.max(this.max, this.data[i][1]);
+ }
+ this.scale = RGraph.getScale(this.max, this);
+ this.max = this.scale[4];
+
+ // Hmmmmmmmm
+ if (String(this.scale[0]).indexOf('e') == -1) {
+
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale[0] = Number(this.scale[0]).toFixed(decimals);
+ this.scale[1] = Number(this.scale[1]).toFixed(decimals);
+ this.scale[2] = Number(this.scale[2]).toFixed(decimals);
+ this.scale[3] = Number(this.scale[3]).toFixed(decimals);
+ this.scale[4] = Number(this.scale[4]).toFixed(decimals);
+ }
+ }
+
+ /**
+ * Change the centerx marginally if the key is defined
+ */
+ if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
+ this.centerx = this.centerx - this.Get('chart.gutter.right') + 5;
+ }
+
+ /**
+ * Populate the colors array for the purposes of generating the key
+ */
+ if (typeof(this.Get('chart.key')) == 'object' && RGraph.is_array(this.Get('chart.key')) && this.Get('chart.key')[0]) {
+ for (var i=0; i<this.data.length; ++i) {
+ if (this.data[i][2] && typeof(this.data[i][2]) == 'string') {
+ this.Get('chart.colors').push(this.data[i][2]);
+ }
+ }
+ }
+
+ this.DrawBackground();
+ this.DrawRscatter();
+ this.DrawLabels();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Tooltips
+ */
+ if (this.hasTooltips) {
+
+ /**
+ * Register this object for redrawing
+ */
+ RGraph.Register(this);
+
+ /**
+ * The onmousemove event
+ */
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var obj = event.target.__object__;
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var overHotspot = false;
+ var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
+ var point = obj.getPoint(e);
+
+ if (point) {
+
+ overHotspot = true;
+ canvas.style.cursor = 'pointer';
+ var tooltip = obj.data[point[3]][3];
+
+ if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__text__ != tooltip) {
+
+ if (obj.Get('chart.tooltips.highlight')) {
+ RGraph.Redraw();
+ }
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(tooltip) == 'function') {
+ var text = String(tooltip(i));
+
+ } else {
+ var text = String(tooltip);
+ }
+
+ RGraph.Tooltip(canvas, text, e.pageX + 5, e.pageY - 5, point[3]);
+
+ /**
+ * Highlight the tickmark
+ */
+ if (obj.Get('chart.tooltips.highlight')) {
+ context.beginPath();
+ context.fillStyle = 'rgba(255,255,255,0.5)';
+ context.arc(point[1], point[2], 3, 0, 6.2830, 0);
+ context.fill();
+ }
+ }
+ }
+
+ if (!overHotspot) {
+ canvas.style.cursor = 'default';
+ }
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+ // Draw the title if any has been set
+ if (this.Get('chart.title')) {
+ RGraph.DrawTitle(this.canvas,
+ this.Get('chart.title'),
+ (this.canvas.height / 2) - this.radius - 5,
+ this.centerx,
+ this.Get('chart.title.size') ? this.Get('chart.title.size') : this.Get('chart.text.size') + 2);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+ /**
+ * This method draws the rose charts background
+ */
+ RGraph.Rscatter.prototype.DrawBackground = function ()
+ {
+ this.context.lineWidth = 1;
+
+ // Draw the background grey circles
+ this.context.strokeStyle = '#ccc';
+ for (var i=15; i<this.radius - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
+ //this.context.moveTo(this.centerx + i, this.centery);
+
+ // Radius must be greater than 0 for Opera to work
+ this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
+ }
+ this.context.stroke();
+
+ // Draw the background lines that go from the center outwards
+ this.context.beginPath();
+ for (var i=15; i<360; i+=15) {
+
+ // Radius must be greater than 0 for Opera to work
+ this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.01) / 57.3, 0);
+
+ this.context.lineTo(this.centerx, this.centery);
+ }
+ this.context.stroke();
+
+ this.context.beginPath();
+ this.context.strokeStyle = 'black';
+
+ // Draw the X axis
+ this.context.moveTo(this.centerx - this.radius, this.centery);
+ this.context.lineTo(this.centerx + this.radius, this.centery);
+
+ // Draw the X ends
+ this.context.moveTo(this.centerx - this.radius, this.centery - 5);
+ this.context.lineTo(this.centerx - this.radius, this.centery + 5);
+ this.context.moveTo(this.centerx + this.radius, this.centery - 5);
+ this.context.lineTo(this.centerx + this.radius, this.centery + 5);
+
+ // Draw the X check marks
+ for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) {
+ this.context.moveTo(i, this.centery - 3);
+ this.context.lineTo(i, this.centery + 3);
+ }
+
+ // Draw the Y check marks
+ for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) {
+ this.context.moveTo(this.centerx - 3, i);
+ this.context.lineTo(this.centerx + 3, i);
+ }
+
+ // Draw the Y axis
+ this.context.moveTo(this.centerx, this.centery - this.radius);
+ this.context.lineTo(this.centerx, this.centery + this.radius);
+
+ // Draw the Y ends
+ this.context.moveTo(this.centerx - 5, this.centery - this.radius);
+ this.context.lineTo(this.centerx + 5, this.centery - this.radius);
+
+ this.context.moveTo(this.centerx - 5, this.centery + this.radius);
+ this.context.lineTo(this.centerx + 5, this.centery + this.radius);
+
+ // Stroke it
+ this.context.closePath();
+ this.context.stroke();
+ }
+
+
+ /**
+ * This method draws a set of data on the graph
+ */
+ RGraph.Rscatter.prototype.DrawRscatter = function ()
+ {
+ var data = this.data;
+
+ for (var i=0; i<data.length; ++i) {
+
+ var d1 = data[i][0];
+ var d2 = data[i][1];
+ var a = d1 / (180 / Math.PI); // RADIANS
+ var r = ( (d2 - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin')) ) * this.radius;
+ var x = Math.sin(a) * r;
+ var y = Math.cos(a) * r;
+ var color = data[i][2] ? data[i][2] : this.Get('chart.colors.default');
+ var tooltip = data[i][3] ? data[i][3] : null;
+
+ if (tooltip && tooltip.length) {
+ this.hasTooltips = true;
+ }
+
+ /**
+ * Account for the correct quadrant
+ */
+ x = x + this.centerx;
+ y = this.centery - y;
+
+
+ this.DrawTick(x, y, color);
+
+ // Populate the coords array with the coordinates and the tooltip
+ this.coords.push([x, y, color, tooltip]);
+ }
+ }
+
+
+ /**
+ * Unsuprisingly, draws the labels
+ */
+ RGraph.Rscatter.prototype.DrawLabels = function ()
+ {
+ this.context.lineWidth = 1;
+ var key = this.Get('chart.key');
+
+ // Set the color to black
+ this.context.fillStyle = 'black';
+ this.context.strokeStyle = 'black';
+
+ var r = this.radius;
+ var color = this.Get('chart.text.color');
+ var font_face = this.Get('chart.text.font');
+ var font_size = this.Get('chart.text.size');
+ var context = this.context;
+ var axes = this.Get('chart.labels.axes').toLowerCase();
+
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ // Draw any labels
+ if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
+ this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r);
+ }
+
+
+ var color = 'rgba(255,255,255,0.8)';
+
+ // The "North" axis labels
+ if (axes.indexOf('n') > -1) {
+ RGraph.Text(context,font_face,font_size,this.centerx,this.centery - ((r) * 0.2),String(this.scale[0]),'center','center',true,false,color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "South" axis labels
+ if (axes.indexOf('s') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "East" axis labels
+ if (axes.indexOf('e') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // The "West" axis labels
+ if (axes.indexOf('w') > -1) {
+ RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
+ RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
+ }
+
+ // Draw the center minimum value (but only if there's at least one axes labels stipulated)
+ if (this.Get('chart.labels.axes').length > 0) {
+ RGraph.Text(context, font_face, font_size, this.centerx, this.centery, this.Get('chart.ymin') > 0 ? String(this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color);
+ }
+
+ /**
+ * Draw the key
+ */
+ if (key && key.length) {
+ RGraph.DrawKey(this, key, this.Get('chart.colors'));
+ }
+ }
+
+
+ /**
+ * Draws the circular labels that go around the charts
+ *
+ * @param labels array The labels that go around the chart
+ */
+ RGraph.Rscatter.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
+ {
+ var position = this.Get('chart.labels.position');
+ var r = r + 10;
+
+ for (var i=0; i<labels.length; ++i) {
+
+
+ var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
+ var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
+
+ var x = Math.cos(a / 57.29577866666) * (r + 10);
+ var y = Math.sin(a / 57.29577866666) * (r + 10);
+
+ RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center');
+ }
+ }
+
+
+ /**
+ * Draws a single tickmark
+ */
+ RGraph.Rscatter.prototype.DrawTick = function (x, y, color)
+ {
+ var tickmarks = this.Get('chart.tickmarks');
+ var ticksize = this.Get('chart.ticksize');
+
+ this.context.strokeStyle = color;
+ this.context.fillStyle = color;
+
+ // Cross
+ if (tickmarks == 'cross') {
+
+ this.context.beginPath();
+ this.context.moveTo(x + ticksize, y + ticksize);
+ this.context.lineTo(x - ticksize, y - ticksize);
+ this.context.stroke();
+
+ this.context.beginPath();
+ this.context.moveTo(x - ticksize, y + ticksize);
+ this.context.lineTo(x + ticksize, y - ticksize);
+ this.context.stroke();
+
+ // Circle
+ } else if (tickmarks == 'circle') {
+
+ this.context.beginPath();
+ this.context.arc(x, y, ticksize, 0, 6.2830, false);
+ this.context.fill();
+
+ // Square
+ } else if (tickmarks == 'square') {
+
+ this.context.beginPath();
+ this.context.fillRect(x - ticksize, y - ticksize, 2 * ticksize, 2 * ticksize);
+ this.context.fill();
+
+ // Diamond shape tickmarks
+ } else if (tickmarks == 'diamond') {
+
+ this.context.beginPath();
+ this.context.moveTo(x, y - ticksize);
+ this.context.lineTo(x + ticksize, y);
+ this.context.lineTo(x, y + ticksize);
+ this.context.lineTo(x - ticksize, y);
+ this.context.closePath();
+ this.context.fill();
+
+ // Plus style tickmarks
+ } else if (tickmarks == 'plus') {
+
+ this.context.lineWidth = 1;
+
+ this.context.beginPath();
+ this.context.moveTo(x, y - ticksize);
+ this.context.lineTo(x, y + ticksize);
+ this.context.moveTo(x - ticksize, y);
+ this.context.lineTo(x + ticksize, y);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * This function makes it much easier to get the (if any) point that is currently being hovered over.
+ *
+ * @param object e The event object
+ */
+ RGraph.Rscatter.prototype.getPoint = function (e)
+ {
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var context = obj.context;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var overHotspot = false;
+ var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
+
+ for (var i=0; i<obj.coords.length; ++i) {
+
+ var x = obj.coords[i][0];
+ var y = obj.coords[i][1];
+ var tooltip = obj.coords[i][3];
+
+ if (
+ mouseX < (x + offset) &&
+ mouseX > (x - offset) &&
+ mouseY < (y + offset) &&
+ mouseY > (y - offset) &&
+ typeof(tooltip) == 'string' &&
+ tooltip.length > 0
+ ) {
+
+ return [obj, x, y, i];
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.scatter.js b/schall/static/RGraph/libraries/RGraph.scatter.js
new file mode 100644
index 0000000..3fde9f3
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.scatter.js
@@ -0,0 +1,1662 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The scatter graph constructor
+ *
+ * @param object canvas The cxanvas object
+ * @param array data The chart data
+ */
+ RGraph.Scatter = function (id, data)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.canvas.__object__ = this;
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.max = 0;
+ this.coords = [];
+ this.data = [];
+ this.type = 'scatter';
+ this.isRGraph = true;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config properties
+ this.properties = {
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
+ 'chart.background.grid': true,
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.hsize': 20,
+ 'chart.background.grid.vsize': 20,
+ 'chart.background.hbars': null,
+ 'chart.background.vbars': null,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':true,
+ 'chart.background.grid.autofit.numhlines': 5,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.text.size': 10,
+ 'chart.text.angle': 0,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.hotspot': 3,
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.numyticks': 10,
+ 'chart.tickmarks': 'cross',
+ 'chart.ticksize': 5,
+ 'chart.xticks': true,
+ 'chart.xaxis': true,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.xmin': 0,
+ 'chart.xmax': 0,
+ 'chart.ymax': null,
+ 'chart.ymin': null,
+ 'chart.scale.decimals': null,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.title.xaxis': '',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': null,
+ 'chart.labels': [],
+ 'chart.labels.ingraph': null,
+ 'chart.labels.above': false,
+ 'chart.labels.above.size': 8,
+ 'chart.labels.above.decimals': 0,
+ 'chart.ylabels': true,
+ 'chart.ylabels.count': 5,
+ 'chart.ylabels.invert': false,
+ 'chart.ylabels.specific': null,
+ 'chart.ylabels.inside': false,
+ 'chart.contextmenu': null,
+ 'chart.defaultcolor': 'black',
+ 'chart.xaxispos': 'bottom',
+ 'chart.yaxispos': 'left',
+ 'chart.crosshairs': false,
+ 'chart.crosshairs.color': '#333',
+ 'chart.crosshairs.linewidth': 1,
+ 'chart.crosshairs.coords': false,
+ 'chart.crosshairs.coords.fixed':true,
+ 'chart.crosshairs.coords.fadeout':false,
+ 'chart.crosshairs.coords.labels.x': 'X',
+ 'chart.crosshairs.coords.labels.y': 'Y',
+ 'chart.crosshairs.hline': true,
+ 'chart.crosshairs.vline': true,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.line': false,
+ 'chart.line.linewidth': 1,
+ 'chart.line.colors': ['green', 'red'],
+ 'chart.line.shadow.color': 'rgba(0,0,0,0)',
+ 'chart.line.shadow.blur': 2,
+ 'chart.line.shadow.offsetx': 3,
+ 'chart.line.shadow.offsety': 3,
+ 'chart.line.stepped': false,
+ 'chart.noaxes': false,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.axis.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.boxplot.width': 8,
+ 'chart.boxplot.capped': true,
+ 'chart.resizable': false,
+ 'chart.resize.handle.background': null,
+ 'chart.xmin': 0,
+ 'chart.labels.specific.align': 'left',
+ 'chart.xscale': false,
+ 'chart.xscale.units.pre': '',
+ 'chart.xscale.units.post': '',
+ 'chart.xscale.numlabels': 10,
+ 'chart.xscale.formatter': null,
+ 'chart.noendxtick': false,
+ 'chart.noendytick': true
+ }
+
+ // Handle multiple datasets being given as one argument
+ if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0][0]) == 'number') {
+ // Store the data set(s)
+ for (var i=0; i<arguments[1].length; ++i) {
+ this.data[i] = arguments[1][i];
+ }
+
+ // Handle multiple data sets being supplied as seperate arguments
+ } else {
+ // Store the data set(s)
+ for (var i=1; i<arguments.length; ++i) {
+ this.data[i - 1] = arguments[i];
+ }
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[SCATTER] No canvas support');
+ return;
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getPoint;
+ }
+
+
+ /**
+ * A simple setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the property
+ */
+ RGraph.Scatter.prototype.Set = function (name, value)
+ {
+ /**
+ * This is here because the key expects a name of "chart.colors"
+ */
+ if (name == 'chart.line.colors') {
+ this.properties['chart.colors'] = value;
+ }
+
+ /**
+ * Allow compatibility with older property names
+ */
+ if (name == 'chart.tooltip.hotspot') {
+ name = 'chart.tooltips.hotspot';
+ }
+
+ /**
+ * chart.yaxispos should be left or right
+ */
+ if (name == 'chart.yaxispos' && value != 'left' && value != 'right') {
+ alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left");
+ value = 'left';
+ }
+
+ /**
+ * Check for xaxispos
+ */
+ if (name == 'chart.xaxispos' ) {
+ if (value != 'bottom' && value != 'center') {
+ alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center');
+ value = 'center';
+ }
+ }
+
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A simple getter
+ *
+ * @param string name The name of the property to set
+ */
+ RGraph.Scatter.prototype.Get = function (name)
+ {
+ return this.properties[name];
+ }
+
+
+ /**
+ * The function you call to draw the line chart
+ */
+ RGraph.Scatter.prototype.Draw = function ()
+ {
+ // MUST be the first thing done!
+ if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
+ RGraph.DrawBackgroundImage(this);
+ return;
+ }
+
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Go through all the data points and see if a tooltip has been given
+ this.Set('chart.tooltips', false);
+ this.hasTooltips = false;
+ var overHotspot = false;
+
+ // Reset the coords array
+ this.coords = [];
+
+ if (!RGraph.isIE8()) {
+ for (var i=0; i<this.data.length; ++i) {
+ for (var j =0;j<this.data[i].length; ++j) {
+ if (this.data[i][j] && this.data[i][j][3] && typeof(this.data[i][j][3]) == 'string' && this.data[i][j][3].length) {
+ this.Set('chart.tooltips', [1]); // An array
+ this.hasTooltips = true;
+ }
+ }
+ }
+ }
+
+ // Reset the maximum value
+ this.max = 0;
+
+ // Work out the maximum Y value
+ if (this.Get('chart.ymax') && this.Get('chart.ymax') > 0) {
+
+ this.scale = [];
+ this.max = this.Get('chart.ymax');
+ this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+
+ this.scale[0] = ((this.max - this.min) * (1/5)) + this.min;
+ this.scale[1] = ((this.max - this.min) * (2/5)) + this.min;
+ this.scale[2] = ((this.max - this.min) * (3/5)) + this.min;
+ this.scale[3] = ((this.max - this.min) * (4/5)) + this.min;
+ this.scale[4] = ((this.max - this.min) * (5/5)) + this.min;
+
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale = [
+ Number(this.scale[0]).toFixed(decimals),
+ Number(this.scale[1]).toFixed(decimals),
+ Number(this.scale[2]).toFixed(decimals),
+ Number(this.scale[3]).toFixed(decimals),
+ Number(this.scale[4]).toFixed(decimals)
+ ];
+
+ } else {
+
+ var i = 0;
+ var j = 0;
+
+ for (i=0; i<this.data.length; ++i) {
+ for (j=0; j<this.data[i].length; ++j) {
+ this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RGraph.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
+ }
+ }
+
+ this.scale = RGraph.getScale(this.max, this);
+
+ this.max = this.scale[4];
+ this.min = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+
+ if (this.min) {
+ this.scale[0] = ((this.max - this.min) * (1/5)) + this.min;
+ this.scale[1] = ((this.max - this.min) * (2/5)) + this.min;
+ this.scale[2] = ((this.max - this.min) * (3/5)) + this.min;
+ this.scale[3] = ((this.max - this.min) * (4/5)) + this.min;
+ this.scale[4] = ((this.max - this.min) * (5/5)) + this.min;
+ }
+
+
+ if (typeof(this.Get('chart.scale.decimals')) == 'number') {
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale = [
+ Number(this.scale[0]).toFixed(decimals),
+ Number(this.scale[1]).toFixed(decimals),
+ Number(this.scale[2]).toFixed(decimals),
+ Number(this.scale[3]).toFixed(decimals),
+ Number(this.scale[4]).toFixed(decimals)
+ ];
+ }
+ }
+
+ this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom;
+
+ // Progressively Draw the chart
+ RGraph.background.Draw(this);
+
+ /**
+ * Draw any horizontal bars that have been specified
+ */
+ if (this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length) {
+ RGraph.DrawBars(this);
+ }
+
+ /**
+ * Draw any vertical bars that have been specified
+ */
+ if (this.Get('chart.background.vbars') && this.Get('chart.background.vbars').length) {
+ this.DrawVBars();
+ }
+
+ if (!this.Get('chart.noaxes')) {
+ this.DrawAxes();
+ }
+
+ this.DrawLabels();
+
+ i = 0;
+ for(i=0; i<this.data.length; ++i) {
+ this.DrawMarks(i);
+
+ // Set the shadow
+ this.context.shadowColor = this.Get('chart.line.shadow.color');
+ this.context.shadowOffsetX = this.Get('chart.line.shadow.offsetx');
+ this.context.shadowOffsetY = this.Get('chart.line.shadow.offsety');
+ this.context.shadowBlur = this.Get('chart.line.shadow.blur');
+
+ this.DrawLine(i);
+
+ // Turn the shadow off
+ RGraph.NoShadow(this);
+ }
+
+
+ if (this.Get('chart.line')) {
+ for (var i=0;i<this.data.length; ++i) {
+ this.DrawMarks(i); // Call this again so the tickmarks appear over the line
+ }
+ }
+
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Install the event handler for tooltips
+ */
+ if (this.hasTooltips) {
+
+ /**
+ * Register all charts
+ */
+ RGraph.Register(this);
+
+ var overHotspot = false;
+
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var context = obj.context;
+ var mouseCoords = RGraph.getMouseXY(e);
+ var point = obj.getPoint(e);
+ var overHotspot = false;
+
+ if (point) {
+
+ var __dataset__ = point[2];
+ var __index__ = point[3];
+ var __text__ = point[4];
+ var overHotspot = true;
+
+
+
+ if (point[4]) {
+ canvas.style.cursor = 'pointer';
+
+ if (
+ !RGraph.Registry.Get('chart.tooltip') ||
+ RGraph.Registry.Get('chart.tooltip').__text__ != __text__ ||
+ RGraph.Registry.Get('chart.tooltip').__index__ != __index__ ||
+ RGraph.Registry.Get('chart.tooltip').__dataset__ != __dataset__
+ ) {
+
+ if (obj.Get('chart.tooltips.highlight')) {
+ RGraph.Redraw();
+ }
+
+ /**
+ * Get the tooltip text
+ */
+ if (typeof(__text__) == 'function') {
+ var text = String(__text__(i));
+
+ } else {
+ var text = String(__text__);
+ }
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, __index__);
+
+ RGraph.Registry.Get('chart.tooltip').__index__ = __index__;
+
+ if (RGraph.Registry.Get('chart.tooltip')) {
+ RGraph.Registry.Get('chart.tooltip').__dataset__ = __dataset__;
+ }
+
+
+
+ /**
+ * Draw a circle around the mark. Also highlight the boxplot if necessary
+ */
+ if (obj.Get('chart.tooltips.highlight') && typeof(point[0]) == 'object') {
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.fillStyle = 'rgba(255,255,255,0.5)';
+ context.strokeRect(point[0][0], point[1][0], point[0][1], point[1][1]);
+ context.fillRect(point[0][0], point[1][0], point[0][1], point[1][1]);
+ context.stroke();
+ context.fill();
+
+ } else if (obj.Get('chart.tooltips.highlight') && typeof(point[0]) == 'number') {
+ context.beginPath();
+ context.fillStyle = 'rgba(255,255,255,0.5)';
+ context.arc(point[0], point[1], 3, 0, 6.28, 0);
+ context.fill();
+ }
+ }
+ }
+ }
+
+ /**
+ * Reset the pointer
+ */
+ if (!overHotspot || !point[4]) {
+ canvas.style.cursor = 'default';
+ }
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+
+ /**
+ * Draw the key if necessary
+ */
+ if (this.Get('chart.key') && this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.line.colors'));
+ }
+
+
+ /**
+ * Draw " above" labels if enabled
+ */
+ if (this.Get('chart.labels.above')) {
+ this.DrawAboveLabels();
+ }
+
+ /**
+ * Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
+ */
+ this.DrawInGraphLabels(this);
+
+
+ /**
+ * Draw crosschairs
+ */
+ RGraph.DrawCrosshairs(this);
+
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the axes of the scatter graph
+ */
+ RGraph.Scatter.prototype.DrawAxes = function ()
+ {
+ var canvas = this.canvas;
+ var context = this.context;
+ var graphHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+
+ context.beginPath();
+ context.strokeStyle = this.Get('chart.axis.color');
+ context.lineWidth = 1;
+
+ // Draw the Y axis
+ if (this.Get('chart.yaxispos') == 'left') {
+ context.moveTo(this.gutterLeft, this.gutterTop);
+ context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ } else {
+ context.moveTo(RGraph.GetWidth(this) - this.gutterRight, this.gutterTop);
+ context.lineTo(RGraph.GetWidth(this) - this.gutterRight, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+
+
+ // Draw the X axis
+ if (this.Get('chart.xaxis')) {
+ if (this.Get('chart.xaxispos') == 'center') {
+ context.moveTo(this.gutterLeft, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2));
+ context.lineTo(this.canvas.width - this.gutterRight, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2));
+ } else {
+ context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom);
+ }
+ }
+
+ /**
+ * Draw the Y tickmarks
+ */
+ var numyticks = this.Get('chart.numyticks');
+
+ for (y=this.gutterTop; y < this.canvas.height - this.gutterBottom + (this.Get('chart.xaxispos') == 'center' ? 1 : 0) ; y+=(graphHeight / numyticks)) {
+
+ // This is here to accomodate the X axis being at the center
+ if (y == (this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2)) ) continue;
+
+ if (this.Get('chart.yaxispos') == 'left') {
+ context.moveTo(this.gutterLeft, y);
+ context.lineTo(this.gutterLeft - 3, y);
+ } else {
+ context.moveTo(this.canvas.width - this.gutterRight +3, y);
+ context.lineTo(this.canvas.width - this.gutterRight, y);
+ }
+
+ /**
+ * Draw an extra tick if the X axis isn't being shown
+ */
+ if (this.Get('chart.xaxis') == false && this.Get('chart.noendytick') == false) {
+ this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft - 3, this.canvas.height - this.gutterBottom);
+ }
+ }
+
+
+ /**
+ * Draw the X tickmarks
+ */
+ if (this.Get('chart.xticks') && this.Get('chart.xaxis')) {
+ var x = 0;
+ var y = (this.Get('chart.xaxispos') == 'center') ? this.gutterTop + (this.grapharea / 2): (this.canvas.height - this.gutterBottom);
+ this.xTickGap = (this.Get('chart.labels') && this.Get('chart.labels').length) ? ((this.canvas.width - this.gutterLeft - this.gutterRight ) / this.Get('chart.labels').length) : (this.canvas.width - this.gutterLeft - this.gutterRight) / 10;
+
+ for (x = (this.gutterLeft + (this.Get('chart.yaxispos') == 'left' ? this.xTickGap : 0) );
+ x <= (this.canvas.width - this.gutterRight - (this.Get('chart.yaxispos') == 'left' ? 0 : 1));
+ x += this.xTickGap) {
+
+ if (this.Get('chart.yaxispos') == 'left' && this.Get('chart.noendxtick') == true && x == (this.canvas.width - this.gutterRight) ) {
+ continue;
+ } else if (this.Get('chart.yaxispos') == 'right' && this.Get('chart.noendxtick') == true && x == this.gutterLeft) {
+ continue;
+ }
+
+ context.moveTo(x, y - (this.Get('chart.xaxispos') == 'center' ? 3 : 0));
+ context.lineTo(x, y + 3);
+ }
+ }
+
+ context.stroke();
+ }
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * Draws the labels on the scatter graph
+ */
+ RGraph.Scatter.prototype.DrawLabels = function ()
+ {
+ this.context.fillStyle = this.Get('chart.text.color');
+ var font = this.Get('chart.text.font');
+ var xMin = this.Get('chart.xmin');
+ var xMax = this.Get('chart.xmax');
+ var yMax = this.scale[4];
+ var yMin = this.Get('chart.ymin');
+ var text_size = this.Get('chart.text.size');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var numYLabels = this.Get('chart.ylabels.count');
+ var invert = this.Get('chart.ylabels.invert');
+ var inside = this.Get('chart.ylabels.inside');
+ var context = this.context;
+ var canvas = this.canvas;
+ var boxed = false;
+
+ this.halfTextHeight = text_size / 2;
+
+
+ this.halfGraphHeight = (this.canvas.height - this.gutterTop - this.gutterBottom) / 2;
+
+ /**
+ * Draw the Y yaxis labels, be it at the top or center
+ */
+ if (this.Get('chart.ylabels')) {
+
+ var xPos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : RGraph.GetWidth(this) - this.gutterRight + 5;
+ var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right';
+
+ /**
+ * Now change the two things above if chart.ylabels.inside is specified
+ */
+ if (inside) {
+ if (this.Get('chart.yaxispos') == 'left') {
+ xPos = this.Get('chart.gutter.left') + 5;
+ align = 'left';
+ boxed = true;
+ } else {
+ xPos = this.canvas.width - this.Get('chart.gutter.right') - 5;
+ align = 'right';
+ boxed = true;
+ }
+ }
+
+ if (this.Get('chart.xaxispos') == 'center') {
+
+
+ /**
+ * Specific Y labels
+ */
+ if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific') != null && this.Get('chart.ylabels.specific').length) {
+
+ var labels = this.Get('chart.ylabels.specific');
+
+ if (this.Get('chart.ymin') > 0) {
+ labels = [];
+ for (var i=0; i<(this.Get('chart.ylabels.specific').length - 1); ++i) {
+ labels.push(this.Get('chart.ylabels.specific')[i]);
+ }
+ }
+
+ for (var i=0; i<labels.length; ++i) {
+ var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
+ RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align, boxed);
+ }
+
+ var reversed_labels = RGraph.array_reverse(labels);
+
+ for (var i=0; i<reversed_labels.length; ++i) {
+ var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
+ RGraph.Text(context,font, text_size, xPos, y, reversed_labels[i], 'center', align, boxed);
+ }
+
+ /**
+ * Draw the center label if chart.ymin is specified
+ */
+ if (this.Get('chart.ymin') > 0) {
+ RGraph.Text(context, font, text_size, xPos, (this.grapharea / 2) + this.Get('chart.gutter.top'), this.Get('chart.ylabels.specific')[this.Get('chart.ylabels.specific').length - 1], 'center', align, boxed);
+ }
+
+ return;
+ }
+
+
+ if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
+ // Draw the top halves labels
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed);
+
+
+ if (numYLabels >= 5) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed);
+ }
+
+ // Draw the bottom halves labels
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (1/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (3/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed);
+ }
+
+ if (numYLabels == 5) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (2/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (4/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed);
+ }
+
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (5/10) ) + this.halfGraphHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed);
+
+ } else if (numYLabels == 10) {
+ // 10 Y labels
+ var interval = (this.grapharea / numYLabels) / 2;
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/20) ),RGraph.number_format(this,(this.max - (this.max * (i/10))).toFixed(this.Get('chart.scale.decimals')),units_pre, units_post),'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/20) ) + (this.grapharea / 2) + (this.grapharea / 20),'-' + RGraph.number_format(this, ((this.max * (i/10)) + (this.max * (1/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post), 'center', align, boxed);
+ }
+
+ } else {
+ alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only');
+ }
+
+ } else {
+
+ var xPos = this.Get('chart.yaxispos') == 'left' ? this.gutterLeft - 5 : this.canvas.width - this.gutterRight + 5;
+ var align = this.Get('chart.yaxispos') == 'right' ? 'left' : 'right';
+
+ if (inside) {
+ if (this.Get('chart.yaxispos') == 'left') {
+ xPos = this.Get('chart.gutter.left') + 5;
+ align = 'left';
+ boxed = true;
+ } else {
+ xPos = this.canvas.width - this.Get('chart.gutter.right') - 5;
+ align = 'right';
+ boxed = true;
+ }
+ }
+ /**
+ * Specific Y labels
+ */
+ if (typeof(this.Get('chart.ylabels.specific')) == 'object' && this.Get('chart.ylabels.specific')) {
+
+ var labels = this.Get('chart.ylabels.specific');
+
+ // Lose the last label
+ if (this.Get('chart.ymin') > 0) {
+ labels = [];
+ for (var i=0; i<(this.Get('chart.ylabels.specific').length - 1); ++i) {
+ labels.push(this.Get('chart.ylabels.specific')[i]);
+ }
+ }
+
+ for (var i=0; i<labels.length; ++i) {
+ var y = this.gutterTop + (i * (this.grapharea / labels.length) );
+
+ RGraph.Text(context, font, text_size, xPos, y, labels[i], 'center', align, boxed);
+ }
+
+ /**
+ * Draw the center label if chart.ymin is specified
+ */
+ if (this.Get('chart.ymin') > 0) {
+ RGraph.Text(context, font, text_size, xPos, this.canvas.height - this.Get('chart.gutter.bottom'), this.Get('chart.ylabels.specific')[this.Get('chart.ylabels.specific').length - 1], 'center', align, boxed);
+ }
+
+ return;
+ }
+
+ if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) {
+ if (invert) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, 0, units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) * (5/5) ), RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed);
+
+ if (numYLabels >= 5) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed);
+ }
+ } else {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop, RGraph.number_format(this, this.scale[4], units_pre, units_post), 'center', align, boxed);
+
+ if (numYLabels >= 5) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (1/5) ), RGraph.number_format(this, this.scale[3], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (3/5) ), RGraph.number_format(this, this.scale[1], units_pre, units_post), 'center', align, boxed);
+ }
+
+ if (numYLabels >= 3) {
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (2/5) ), RGraph.number_format(this, this.scale[2], units_pre, units_post), 'center', align, boxed);
+ RGraph.Text(context, font, text_size, xPos, this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (4/5) ), RGraph.number_format(this, this.scale[0], units_pre, units_post), 'center', align, boxed);
+ }
+ }
+ } else if (numYLabels == 10) {
+ var interval = (this.grapharea / numYLabels) / 2;
+ if (invert) {
+ for (var i=numYLabels; i>=0; --i) {
+ RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * ((10-i)/10) ),RGraph.number_format(this,(this.max - (this.max * (i/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post),'center', align, boxed);
+ }
+ } else {
+ // 10 Y labels
+ for (var i=0; i<numYLabels; ++i) {
+
+ RGraph.Text(context, font, text_size, xPos,this.gutterTop + ((this.canvas.height - this.gutterTop - this.gutterBottom) * (i/10) ),RGraph.number_format(this, (this.max - ((this.max - this.min) * (i/10))).toFixed((this.Get('chart.scale.decimals'))), units_pre, units_post),'center', align, boxed);
+ }
+ }
+ } else {
+ alert('[SCATTER SCALE] Number of Y labels can be 1/3/5/10 only');
+ }
+
+ if (this.Get('chart.ymin')) {
+ RGraph.Text(context, font, text_size, xPos, this.canvas.height - this.gutterBottom,RGraph.number_format(this, this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals')), units_pre, units_post),'center', align, boxed);
+ }
+ }
+ }
+
+
+
+
+ /**
+ * Draw an X scale
+ */
+ if (this.Get('chart.xscale')) {
+
+ var numXLabels = this.Get('chart.xscale.numlabels');
+ var interval = (this.canvas.width - this.gutterLeft - this.gutterRight) / numXLabels;
+ var y = this.canvas.height - this.gutterBottom + 5 + (text_size / 2);
+ var units_pre_x = this.Get('chart.xscale.units.pre');
+ var units_post_x = this.Get('chart.xscale.units.post');
+
+
+ if (!this.Get('chart.xmax')) {
+ var xmax = 0;
+
+ for (var ds=0; ds<this.data.length; ++ds) {
+ for (var point=0; point<this.data[ds].length; ++point) {
+ xmax = Math.max(xmax, this.data[ds][point][0]);
+ }
+ }
+
+ this.Set('chart.xmax', RGraph.getScale(xmax)[4]);
+ }
+
+
+ for (var i=0; i<numXLabels; ++i) {
+
+ var num = ( (this.Get('chart.xmax') - this.Get('chart.xmin')) * ((i+1) / numXLabels)) + this.Get('chart.xmin');
+ var x = this.gutterLeft + ((i+1) * interval);
+
+ if (typeof(this.Get('chart.xscale.formatter')) == 'function') {
+ var text = this.Get('chart.xscale.formatter')(this, num);
+ } else {
+ var text = RGraph.number_format(this,
+ num.toFixed(this.Get('chart.scale.decimals')),
+ units_pre_x,
+ units_post_x);
+ }
+
+ RGraph.Text(context, font, text_size, x, y, text, 'center', 'center');
+ }
+
+ /**
+ * Draw X labels
+ */
+ } else {
+ // Put the text on the X axis
+ var graphArea = this.canvas.width - this.gutterLeft - this.gutterRight;
+ var xInterval = graphArea / this.Get('chart.labels').length;
+ var xPos = this.gutterLeft;
+ var yPos = (this.canvas.height - this.gutterBottom) + 15;
+ var labels = this.Get('chart.labels');
+
+ /**
+ * Text angle
+ */
+ var angle = 0;
+ var valign = null;
+ var halign = 'center';
+
+ if (this.Get('chart.text.angle') > 0) {
+ angle = -1 * this.Get('chart.text.angle');
+ valign = 'center';
+ halign = 'right';
+ yPos -= 10;
+ }
+
+ for (i=0; i<labels.length; ++i) {
+
+ if (typeof(labels[i]) == 'object') {
+
+ if (this.Get('chart.labels.specific.align') == 'center') {
+ var rightEdge = 0;
+
+ if (labels[i+1] && labels[i+1][1]) {
+ rightEdge = labels[i+1][1];
+ } else {
+ rightEdge = this.Get('chart.xmax');
+ }
+
+ var offset = (rightEdge - labels[i][1]) / 2;
+
+ } else {
+ var offset = 0;
+ }
+
+
+ RGraph.Text(context,
+ font,
+ this.Get('chart.text.size'),
+ this.gutterLeft + (graphArea * ((labels[i][1] - xMin + offset) / (this.Get('chart.xmax') - xMin))) + 5,
+ yPos,
+ String(labels[i][0]),
+ valign,
+ angle != 0 ? 'right' : (this.Get('chart.labels.specific.align') == 'center' ? 'center' : 'left'),
+ null,
+ angle
+ );
+
+ /**
+ * Draw the gray indicator line
+ */
+ this.context.beginPath();
+ this.context.strokeStyle = '#bbb';
+ this.context.moveTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (this.Get('chart.xmax') - xMin))), RGraph.GetHeight(this) - this.gutterBottom + 20);
+ this.context.stroke();
+
+ } else {
+ RGraph.Text(context, font, this.Get('chart.text.size'), xPos + (this.xTickGap / 2), yPos, String(labels[i]), valign, halign, null, angle);
+ }
+
+ // Do this for the next time around
+ xPos += xInterval;
+ }
+
+ /**
+ * Draw the final indicator line
+ */
+ if (typeof(labels[0]) == 'object') {
+ this.context.beginPath();
+ this.context.strokeStyle = '#bbb';
+ this.context.moveTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft + graphArea, RGraph.GetHeight(this) - this.gutterBottom + 20);
+ this.context.stroke();
+ }
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /**
+ * Draws the actual scatter graph marks
+ *
+ * @param i integer The dataset index
+ */
+ RGraph.Scatter.prototype.DrawMarks = function (i)
+ {
+ /**
+ * Reset the coords array
+ */
+ this.coords[i] = [];
+
+ /**
+ * Plot the values
+ */
+ var xmax = this.Get('chart.xmax');
+ var default_color = this.Get('chart.defaultcolor');
+
+ for (var j=0; j<this.data[i].length; ++j) {
+ /**
+ * This is here because tooltips are optional
+ */
+ var data_point = this.data[i];
+
+ var xCoord = data_point[j][0];
+ var yCoord = data_point[j][1];
+ var color = data_point[j][2] ? data_point[j][2] : default_color;
+ var tooltip = (data_point[j] && data_point[j][3]) ? data_point[j][3] : null;
+
+
+ this.DrawMark(
+ i,
+ xCoord,
+ yCoord,
+ xmax,
+ this.scale[4],
+ color,
+ tooltip,
+ this.coords[i],
+ data_point
+ );
+ }
+ }
+
+
+ /**
+ * Draws a single scatter mark
+ */
+ RGraph.Scatter.prototype.DrawMark = function (index, x, y, xMax, yMax, color, tooltip, coords, data)
+ {
+ /**
+ * Inverted Y scale handling
+ */
+ if (this.Get('chart.ylabels.invert')) {
+ if (typeof(y) == 'number') {
+ y = yMax - y;
+ }
+ }
+
+ var tickmarks = this.Get('chart.tickmarks');
+ var tickSize = this.Get('chart.ticksize');
+ var xMin = this.Get('chart.xmin');
+ var x = ((x - xMin) / (xMax - xMin)) * (this.canvas.width - this.gutterLeft - this.gutterRight);
+ var originalX = x;
+ var originalY = y;
+
+
+ /**
+ * This allows chart.tickmarks to be an array
+ */
+
+ if (tickmarks && typeof(tickmarks) == 'object') {
+ tickmarks = tickmarks[index];
+ }
+
+
+ /**
+ * This allows chart.ticksize to be an array
+ */
+ if (typeof(tickSize) == 'object') {
+ var tickSize = tickSize[index];
+ var halfTickSize = tickSize / 2;
+ } else {
+ var halfTickSize = tickSize / 2;
+ }
+
+
+ /**
+ * This bit is for boxplots only
+ */
+ if ( typeof(y) == 'object'
+ && typeof(y[0]) == 'number'
+ && typeof(y[1]) == 'number'
+ && typeof(y[2]) == 'number'
+ && typeof(y[3]) == 'number'
+ && typeof(y[4]) == 'number'
+ ) {
+
+ var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+ this.Set('chart.boxplot', true);
+ this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom;
+
+ if (this.Get('chart.xaxispos') == 'center') {
+ this.graphheight /= 2;
+ }
+
+ var y0 = (this.graphheight) - ((y[4] - yMin) / (yMax - yMin)) * (this.graphheight);
+ var y1 = (this.graphheight) - ((y[3] - yMin) / (yMax - yMin)) * (this.graphheight);
+ var y2 = (this.graphheight) - ((y[2] - yMin) / (yMax - yMin)) * (this.graphheight);
+ var y3 = (this.graphheight) - ((y[1] - yMin) / (yMax - yMin)) * (this.graphheight);
+ var y4 = (this.graphheight) - ((y[0] - yMin) / (yMax - yMin)) * (this.graphheight);
+
+ /**
+ * Inverted labels
+ */
+ if (this.Get('chart.ylabels.invert')) {
+ y0 = this.graphheight - y0;
+ y1 = this.graphheight - y1;
+ y2 = this.graphheight - y2;
+ y3 = this.graphheight - y3;
+ y4 = this.graphheight - y4;
+ }
+
+ var col1 = y[5];
+ var col2 = y[6];
+
+ // Override the boxWidth
+ if (typeof(y[7]) == 'number') {
+ var boxWidth = y[7];
+ }
+
+ var y = this.graphheight - y2;
+
+ } else {
+ var yMin = this.Get('chart.ymin') ? this.Get('chart.ymin') : 0;
+ var y = (( (y - yMin) / (yMax - yMin)) * (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom));
+ }
+
+ /**
+ * Account for the X axis being at the centre
+ */
+ if (this.Get('chart.xaxispos') == 'center') {
+ y /= 2;
+ y += this.halfGraphHeight;
+ }
+
+ // This is so that points are on the graph, and not the gutter
+ x += this.gutterLeft;
+ y = this.canvas.height - this.gutterBottom - y;
+
+ this.context.beginPath();
+
+ // Color
+ this.context.strokeStyle = color;
+
+ /**
+ * Boxplots
+ */
+ if ( this.Get('chart.boxplot')
+ && typeof(y0) == 'number'
+ && typeof(y1) == 'number'
+ && typeof(y2) == 'number'
+ && typeof(y3) == 'number'
+ && typeof(y4) == 'number'
+ ) {
+
+ var boxWidth = boxWidth ? boxWidth : this.Get('chart.boxplot.width');
+ var halfBoxWidth = boxWidth / 2;
+
+ this.context.beginPath();
+
+ // Draw the upper coloured box if a value is specified
+ if (col1) {
+ this.context.fillStyle = col1;
+ this.context.fillRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y2 - y1);
+ }
+
+ // Draw the lower coloured box if a value is specified
+ if (col2) {
+ this.context.fillStyle = col2;
+ this.context.fillRect(x - halfBoxWidth, y2 + this.gutterTop, boxWidth, y3 - y2);
+ }
+
+ this.context.strokeRect(x - halfBoxWidth, y1 + this.gutterTop, boxWidth, y3 - y1);
+ this.context.stroke();
+
+ // Now draw the whiskers
+ this.context.beginPath();
+ if (this.Get('chart.boxplot.capped')) {
+ this.context.moveTo(x - halfBoxWidth, y0 + this.gutterTop);
+ this.context.lineTo(x + halfBoxWidth, y0 + this.gutterTop);
+ }
+
+ this.context.moveTo(x, y0 + this.gutterTop);
+ this.context.lineTo(x, y1 + this.gutterTop);
+
+ if (this.Get('chart.boxplot.capped')) {
+ this.context.moveTo(x - halfBoxWidth, y4 + this.gutterTop);
+ this.context.lineTo(x + halfBoxWidth, y4 + this.gutterTop);
+ }
+
+ this.context.moveTo(x, y4 + this.gutterTop);
+ this.context.lineTo(x, y3 + this.gutterTop);
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draw the tickmark, but not for boxplots
+ */
+ if (!y0 && !y1 && !y2 && !y3 && !y4) {
+
+ this.graphheight = this.canvas.height - this.gutterTop - this.gutterBottom;
+
+
+
+ if (tickmarks == 'circle') {
+ this.context.arc(x, y, halfTickSize, 0, 6.28, 0);
+ this.context.fillStyle = color;
+ this.context.fill();
+
+ } else if (tickmarks == 'plus') {
+
+ this.context.moveTo(x, y - halfTickSize);
+ this.context.lineTo(x, y + halfTickSize);
+ this.context.moveTo(x - halfTickSize, y);
+ this.context.lineTo(x + halfTickSize, y);
+ this.context.stroke();
+
+ } else if (tickmarks == 'square') {
+ this.context.strokeStyle = color;
+ this.context.fillStyle = color;
+ this.context.fillRect(
+ x - halfTickSize,
+ y - halfTickSize,
+ tickSize,
+ tickSize
+ );
+ //this.context.fill();
+
+ } else if (tickmarks == 'cross') {
+
+ this.context.moveTo(x - halfTickSize, y - halfTickSize);
+ this.context.lineTo(x + halfTickSize, y + halfTickSize);
+ this.context.moveTo(x + halfTickSize, y - halfTickSize);
+ this.context.lineTo(x - halfTickSize, y + halfTickSize);
+
+ this.context.stroke();
+
+ /**
+ * Diamond shape tickmarks
+ */
+ } else if (tickmarks == 'diamond') {
+ this.context.fillStyle = this.context.strokeStyle;
+
+ this.context.moveTo(x, y - halfTickSize);
+ this.context.lineTo(x + halfTickSize, y);
+ this.context.lineTo(x, y + halfTickSize);
+ this.context.lineTo(x - halfTickSize, y);
+ this.context.lineTo(x, y - halfTickSize);
+
+ this.context.fill();
+ this.context.stroke();
+
+ /**
+ * Custom tickmark style
+ */
+ } else if (typeof(tickmarks) == 'function') {
+
+ var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight
+ var xVal = ((x - this.gutterLeft) / graphWidth) * xMax;
+ var yVal = ((this.graphheight - (y - this.gutterTop)) / this.graphheight) * yMax;
+
+ tickmarks(this, data, x, y, xVal, yVal, xMax, yMax, color)
+
+ /**
+ * No tickmarks
+ */
+ } else if (tickmarks == null) {
+
+ /**
+ * Unknown tickmark type
+ */
+ } else {
+ alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
+ }
+ }
+
+ /**
+ * Add the tickmark to the coords array
+ */
+ if ( this.Get('chart.boxplot')
+ && typeof(y0) == 'number'
+ && typeof(y1) == 'number'
+ && typeof(y2) == 'number'
+ && typeof(y3) == 'number'
+ && typeof(y4) == 'number') {
+
+ x = [x - halfBoxWidth, x + halfBoxWidth];
+ y = [y0 + this.gutterTop, y1 + this.gutterTop, y2 + this.gutterTop, y3 + this.gutterTop, y4 + this.gutterTop];
+
+ }
+
+ coords.push([x, y, tooltip]);
+ }
+
+
+ /**
+ * Draws an optional line connecting the tick marks.
+ *
+ * @param i The index of the dataset to use
+ */
+ RGraph.Scatter.prototype.DrawLine = function (i)
+ {
+ if (this.Get('chart.line') && this.coords[i].length >= 2) {
+
+ this.context.lineCap = 'round';
+ this.context.lineJoin = 'round';
+ this.context.lineWidth = this.GetLineWidth(i);// i is the index of the set of coordinates
+ this.context.strokeStyle = this.Get('chart.line.colors')[i];
+ this.context.beginPath();
+
+ var len = this.coords[i].length;
+
+ for (var j=0; j<this.coords[i].length; ++j) {
+
+ var xPos = this.coords[i][j][0];
+ var yPos = this.coords[i][j][1];
+
+ if (j == 0) {
+ this.context.moveTo(xPos, yPos);
+ } else {
+
+ // Stepped?
+ var stepped = this.Get('chart.line.stepped');
+
+ if ( (typeof(stepped) == 'boolean' && stepped)
+ || (typeof(stepped) == 'object' && stepped[i])
+ ) {
+ this.context.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
+ }
+
+ this.context.lineTo(xPos, yPos);
+ }
+ }
+
+ this.context.stroke();
+ }
+
+ /**
+ * Set the linewidth back to 1
+ */
+ this.context.lineWidth = 1;
+ }
+
+
+ /**
+ * Returns the linewidth
+ *
+ * @param number i The index of the "line" (/set of coordinates)
+ */
+ RGraph.Scatter.prototype.GetLineWidth = function (i)
+ {
+ var linewidth = this.Get('chart.line.linewidth');
+
+ if (typeof(linewidth) == 'number') {
+ return linewidth;
+
+ } else if (typeof(linewidth) == 'object') {
+ if (linewidth[i]) {
+ return linewidth[i];
+ } else {
+ return linewidth[0];
+ }
+
+ alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');
+ }
+ }
+
+
+ /**
+ * Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
+ * is not common
+ */
+ RGraph.Scatter.prototype.DrawVBars = function ()
+ {
+ var canvas = this.canvas;
+ var context = this.context;
+ var vbars = this.Get('chart.background.vbars');
+ var graphWidth = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
+
+ if (vbars) {
+
+ var xmax = this.Get('chart.xmax');
+
+ for (var i=0; i<vbars.length; ++i) {
+ var startX = ((vbars[i][0] / xmax) * graphWidth) + this.gutterLeft;
+ var width = (vbars[i][1] / xmax) * graphWidth;
+
+ context.beginPath();
+ context.fillStyle = vbars[i][2];
+ context.fillRect(startX, this.gutterTop, width, (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom));
+ context.fill();
+ }
+ }
+ }
+
+
+
+
+
+ /**
+ * Draws in-graph labels.
+ *
+ * @param object obj The graph object
+ */
+ RGraph.Scatter.prototype.DrawInGraphLabels = function (obj)
+ {
+ var canvas = obj.canvas;
+ var context = obj.context;
+ var labels = obj.Get('chart.labels.ingraph');
+ var labels_processed = [];
+
+ // Defaults
+ var fgcolor = 'black';
+ var bgcolor = 'white';
+ var direction = 1;
+
+ if (!labels) {
+ return;
+ }
+
+ /**
+ * Preprocess the labels array. Numbers are expanded
+ */
+ for (var i=0; i<labels.length; ++i) {
+ if (typeof(labels[i]) == 'number') {
+ for (var j=0; j<labels[i]; ++j) {
+ labels_processed.push(null);
+ }
+ } else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
+ labels_processed.push(labels[i]);
+
+ } else {
+ labels_processed.push('');
+ }
+ }
+
+ /**
+ * Turn off any shadow
+ */
+ RGraph.NoShadow(obj);
+
+ if (labels_processed && labels_processed.length > 0) {
+
+ var i=0;
+
+ for (var set=0; set<obj.coords.length; ++set) {
+ for (var point = 0; point<obj.coords[set].length; ++point) {
+ if (labels_processed[i]) {
+ var x = obj.coords[set][point][0];
+ var y = obj.coords[set][point][1];
+ var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
+
+ var text_x = x;
+ var text_y = y - 5 - length;
+
+ context.moveTo(x, y - 5);
+ context.lineTo(x, y - 5 - length);
+
+ context.stroke();
+ context.beginPath();
+
+ // This draws the arrow
+ context.moveTo(x, y - 5);
+ context.lineTo(x - 3, y - 10);
+ context.lineTo(x + 3, y - 10);
+ context.closePath();
+
+
+ context.beginPath();
+
+ // Fore ground color
+ context.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
+
+ RGraph.Text(context,
+ obj.Get('chart.text.font'),
+ obj.Get('chart.text.size'),
+ text_x,
+ text_y,
+ (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
+ 'bottom',
+ 'center',
+ true,
+ null,
+ (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white');
+ context.fill();
+ }
+
+ i++;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This function makes it much easier to get the (if any) point that is currently being hovered over.
+ *
+ * @param object e The event object
+ */
+ RGraph.Scatter.prototype.getPoint = function (e)
+ {
+ var canvas = e.target;
+ var obj = canvas.__object__;
+ var context = obj.context;
+ var mouseXY = RGraph.getMouseXY(e);
+ var mouseX = mouseXY[0];
+ var mouseY = mouseXY[1];
+ var overHotspot = false;
+ var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
+
+ for (var set=0; set<obj.coords.length; ++set) {
+ for (var i=0; i<obj.coords[set].length; ++i) {
+
+ var xCoord = obj.coords[set][i][0];
+ var yCoord = obj.coords[set][i][1];
+
+ if (typeof(yCoord) == 'number') {
+ if (mouseX <= (xCoord + offset) &&
+ mouseX >= (xCoord - offset) &&
+ mouseY <= (yCoord + offset) &&
+ mouseY >= (yCoord - offset)) {
+
+ return [xCoord, yCoord, set, i, obj.data[set][i][3]];
+ }
+ } else {
+
+ var mark = obj.data[set][i];
+
+
+ /**
+ * Determine the width
+ */
+ var width = obj.Get('chart.boxplot.width');
+
+ if (typeof(mark[1][7]) == 'number') {
+ width = mark[1][7];
+ }
+
+ if ( typeof(xCoord) == 'object'
+ && mouseX > xCoord[0]
+ && mouseX < xCoord[1]
+ && mouseY < yCoord[3]
+ && mouseY > yCoord[1]
+ ) {
+
+ return [[xCoord[0], xCoord[1] - xCoord[0]], [yCoord[1], yCoord[3] - yCoord[1]], set, i, obj.data[set][i][3]];
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Draws the above line labels
+ */
+ RGraph.Scatter.prototype.DrawAboveLabels = function ()
+ {
+ var context = this.context;
+ var size = this.Get('chart.labels.above.size');
+ var font = this.Get('chart.text.font');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+
+
+ for (var set=0; set<this.coords.length; ++set) {
+ for (var point=0; point<this.coords[set].length; ++point) {
+
+ var x_val = this.data[set][point][0];
+ var y_val = this.data[set][point][1];
+
+
+ var x_pos = this.coords[set][point][0];
+ var y_pos = this.coords[set][point][1];
+
+ RGraph.Text(context,
+ font,
+ size,
+ x_pos,
+ y_pos - 5 - size,
+ x_val.toFixed(this.Get('chart.labels.above.decimals')) + ', ' + y_val.toFixed(this.Get('chart.labels.above.decimals')),
+ 'center',
+ 'center',
+ true,
+ null,
+ 'rgba(255, 255, 255, 0.7)');
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.skeleton.js b/schall/static/RGraph/libraries/RGraph.skeleton.js
new file mode 100644
index 0000000..fd93edc
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.skeleton.js
@@ -0,0 +1,333 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+
+
+
+ /**
+ * This is the RGraph.skeleton.js file which you can use as a base for creating new graph types.
+ */
+
+
+
+
+ /**
+ * Having this here means that the RGraph libraries can be included in any order, instead of you having
+ * to include the common core library first.
+ */
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+
+
+
+ /**
+ * The chart constructor. This function sets up the object. It takes the ID (the HTML attribute) of the canvas as the
+ * first argument and the data as the second. If you need to change this, you can.
+ *
+ * @param string id The canvas tag ID
+ * @param array data The chart data
+ */
+ RGraph.Skeleton = function (id, data)
+ {
+ /**
+ * Set these as object properties so they don't have to be constantly retrieved. Note that using a dollar
+ * function - $() - can cause conflicts with popular javascript libraries, eg jQuery. It's therefore best
+ * to stick to document.getElementById(). Setting the canvas and context as object properties means you
+ * can reference them like this: myObj.canvas
+ * myObj.context
+ */
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+
+ /**
+ * This puts a reference to this object on to the canvas. Useful in event handling.
+ */
+ this.canvas.__object__ = this;
+
+ /**
+ * This defines the type of this graph type and should be a one word description.
+ */
+ this.type = 'skeleton';
+
+ /**
+ * This facilitates easy object identification, and should be true
+ */
+ this.isRGraph = true;
+
+ /**
+ * This does a few things, for example adding the .fillText() method to the canvas 2D context when
+ * it doesn't exist. This facilitates the graphs to be still shown in older browser (though without
+ * text obviously). You'll find the function in RGraph.common.core.js
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ /**
+ * Some example background properties, as used by the method RGraph.background.Draw()
+ */
+ this.properties = {
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.colors': ['red','blue'],
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
+ 'chart.background.grid': true,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.hsize': 20,
+ 'chart.background.grid.vsize': 20,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':false,
+ 'chart.background.grid.autofit.align':false,
+ 'chart.background.grid.autofit.numhlines': 7,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.contextmenu': null,
+ 'chart.labels': null,
+ 'chart.labels.ingraph': null,
+ 'chart.labels.above': false,
+ 'chart.labels.above.decimals': 0,
+ 'chart.labels.above.size': null,
+ 'chart.scale.decimals': 0,
+ 'chart.scale.point': '.',
+ 'chart.scale.thousand': ',',
+ 'chart.crosshairs': false,
+ 'chart.crosshairs.color': '#333',
+ 'chart.crosshairs.hline': true,
+ 'chart.crosshairs.vline': true,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.key': null,
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.title.xaxis': '',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': null,
+ 'chart.title.yaxis.position': 'left',
+ 'chart.text.color': 'black',
+ 'chart.text.size': 10,
+ 'chart.text.angle': 0,
+ 'chart.text.font': 'Verdana',
+ 'chart.resizable': false,
+ 'chart.resize.handle.background': null,
+ 'chart.adjustable': false,
+ 'chart.hmargin': 5,
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.tooltips': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.highlight': true
+ }
+
+ /**
+ * A simple check that the browser has canvas support
+ */
+ if (!this.canvas) {
+ alert('[SKELETON] No canvas support');
+ return;
+ }
+
+ /**
+ * Store the data that was passed to this constructor
+ */
+ this.data = data;
+
+ /**
+ * This can be used to store the coordinates of shapes on the graph
+ */
+ this.coords = [];
+
+
+ /**
+ * If you add a .get*(e) method to ease getting the shape that's currently being hovered over
+ * or has been clicked on (in the same vein as the Bar charts .getBar() method or the Line charts
+ * .getPoint() method) then you should set this so that common methods have a common function
+ * name to call - for example the context menu uses the common name .getShape(e) to add the
+ * details to the context menu.
+ */
+ this.getShape = this.getXXX;
+ }
+
+
+
+
+ /**
+ * A setter method for setting graph properties. It can be used like this: obj.Set('chart.background.grid', false);
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Skeleton.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+
+
+ /**
+ * A getter method for retrieving graph properties. It can be used like this: obj.Get('chart.background.grid');
+ * This can be used inside your methods that draw the graph.
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Skeleton.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+
+
+ /**
+ * The function you call to draw the chart after you have set all of the graph properties
+ */
+ RGraph.Skeleton.prototype.Draw = function ()
+ {
+ /**
+ * This draws the background image, which when loaded draws the graph, hence the return
+ */
+ if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
+ RGraph.DrawBackgroundImage(this);
+ return;
+ }
+
+ /**
+ * Fire the custom RGraph onbeforedraw event (which should be fired before the chart is drawn)
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+
+
+
+ /*************************
+ * Draw the chart here... *
+ *************************/
+
+
+
+
+ /**
+ * These call common functions, that facilitate some of RGraphs features
+ */
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Draw "in graph" labels
+ */
+ if (this.Get('chart.labels.ingraph')) {
+ RGraph.DrawInGraphLabels(this);
+ }
+
+ /**
+ * Draw crosschairs
+ */
+ if (this.Get('chart.crosshairs')) {
+ RGraph.DrawCrosshairs(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * This function enables adjusting
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the custom RGraph ondraw event (which should be fired when you have drawn the chart)
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
diff --git a/schall/static/RGraph/libraries/RGraph.thermometer.js b/schall/static/RGraph/libraries/RGraph.thermometer.js
new file mode 100644
index 0000000..e3594a6
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.thermometer.js
@@ -0,0 +1,418 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The chart constructor. This function sets up the object. It takes the ID (the HTML attribute) of the canvas as the
+ * first argument and the data as the second. If you need to change this, you can.
+ *
+ * @param string id The canvas tag ID
+ * @param number min The minimum value
+ * @param number max The maximum value
+ * @param number value The value reported by the thermometer
+ */
+ RGraph.Thermometer = function (id, min, max, value)
+ {
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+
+ this.type = 'thermometer';
+ this.isRGraph = true;
+ this.min = min;
+ this.max = max;
+ this.value = value;
+ this.coords = [];
+ this.graphArea = [];
+ this.currentValue = null;
+
+ RGraph.OldBrowserCompat(this.context);
+
+ this.properties = {
+ 'chart.colors': ['red'],
+ 'chart.gutter.left': 15,
+ 'chart.gutter.right': 15,
+ 'chart.gutter.top': 15,
+ 'chart.gutter.bottom': 15,
+ 'chart.ticksize': 5,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.text.size': 10,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.title': '',
+ 'chart.title.hpos': 0.5,
+ 'chart.title.side': '',
+ 'chart.title.side.bold': true,
+ 'chart.title.side.font': null,
+ 'chart.shadow': true,
+ 'chart.shadow.offsetx': 0,
+ 'chart.shadow.offsety': 0,
+ 'chart.shadow.blur': 15,
+ 'chart.shadow.color': 'gray',
+ 'chart.resizable': false,
+ 'chart.contextmenu': null,
+ 'chart.adjustable': false,
+ 'chart.value.label': true,
+ 'chart.scale.visible': false,
+ 'chart.scale.decimals': 0,
+ 'chart.ylabels.count': 10,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black'
+ }
+
+ /**
+ * A simple check that the browser has canvas support
+ */
+ if (!this.canvas) {
+ alert('[THERMOMETER] No canvas support');
+ return;
+ }
+ }
+
+
+
+
+ /**
+ * A setter.
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Thermometer.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+
+
+ /**
+ * A getter.
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Thermometer.prototype.Get = function (name)
+ {
+ return this.properties[name];
+ }
+
+
+
+
+ /**
+ * Draws the thermometer
+ */
+ RGraph.Thermometer.prototype.Draw = function ()
+ {
+ /**
+ * Fire the custom RGraph onbeforedraw event (which should be fired before the chart is drawn)
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+
+
+ /**
+ * Draw the background
+ */
+ this.DrawBackground();
+
+ /**
+ * Draw the bar that represents the value
+ */
+ this.DrawBar();
+
+ /**
+ * Draw the tickmarks/hatchmarks
+ */
+ this.DrawTickMarks();
+
+ /**
+ * Draw the label
+ */
+ this.DrawLabels();
+
+ /**
+ * Draw the title
+ */
+ if (this.Get('chart.title')) {
+ this.DrawTitle();
+ }
+
+ /**
+ * Draw the side title
+ */
+ if (this.Get('chart.title.side')) {
+ this.DrawSideTitle();
+ }
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * Instead of using RGraph.common.adjusting.js, handle them here
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+
+
+
+ /**
+ * Fire the custom RGraph ondraw event (which should be fired when you have drawn the chart)
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+
+
+
+ /**
+ * Draws the thermometer itself
+ */
+ RGraph.Thermometer.prototype.DrawBackground = function ()
+ {
+ var canvas = this.canvas;
+ var context = this.context;
+ var bulbRadius = (this.canvas.width - this.gutterLeft - this.gutterRight) / 2;
+
+ // Cache the bulbRadius as an object variable
+ this.bulbRadius = bulbRadius;
+
+ // Draw the black background that becomes the border
+ context.beginPath();
+ context.fillStyle = 'black';
+
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ context.fillRect(this.gutterLeft + 12,this.gutterTop + bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom - bulbRadius - bulbRadius);
+ context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius, 0, 6.28, 0);
+ context.arc(this.gutterLeft + bulbRadius,this.gutterTop + bulbRadius,(this.canvas.width - this.gutterLeft - this.gutterRight - 24)/ 2,0,6.28,0);
+ context.fill();
+
+ RGraph.NoShadow(this);
+
+ // Draw the white inner content background that creates the border
+ context.beginPath();
+ context.fillStyle = 'white';
+ context.fillRect(this.gutterLeft + 12 + 1,this.gutterTop + bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24 - 2,RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom - bulbRadius - bulbRadius);
+ context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius - 1, 0, 6.28, 0);
+ context.arc(this.gutterLeft + bulbRadius,this.gutterTop + bulbRadius,((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24)/ 2) - 1,0,6.28,0);
+ context.fill();
+
+ // Draw the bottom content of the thermometer
+ context.beginPath();
+ context.fillStyle = this.Get('chart.colors')[0];
+ context.arc(this.gutterLeft + bulbRadius, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius, bulbRadius - 1, 0, 6.28, 0);
+ context.fillRect(this.gutterLeft + 12 + 1, RGraph.GetHeight(this) - this.gutterBottom - bulbRadius - bulbRadius,RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight - 24 - 2, bulbRadius);
+ context.fill();
+
+ // Save the X/Y/width/height
+ this.graphArea[0] = this.gutterLeft + 12 + 1;
+ this.graphArea[1] = this.gutterTop + bulbRadius;
+ this.graphArea[2] = this.canvas.width - this.gutterLeft - this.gutterRight - 24 - 2;
+ this.graphArea[3] = (this.canvas.height - this.gutterBottom - bulbRadius - bulbRadius) - (this.graphArea[1]);
+ }
+
+
+ /**
+ * This draws the bar that indicates the value of the thermometer
+ */
+ RGraph.Thermometer.prototype.DrawBar = function ()
+ {
+ var barHeight = ((this.value - this.min) / (this.max - this.min)) * this.graphArea[3];
+ var context = this.context;
+
+ // Draw the actual bar that indicates the value
+ context.beginPath();
+ context.fillStyle = this.Get('chart.colors')[0];
+ context.fillRect(this.graphArea[0],this.graphArea[1] + this.graphArea[3] - barHeight,this.graphArea[2],barHeight);
+ context.fill();
+
+ this.coords = [this.graphArea[0],this.graphArea[1] + this.graphArea[3] - barHeight,this.graphArea[2],barHeight];
+ }
+
+
+ /**
+ * Draws the tickmarks of the thermometer
+ */
+ RGraph.Thermometer.prototype.DrawTickMarks = function ()
+ {
+ var ticksize = this.Get('chart.ticksize');
+
+ // Left hand side tickmarks
+ for (var i=this.graphArea[1]; i<=(this.graphArea[1] + this.graphArea[3]); i += (this.graphArea[3] / 10)) {
+ this.context.beginPath();
+ this.context.moveTo(this.gutterLeft + 12, i);
+ this.context.lineTo(this.gutterLeft + 12 + ticksize, i);
+ this.context.stroke();
+ }
+
+ // Right hand side tickmarks
+ for (var i=this.graphArea[1]; i<=(this.graphArea[1] + this.graphArea[3]); i += (this.graphArea[3] / 10)) {
+ this.context.beginPath();
+ this.context.moveTo(RGraph.GetWidth(this) - (this.gutterRight + 12), i);
+ this.context.lineTo(RGraph.GetWidth(this) - (this.gutterRight + 12 + ticksize), i);
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * Draws the labels of the thermometer. Now (4th August 2011) draws
+ * the scale too
+ */
+ RGraph.Thermometer.prototype.DrawLabels = function ()
+ {
+ if (this.Get('chart.value.label')) {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ var text = this.Get('chart.scale.visible') ? String(this.value) : this.Get('chart.units.pre') + String(this.value) + this.Get('chart.units.post');
+
+ RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size'),this.gutterLeft + this.bulbRadius,this.coords[1] + this.Get('chart.text.size'),text,'center','center',true,null,'white');
+ this.context.fill();
+ }
+
+
+ /**
+ * Draw the scale if requested
+ */
+ if (this.Get('chart.scale.visible')) {
+ this.DrawScale();
+ }
+ }
+
+
+ /**
+ * Draws the title
+ */
+ RGraph.Thermometer.prototype.DrawTitle = function ()
+ {
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+ RGraph.Text(this.context,this.Get('chart.text.font'),this.Get('chart.text.size') + 2,this.gutterLeft + ((RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight) / 2),this.gutterTop,String(this.Get('chart.title')),'center','center',null,null,null,true);
+ this.context.fill();
+ }
+
+
+ /**
+ * Draws the title
+ */
+ RGraph.Thermometer.prototype.DrawSideTitle = function ()
+ {
+ var font = this.Get('chart.title.side.font') ? this.Get('chart.title.side.font') : this.Get('chart.text.font');
+ var size = this.Get('chart.title.side.size') ? this.Get('chart.title.side.size') : this.Get('chart.text.size') + 2;
+
+ this.context.beginPath();
+ this.context.fillStyle = this.Get('chart.text.color');
+ RGraph.Text(this.context,
+ font,
+ size,
+ this.gutterLeft * this.Get('chart.title.hpos'),
+ this.canvas.height / 2,
+ String(this.Get('chart.title.side')),
+ 'center',
+ 'center',
+ null,
+ 270,
+ null,
+ true);
+ this.context.fill();
+ }
+
+
+ /**
+ * Draw the scale if requested
+ */
+ RGraph.Thermometer.prototype.DrawScale = function ()
+ {
+ var numLabels = this.Get('chart.ylabels.count') - 1; // The -1 is so that the number of labels tallies with what is displayed
+ var step = (this.max - this.min) / numLabels;
+
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ this.context.beginPath();
+ for (var i=0; i<=numLabels; ++i) {
+
+ var text = this.Get('chart.units.pre') + (i == 0 ? String(this.min.toFixed(this.Get('chart.scale.decimals'))) : String((this.min + (i * step)).toFixed(this.Get('chart.scale.decimals')))) + this.Get('chart.units.post');
+ var x = this.canvas.width - this.gutterRight;
+ var y = this.canvas.height - this.gutterBottom - (2 * this.bulbRadius) - ((this.graphArea[3] / numLabels) * i);
+
+ RGraph.Text(this.context,
+ this.Get('chart.text.font'),
+ this.Get('chart.text.size'),
+ x - 6,
+ y,
+ text,
+ 'center');
+ }
+ this.context.fill();
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.vprogress.js b/schall/static/RGraph/libraries/RGraph.vprogress.js
new file mode 100644
index 0000000..dfc1a04
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.vprogress.js
@@ -0,0 +1,625 @@
+ /**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The progress bar constructor
+ *
+ * @param int id The ID of the canvas tag
+ * @param int value The indicated value of the meter.
+ * @param int max The end value (the upper most) of the meter
+ */
+ RGraph.VProgress = function (id, value, max)
+ {
+ this.id = id;
+ this.max = max;
+ this.value = value;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext('2d');
+ this.canvas.__object__ = this;
+ this.type = 'vprogress';
+ this.coords = [];
+ this.isRGraph = true;
+ this.currentValue = null;
+
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+ this.properties = {
+ 'chart.colors': ['#0c0'],
+ 'chart.strokestyle': '#999',
+ 'chart.tickmarks': true,
+ 'chart.tickmarks.zerostart':false,
+ 'chart.tickmarks.color': 'black',
+ 'chart.tickmarks.inner': false,
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.numticks': 10,
+ 'chart.numticks.inner': 50,
+ 'chart.background.color': '#eee',
+ 'chart.shadow': false,
+ 'chart.shadow.color': 'rgba(0,0,0,0.5)',
+ 'chart.shadow.blur': 3,
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.title': '',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.width': 0,
+ 'chart.height': 0,
+ 'chart.text.size': 10,
+ 'chart.text.color': 'black',
+ 'chart.text.font': 'Verdana',
+ 'chart.contextmenu': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ 'chart.tooltips': [],
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.highlight': true,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.background': true,
+ 'chart.zoom.action': 'zoom',
+ 'chart.arrows': false,
+ 'chart.margin': 0,
+ 'chart.resizable': false,
+ 'chart.resize.handle.adjust': [0,0],
+ 'chart.resize.handle.background': null,
+ 'chart.label.inner': false,
+ 'chart.labels.count': 10,
+ 'chart.labels.position': 'right',
+ 'chart.adjustable': false,
+ 'chart.min': 0,
+ 'chart.scale.decimals': 0,
+ 'chart.key': [],
+ 'chart.key.background': 'white',
+ 'chart.key.position': 'graph',
+ 'chart.key.halign': 'right',
+ 'chart.key.shadow': false,
+ 'chart.key.shadow.color': '#666',
+ 'chart.key.shadow.blur': 3,
+ 'chart.key.shadow.offsetx': 2,
+ 'chart.key.shadow.offsety': 2,
+ 'chart.key.position.gutter.boxed': true,
+ 'chart.key.position.x': null,
+ 'chart.key.position.y': null,
+ 'chart.key.color.shape': 'square',
+ 'chart.key.rounded': true,
+ 'chart.key.linewidth': 1
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[PROGRESS] No canvas support');
+ return;
+ }
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A generic setter
+ *
+ * @param string name The name of the property to set
+ * @param string value The value of the poperty
+ */
+ RGraph.VProgress.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A generic getter
+ *
+ * @param string name The name of the property to get
+ */
+ RGraph.VProgress.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * Draws the progress bar
+ */
+ RGraph.VProgress.prototype.Draw = function ()
+ {
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * Set the current value
+ */
+ this.currentValue = this.value;
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ // Figure out the width and height
+ this.width = RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight;
+ this.height = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+ this.coords = [];
+
+ this.Drawbar();
+ this.DrawTickMarks();
+ this.DrawLabels();
+
+ this.context.stroke();
+ this.context.fill();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+ /**
+ * Alternatively, show the tooltip if requested
+ */
+ if (typeof(this.Get('chart.tooltips')) == 'function' || this.Get('chart.tooltips').length) {
+
+ // Need to register this object for redrawing
+ RGraph.Register(this);
+
+ /**
+ * Install the window onclick handler
+ */
+ var window_onclick = function (){RGraph.Redraw();}
+ window.addEventListener('click', window_onclick, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', window_onclick);
+
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ //this.canvas.onclick = function (e)
+ var canvas_onclick_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Redraw();
+
+
+ if (bar) {
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]);
+
+ /**
+ * Show a tooltip if it's defined
+ */
+ if (text) {
+
+ obj.context.beginPath();
+ obj.context.strokeStyle = obj.Get('chart.highlight.stroke');
+ obj.context.fillStyle = obj.Get('chart.highlight.fill');
+ obj.context.strokeRect(bar[1], bar[2], bar[3], bar[4]);
+ obj.context.fillRect(bar[1], bar[2], bar[3], bar[4]);
+
+ obj.context.stroke();
+ obj.context.fill();
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]);
+ }
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+ }
+ this.canvas.addEventListener('click', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
+
+
+ /**
+ * If the cursor is over a hotspot, change the cursor to a hand
+ */
+ //this.canvas.onmousemove = function (e)
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Change the mouse pointer
+ */
+ if (bar) {
+
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]);
+
+ if (text) {
+ canvas.style.cursor = 'pointer';
+ return;
+ }
+ }
+
+ canvas.style.cursor = 'default';
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+ // Draw a key if necessary
+ if (this.Get('chart.key').length) {
+ RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));
+ }
+
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Instead of using RGraph.common.adjusting.js, handle them here
+ */
+ if (this.Get('chart.adjustable')) {
+ RGraph.AllowAdjusting(this);
+ }
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draw the bar itself
+ */
+ RGraph.VProgress.prototype.Drawbar = function ()
+ {
+ // Set a shadow if requested
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ }
+
+ // Draw the shadow for MSIE
+ if (RGraph.isIE8() && this.Get('chart.shadow')) {
+ this.context.fillStyle = this.Get('chart.shadow.color');
+ this.context.fillRect(this.gutterLeft + this.Get('chart.shadow.offsetx'), this.gutterTop + this.Get('chart.shadow.offsety'), this.width, this.height);
+ }
+
+ // Draw the outline
+ this.context.fillStyle = this.Get('chart.background.color');
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.strokeRect(this.gutterLeft, this.gutterTop, this.width, this.height);
+ this.context.fillRect(this.gutterLeft, this.gutterTop, this.width, this.height);
+
+ // Turn off any shadow
+ RGraph.NoShadow(this);
+
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+ this.context.fillStyle = this.Get('chart.colors')[0];
+ var margin = this.Get('chart.margin');
+ var barHeight = RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom;
+
+ // Draw the actual bar itself
+ if (typeof(this.value) == 'number') {
+
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ } else if (typeof(this.value) == 'object') {
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ var startPoint = this.canvas.height - this.gutterBottom;
+
+ for (var i=0; i<this.value.length; ++i) {
+
+ var segmentHeight = ( (this.value[i] - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * barHeight;
+
+ this.context.fillStyle = this.Get('chart.colors')[i];
+
+ this.context.fillRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
+ this.context.strokeRect(this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight);
+
+
+ // Store the coords
+ this.coords.push([this.gutterLeft + margin, startPoint - segmentHeight, this.width - margin - margin, segmentHeight]);
+
+ startPoint -= segmentHeight;
+
+ }
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+ /**
+ * Inner tickmarks
+ */
+ if (this.Get('chart.tickmarks.inner')) {
+
+ var spacing = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / this.Get('chart.numticks.inner');
+
+ this.context.lineWidth = 1;
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ this.context.beginPath();
+
+ for (var y = this.gutterTop; y<RGraph.GetHeight(this) - this.gutterBottom; y+=spacing) {
+ this.context.moveTo(this.gutterLeft, y);
+ this.context.lineTo(this.gutterLeft + 3, y);
+
+ this.context.moveTo(RGraph.GetWidth(this) - this.gutterRight, y);
+ this.context.lineTo(RGraph.GetWidth(this) - this.gutterRight - 3, y);
+ }
+
+ this.context.stroke();
+ }
+
+ /**
+ * Draw the actual bar
+ */
+ var barHeight = Math.min(this.height, ( (this.value - this.Get('chart.min')) / (this.max - this.Get('chart.min')) ) * this.height);
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.strokestyle');
+
+ if (typeof(this.value) == 'number') {
+ this.context.strokeRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight);
+ this.context.fillRect(this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight);
+ }
+
+
+ /**
+ * Draw the arrows indicating the level if requested
+ */
+ if (this.Get('chart.arrows')) {
+ var x = this.gutterLeft - 4;
+ var y = RGraph.GetHeight(this) - this.gutterBottom - barHeight;
+
+ this.context.lineWidth = 1;
+ this.context.fillStyle = 'black';
+ this.context.strokeStyle = 'black';
+
+ this.context.beginPath();
+ this.context.moveTo(x, y);
+ this.context.lineTo(x - 4, y - 2);
+ this.context.lineTo(x - 4, y + 2);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+
+ x += this.width + 8;
+
+ this.context.beginPath();
+ this.context.moveTo(x, y);
+ this.context.lineTo(x + 4, y - 2);
+ this.context.lineTo(x + 4, y + 2);
+ this.context.closePath();
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+
+
+
+ /**
+ * Draw the "in-bar" label
+ */
+ if (this.Get('chart.label.inner')) {
+ this.context.beginPath();
+ this.context.fillStyle = 'black';
+ RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') + 2, RGraph.GetWidth(this) / 2, RGraph.GetHeight(this) - this.gutterBottom - barHeight - 5, String(this.Get('chart.units.pre') + this.value + this.Get('chart.units.post')), 'bottom', 'center');
+ this.context.fill();
+ }
+
+
+ // Store the coords
+ this.coords.push([this.gutterLeft + margin, this.gutterTop + this.height - barHeight, this.width - margin - margin, barHeight]);
+ }
+
+ /**
+ * The function that draws the tick marks. Apt name...
+ */
+ RGraph.VProgress.prototype.DrawTickMarks = function ()
+ {
+ this.context.strokeStyle = this.Get('chart.tickmarks.color');
+
+ if (this.Get('chart.tickmarks')) {
+ this.context.beginPath();
+ for (var i=0; this.Get('chart.tickmarks.zerostart') ? i<=this.Get('chart.numticks') : i<this.Get('chart.numticks'); i++) {
+
+ var startX = this.Get('chart.labels.position') == 'left' ? this.gutterLeft : this.canvas.width - this.Get('chart.gutter.right');
+ var endX = this.Get('chart.labels.position') == 'left' ? startX - 4 : startX + 4;
+ var yPos = (this.height * (i / this.Get('chart.numticks'))) + this.gutterTop
+
+ this.context.moveTo(startX, yPos);
+ this.context.lineTo(endX, yPos);
+ }
+ this.context.stroke();
+ }
+ }
+
+
+ /**
+ * The function that draws the labels
+ */
+ RGraph.VProgress.prototype.DrawLabels = function ()
+ {
+ this.context.fillStyle = this.Get('chart.text.color');
+
+ var context = this.context;
+ var position = this.Get('chart.labels.position');
+ var xAlignment = position == 'left' ? 'right' : 'left';
+ var yAlignment = 'center';
+ var count = this.Get('chart.labels.count');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+ var text_size = this.Get('chart.text.size');
+ var text_font = this.Get('chart.text.font');
+
+ if (this.Get('chart.tickmarks')) {
+
+ for (var i=0; i<count ; ++i) {
+
+ var text = String(
+ ((( (this.max - this.Get('chart.min')) / count) * (count - i)) + this.Get('chart.min')).toFixed(this.Get('chart.scale.decimals'))
+ );
+
+ RGraph.Text(context,
+ text_font,
+ text_size,
+ position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5),
+ (((RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / count) * i) + this.gutterTop,
+ units_pre + text + units_post,
+ yAlignment,
+ xAlignment);
+ }
+
+ /**
+ * Show zero?
+ */
+ if (this.Get('chart.tickmarks.zerostart') && this.Get('chart.min') == 0) {
+ RGraph.Text(context,
+ text_font,
+ text_size,
+ position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5),
+ RGraph.GetHeight(this) - this.gutterBottom, units_pre + String(this.Get('chart.min').toFixed(this.Get('chart.scale.decimals'))) + units_post,
+ yAlignment,
+ xAlignment);
+ }
+
+ /**
+ * chart.ymin is set
+ */
+ if (this.Get('chart.min') != 0) {
+ RGraph.Text(context,
+ text_font,
+ text_size,
+ position == 'left' ? (this.gutterLeft - 5) : (RGraph.GetWidth(this) - this.gutterRight + 5),
+ RGraph.GetHeight(this) - this.gutterBottom, units_pre + String(this.Get('chart.min').toFixed(this.Get('chart.scale.decimals'))) + units_post,
+ yAlignment,
+ xAlignment);
+ }
+ }
+
+ // Draw the title text
+ if (this.Get('chart.title')) {
+ RGraph.Text(context,
+ text_font,
+ text_size + 2,
+ this.gutterLeft + ((this.canvas.width - this.gutterLeft - this.gutterRight) / 2), // X
+ this.gutterTop - text_size, // Y
+ this.Get('chart.title'),
+ null,
+ 'center',null, null, null, true);
+ }
+ }
+
+
+ /**
+ * Returns the focused bar
+ *
+ * @param event e The event object
+ */
+ RGraph.VProgress.prototype.getBar = function (e)
+ {
+ var obj = e.target.__object__;
+ var mouseCoords = RGraph.getMouseXY(e)
+
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseCoords = RGraph.getMouseXY(e);
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+ var idx = i;
+
+ if (mouseX >= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height) ) {
+ return [obj, left, top, width, height, idx];
+ }
+ }
+ } \ No newline at end of file
diff --git a/schall/static/RGraph/libraries/RGraph.waterfall.js b/schall/static/RGraph/libraries/RGraph.waterfall.js
new file mode 100644
index 0000000..f3bf96c
--- /dev/null
+++ b/schall/static/RGraph/libraries/RGraph.waterfall.js
@@ -0,0 +1,790 @@
+/**
+ * o------------------------------------------------------------------------------o
+ * | This file is part of the RGraph package - you can learn more at: |
+ * | |
+ * | http://www.rgraph.net |
+ * | |
+ * | This package is licensed under the RGraph license. For all kinds of business |
+ * | purposes there is a small one-time licensing fee to pay and for non |
+ * | commercial purposes it is free to use. You can read the full license here: |
+ * | |
+ * | http://www.rgraph.net/LICENSE.txt |
+ * o------------------------------------------------------------------------------o
+ */
+
+ if (typeof(RGraph) == 'undefined') RGraph = {};
+
+ /**
+ * The bar chart constructor
+ *
+ * @param object canvas The canvas object
+ * @param array data The chart data
+ */
+ RGraph.Waterfall = function (id, data)
+ {
+ // Get the canvas and context objects
+ this.id = id;
+ this.canvas = document.getElementById(id);
+ this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;
+ this.canvas.__object__ = this;
+ this.type = 'waterfall';
+ this.max = 0;
+ this.isRGraph = true;
+ this.coords = [];
+
+ /**
+ * Compatibility with older browsers
+ */
+ RGraph.OldBrowserCompat(this.context);
+
+
+ // Various config
+ this.properties = {
+ 'chart.background.barcolor1': 'rgba(0,0,0,0)',
+ 'chart.background.barcolor2': 'rgba(0,0,0,0)',
+ 'chart.background.grid': true,
+ 'chart.background.grid.color': '#ddd',
+ 'chart.background.grid.width': 1,
+ 'chart.background.grid.hsize': 20,
+ 'chart.background.grid.vsize': 20,
+ 'chart.background.grid.vlines': true,
+ 'chart.background.grid.hlines': true,
+ 'chart.background.grid.border': true,
+ 'chart.background.grid.autofit':true,
+ 'chart.background.grid.autofit.numhlines': 5,
+ 'chart.background.grid.autofit.numvlines': 20,
+ 'chart.background.grid.autofit.align': false,
+ 'chart.background.image': null,
+ 'chart.background.hbars': null, // ???
+ 'chart.xaxispos': 'bottom',
+ 'chart.numyticks': 10,
+ 'chart.hmargin': 5,
+ 'chart.strokestyle': '#666',
+ 'chart.axis.color': 'black',
+ 'chart.gutter.left': 25,
+ 'chart.gutter.right': 25,
+ 'chart.gutter.top': 25,
+ 'chart.gutter.bottom': 25,
+ 'chart.labels': [],
+ 'chart.ylabels': true,
+ 'chart.text.color': 'black',
+ 'chart.text.size': 10,
+ 'chart.text.angle': 0,
+ 'chart.text.font': 'Verdana',
+ 'chart.ymax': null,
+ 'chart.title': '',
+ 'chart.title.color': 'black',
+ 'chart.title.background': null,
+ 'chart.title.hpos': null,
+ 'chart.title.vpos': null,
+ 'chart.title.bold': true,
+ 'chart.title.font': null,
+ 'chart.title.xaxis': '',
+ 'chart.title.yaxis': '',
+ 'chart.title.yaxis.bold': true,
+ 'chart.title.yaxis.size': null,
+ 'chart.title.yaxis.font': null,
+ 'chart.title.xaxis.pos': null,
+ 'chart.title.yaxis.pos': null,
+ 'chart.title.yaxis.align': 'left',
+ 'chart.title.xaxis.bold': true,
+ 'chart.title.xaxis.size': null,
+ 'chart.title.xaxis.font': null,
+ 'chart.colors': ['green', 'red', 'blue'],
+ 'chart.shadow': false,
+ 'chart.shadow.color': '#666',
+ 'chart.shadow.offsetx': 3,
+ 'chart.shadow.offsety': 3,
+ 'chart.shadow.blur': 3,
+ 'chart.tooltips': null,
+ 'chart.tooltips.effect': 'fade',
+ 'chart.tooltips.css.class': 'RGraph_tooltip',
+ 'chart.tooltips.event': 'onclick',
+ 'chart.tooltips.highlight': true,
+ 'chart.tooltips.override': null,
+ 'chart.highlight.stroke': 'black',
+ 'chart.highlight.fill': 'rgba(255,255,255,0.5)',
+ 'chart.contextmenu': null,
+ 'chart.units.pre': '',
+ 'chart.units.post': '',
+ //'chart.scale.decimals': 0,
+ //'chart.scale.point': '.',
+ //'chart.scale.thousand': ',',
+ //'chart.scale.formatter': null,
+ 'chart.crosshairs': false,
+ 'chart.crosshairs.color': '#333',
+ 'chart.crosshairs.hline': true,
+ 'chart.crosshairs.vline': true,
+ 'chart.annotatable': false,
+ 'chart.annotate.color': 'black',
+ 'chart.zoom.factor': 1.5,
+ 'chart.zoom.fade.in': true,
+ 'chart.zoom.fade.out': true,
+ 'chart.zoom.hdir': 'right',
+ 'chart.zoom.vdir': 'down',
+ 'chart.zoom.frames': 25,
+ 'chart.zoom.delay': 16.666,
+ 'chart.zoom.shadow': true,
+ 'chart.zoom.mode': 'canvas',
+ 'chart.zoom.thumbnail.width': 75,
+ 'chart.zoom.thumbnail.height': 75,
+ 'chart.zoom.background': true,
+ 'chart.resizable': false,
+ 'chart.resize.handle.background': null,
+ 'chart.noaxes': false,
+ 'chart.noxaxis': false,
+ 'chart.noyaxis': false,
+ 'chart.axis.color': 'black',
+ 'chart.total': true,
+ 'chart.multiplier.x': 1,
+ 'chart.multiplier.w': 1
+ }
+
+ // Check for support
+ if (!this.canvas) {
+ alert('[WATERFALL] No canvas support');
+ return;
+ }
+
+ // Store the data
+ this.data = data;
+
+
+ /**
+ * Set the .getShape commonly named method
+ */
+ this.getShape = this.getBar;
+ }
+
+
+ /**
+ * A setter
+ *
+ * @param name string The name of the property to set
+ * @param value mixed The value of the property
+ */
+ RGraph.Waterfall.prototype.Set = function (name, value)
+ {
+ this.properties[name.toLowerCase()] = value;
+ }
+
+
+ /**
+ * A getter
+ *
+ * @param name string The name of the property to get
+ */
+ RGraph.Waterfall.prototype.Get = function (name)
+ {
+ return this.properties[name.toLowerCase()];
+ }
+
+
+ /**
+ * The function you call to draw the bar chart
+ */
+ RGraph.Waterfall.prototype.Draw = function ()
+ {
+ // MUST be the first thing done!
+ if (typeof(this.Get('chart.background.image')) == 'string' && !this.__background_image__) {
+ RGraph.DrawBackgroundImage(this);
+ return;
+ }
+
+
+ /**
+ * Fire the onbeforedraw event
+ */
+ RGraph.FireCustomEvent(this, 'onbeforedraw');
+
+ /**
+ * Clear all of this canvases event handlers (the ones installed by RGraph)
+ */
+ RGraph.ClearEventListeners(this.id);
+
+ /**
+ * This is new in May 2011 and facilitates indiviual gutter settings,
+ * eg chart.gutter.left
+ */
+ this.gutterLeft = this.Get('chart.gutter.left');
+ this.gutterRight = this.Get('chart.gutter.right');
+ this.gutterTop = this.Get('chart.gutter.top');
+ this.gutterBottom = this.Get('chart.gutter.bottom');
+
+ /**
+ * Stop the coords array from growing uncontrollably
+ */
+ this.coords = [];
+
+ /**
+ * This gets used a lot
+ */
+ this.centery = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+
+ /**
+ * Work out a few things. They need to be here because they depend on things you can change after you instantiate the object
+ */
+ this.max = 0;
+ this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom;
+ this.graphwidth = this.canvas.width - this.gutterLeft - this.gutterRight;
+ this.halfTextHeight = this.Get('chart.text.size') / 2;
+
+
+ /**
+ * Work out the maximum value
+ *
+ * Normally the last bar is a total and not taken into account when the scale is generated.
+ * However it is not necessarily shown, so if it's not being shown it should not be
+ * accounted for.
+ */
+ this.max = this.getMax(this.data);
+ this.scale = RGraph.getScale(typeof(this.Get('chart.ymax')) == 'number' ? this.Get('chart.ymax') : this.max, this);
+ this.max = this.scale[4];
+ var decimals = this.Get('chart.scale.decimals');
+
+ this.scale = [
+ (this.max * (1/5)).toFixed(decimals),
+ (this.max * (2/5)).toFixed(decimals),
+ (this.max * (3/5)).toFixed(decimals),
+ (this.max * (4/5)).toFixed(decimals),
+ typeof(this.max) == 'number' ? this.max.toFixed(decimals) : this.max
+ ];
+
+
+ // Progressively Draw the chart
+ RGraph.background.Draw(this);
+
+ this.DrawAxes();
+ this.Drawbars();
+ this.DrawLabels();
+
+ /**
+ * Setup the context menu if required
+ */
+ if (this.Get('chart.contextmenu')) {
+ RGraph.ShowContext(this);
+ }
+
+
+ /**
+ * Draw crosschairs
+ */
+ if (this.Get('chart.crosshairs')) {
+ RGraph.DrawCrosshairs(this);
+ }
+
+ /**
+ * If the canvas is annotatable, do install the event handlers
+ */
+ if (this.Get('chart.annotatable')) {
+ RGraph.Annotate(this);
+ }
+
+ /**
+ * This bit shows the mini zoom window if requested
+ */
+ if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
+ RGraph.ShowZoomWindow(this);
+ }
+
+
+ /**
+ * This function enables resizing
+ */
+ if (this.Get('chart.resizable')) {
+ RGraph.AllowResizing(this);
+ }
+
+ /**
+ * Tooltips
+ */
+ if (this.Get('chart.tooltips')) {
+
+ RGraph.Register(this);
+
+ /**
+ * Install the onclick event handler for the tooltips
+ */
+ var canvas_onclick_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ if (bar) {
+
+ /**
+ * First, if the event is onmousemove end the tooltip is already being shown, do nothing
+ */
+ if (obj.Get('chart.tooltips.event') == 'onmousemove' && RGraph.Registry.Get('chart.tooltip') && RGraph.Registry.Get('chart.tooltip').__index__ == bar[5]) {
+ return;
+ }
+
+ /**
+ * Redraw the graph first, in effect resetting the graph to as it was when it was first drawn
+ * This "deselects" any already selected bar
+ */
+ RGraph.Redraw();
+
+ var x = bar[1];
+ var y = bar[2];
+ var w = bar[3];
+ var h = bar[4];
+
+ if (!obj.Get('chart.tooltips')[bar[5]]) {
+ return;
+ }
+
+
+ // Draw the highlight (if necessary)
+ if (obj.Get('chart.tooltips.highlight')) {
+ context.beginPath();
+ context.fillStyle = obj.Get('chart.highlight.fill');
+ context.strokeStyle = obj.Get('chart.highlight.stroke');
+ context.strokeRect(x,y,w,h);
+ context.fillRect(x,y,w,h);
+ context.stroke();
+ context.fill();
+ }
+
+ /**
+ * Get the tooltip text
+ */
+ var text = RGraph.parseTooltipText(obj.Get('chart.tooltips'), bar[5]);
+
+ canvas.style.cursor = text ? 'pointer' : 'default';
+
+ RGraph.Tooltip(canvas, text, e.pageX, e.pageY, bar[5]);
+ }
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+
+ return false;
+ }
+ this.canvas.addEventListener(this.Get('chart.tooltips.event') == 'onclick' ? 'click' : 'mousemove', canvas_onclick_func, false);
+ RGraph.AddEventListener(this.id, this.Get('chart.tooltips.event') == 'onclick' ? 'click' : 'mousemove', canvas_onclick_func);
+
+ /**
+ * Install the window onclick handler
+ */
+ var window_onclick_func = function (){RGraph.Redraw();};
+ window.addEventListener('click', window_onclick_func, false);
+ RGraph.AddEventListener('window_' + this.id, 'click', window_onclick_func);
+
+ /**
+ * Install the onmousemove event handler for the tooltips
+ */
+ var canvas_onmousemove_func = function (e)
+ {
+ e = RGraph.FixEventObject(e);
+
+ var canvas = document.getElementById(this.id);
+ var context = canvas.getContext('2d');
+ var obj = canvas.__object__;
+ var bar = obj.getBar(e);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ if (bar) {
+ if (obj.Get('chart.tooltips')[bar[5]]) {
+ canvas.style.cursor = 'pointer';
+ e.stopPropagation();
+ }
+
+ return;
+ }
+
+ canvas.style.cursor = 'default';
+
+ /**
+ * Stop the event bubbling
+ */
+ e.stopPropagation();
+
+ return false;
+ }
+ this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
+ RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+ /**
+ * Fire the RGraph ondraw event
+ */
+ RGraph.FireCustomEvent(this, 'ondraw');
+ }
+
+
+ /**
+ * Draws the charts axes
+ */
+ RGraph.Waterfall.prototype.DrawAxes = function ()
+ {
+ if (this.Get('chart.noaxes')) {
+ return;
+ }
+
+ this.context.beginPath();
+ this.context.strokeStyle = this.Get('chart.axis.color');
+ this.context.lineWidth = 1;
+
+ // Draw the Y axis
+ if (this.Get('chart.noyaxis') == false) {
+ this.context.moveTo(this.gutterLeft, this.gutterTop);
+ this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ }
+
+ // Draw the X axis
+ if (this.Get('chart.noxaxis') == false) {
+ // Center X axis
+ if (this.Get('chart.xaxispos') == 'center') {
+ this.context.moveTo(this.gutterLeft, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop);
+ this.context.lineTo(this.canvas.width - this.gutterRight, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop);
+ } else {
+ this.context.moveTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ this.context.lineTo(this.canvas.width - this.gutterRight, this.canvas.height - this.gutterBottom);
+ }
+ }
+
+ var numYTicks = this.Get('chart.numyticks');
+
+ // Draw the Y tickmarks
+ if (this.Get('chart.noyaxis') == false) {
+
+ var yTickGap = (RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / numYTicks;
+
+ for (y=this.gutterTop; y < (this.canvas.height - this.gutterBottom); y += yTickGap) {
+
+ if (this.Get('chart.xaxispos') == 'bottom' || (y != ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop)) {
+ this.context.moveTo(this.gutterLeft, y);
+ this.context.lineTo(this.gutterLeft - 3, y);
+ }
+ }
+
+ /**
+ * If the X axis is not being shown, draw an extra tick
+ */
+ if (this.Get('chart.noxaxis') || this.Get('chart.xaxispos') == 'center') {
+ this.context.moveTo(this.gutterLeft - 3, this.canvas.height - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft, this.canvas.height - this.gutterBottom);
+ }
+ }
+
+
+ // Draw the X tickmarks
+ if (this.Get('chart.noxaxis') == false) {
+
+ xTickGap = (this.canvas.width - this.gutterLeft - this.gutterRight ) / (this.data.length + (this.Get('chart.total') ? 1 : 0));
+
+ if (this.Get('chart.xaxispos') == 'center') {
+ yStart = ((this.canvas.height - this.gutterBottom - this.gutterTop) / 2) + this.gutterTop - 3;
+ yEnd = ((this.canvas.height - this.gutterBottom - this.gutterTop) / 2) + this.gutterTop + 3;
+ } else {
+ yStart = this.canvas.height - this.gutterBottom;
+ yEnd = (this.canvas.height - this.gutterBottom) + 3;
+ }
+
+ for (x=this.gutterLeft + xTickGap; x<=RGraph.GetWidth(this) - this.gutterRight; x+=xTickGap) {
+ this.context.moveTo(x, yStart);
+ this.context.lineTo(x, yEnd);
+ }
+
+ if (this.Get('chart.noyaxis')) {
+ this.context.moveTo(this.gutterLeft, yStart);
+ this.context.lineTo(this.gutterLeft, yEnd);
+ }
+ }
+
+ /**
+ * If the Y axis is not being shown, draw an extra tick
+ */
+ if (this.Get('chart.noyaxis') && this.Get('chart.noxaxis') == false) {
+ this.context.moveTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom);
+ this.context.lineTo(this.gutterLeft, RGraph.GetHeight(this) - this.gutterBottom + 3);
+ }
+
+ this.context.stroke();
+ }
+
+
+ /**
+ * Draws the labels for the graph
+ */
+ RGraph.Waterfall.prototype.DrawLabels = function ()
+ {
+ var context = this.context;
+ var numYLabels = 5; // Make this configurable
+ var interval = this.grapharea / numYLabels;
+ var font = this.Get('chart.text.font');
+ var size = this.Get('chart.text.size');
+ var color = this.Get('chart.text.color');
+ var units_pre = this.Get('chart.units.pre');
+ var units_post = this.Get('chart.units.post');
+
+ this.context.beginPath();
+ this.context.fillStyle = color;
+
+ /**
+ * First, draw the Y labels
+ */
+ if (this.Get('chart.ylabels')) {
+ if (this.Get('chart.xaxispos') == 'center') {
+
+ var halfInterval = interval / 2;
+ var halfWay = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop;
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context,
+ font,
+ size,
+ this.gutterLeft - 5,
+ this.gutterTop + (i * halfInterval),
+ RGraph.number_format(this, this.scale[4 - i], units_pre, units_post),
+ 'center',
+ 'right');
+ }
+
+ for (var i=0; i<numYLabels; ++i) {
+ RGraph.Text(context,
+ font,
+ size,
+ this.gutterLeft - 5,
+ halfWay + (i * halfInterval) + halfInterval,
+ '-' + RGraph.number_format(this, this.scale[i], units_pre, units_post),
+ 'center',
+ 'right');
+ }
+
+ } else {
+
+ for (var i=1; i<=numYLabels; ++i) {
+ RGraph.Text(context,
+ font,
+ size,
+ this.gutterLeft - 5,
+ this.canvas.height - this.gutterBottom - (i * interval),
+ RGraph.number_format(this, this.scale[i - 1], units_pre, units_post),
+ 'center',
+ 'right');
+
+ }
+ }
+ }
+
+
+
+ /**
+ * Now, draw the X labels
+ */
+ if (this.Get('chart.labels').length > 0) {
+
+ // Recalculate the interval for the X labels
+ interval = (this.canvas.width - this.gutterLeft - this.gutterRight) / this.Get('chart.labels').length;
+
+ var halign = 'center';
+ var angle = this.Get('chart.text.angle');
+
+ if (angle) {
+ halign = 'right';
+ angle *= -1;
+ }
+
+ var labels = this.Get('chart.labels');
+
+ for (var i=0; i<labels.length; ++i) {
+
+ // context, font, size, x, y, text[, valign[, halign[, border[, angle[, background[, bold[, indicator]]]]]]]
+ RGraph.Text(context,
+ font,
+ size,
+ this.gutterLeft + (i * interval) + (interval / 2),
+ this.canvas.height - this.gutterBottom + 5 + this.halfTextHeight,
+ labels[i],
+ 'center',
+ halign,
+ null,
+ angle);
+
+ }
+ }
+
+ this.context.stroke();
+ this.context.fill();
+ }
+
+
+ /**
+ * Draws the bars on to the chart
+ */
+ RGraph.Waterfall.prototype.Drawbars = function ()
+ {
+ var context = this.context;
+ var canvas = this.canvas;
+ var hmargin = this.Get('chart.hmargin');
+ var runningTotal = 0;
+
+
+ for (var i=0; i<this.data.length; ++i) {
+ context.beginPath();
+ context.strokeStyle = this.Get('chart.strokestyle');
+
+ var x = this.gutterLeft + hmargin + (((this.graphwidth / (this.data.length + (this.Get('chart.total') ? 1 : 0))) * i) * this.Get('chart.multiplier.x'));
+ var y = this.gutterTop + this.grapharea - (i == 0 ? ((this.data[0] / this.max) * this.grapharea) : (this.data[i] > 0 ? ((runningTotal + this.data[i]) / this.max) * this.grapharea : (runningTotal / this.max) * this.grapharea));
+ var w = ((this.canvas.width - this.gutterLeft - this.gutterRight) / (this.data.length + (this.Get('chart.total') ? 1 : 0 )) ) - (2 * this.Get('chart.hmargin'));
+ w = w * this.Get('chart.multiplier.w');
+ var h = (Math.abs(this.data[i]) / this.max) * this.grapharea;
+
+ if (this.Get('chart.xaxispos') == 'center') {
+ h /= 2;
+ y = (i == 0 ? ((this.data[0] / this.max) * this.grapharea) : (this.data[i] > 0 ? ((runningTotal + this.data[i]) / this.max) * this.grapharea : (runningTotal / this.max) * this.grapharea));
+ y = this.gutterTop + (this.grapharea/2) - (y / 2);
+ }
+
+ // Color
+ context.fillStyle = this.data[i] >= 0 ? this.Get('chart.colors')[0] : this.Get('chart.colors')[1];
+
+
+ if (this.Get('chart.shadow')) {
+ RGraph.SetShadow(this, this.Get('chart.shadow.color'), this.Get('chart.shadow.offsetx'), this.Get('chart.shadow.offsety'), this.Get('chart.shadow.blur'));
+ } else {
+ RGraph.NoShadow(this);
+ }
+
+ context.strokeRect(x, y, w, h);
+ context.fillRect(x, y, w, h);
+
+ this.coords.push([x, y, w, h]);
+
+ runningTotal += this.data[i];
+
+ context.stroke();
+ context.fill();
+ }
+
+if (this.Get('chart.total')) {
+
+ // This is the height of the final bar
+ h = (runningTotal / this.max) * (this.grapharea / (this.Get('chart.xaxispos') == 'center' ? 2 : 1));
+
+ /**
+ * Set the Y (ie the start point) value
+ */
+ if (this.Get('chart.xaxispos') == 'center') {
+ y = runningTotal > 0 ? this.centery - h : this.centery - h;
+ } else {
+ y = this.canvas.height - this.gutterBottom - h;
+ }
+
+ // This is the X position of the final bar
+ x = x + (this.Get('chart.hmargin') * 2) + w;
+
+
+ // Final color
+ this.context.fillStyle = this.Get('chart.colors')[2];
+
+ this.context.beginPath();
+ this.context.strokeRect(x, y, w, h);
+ this.context.fillRect(x, y, w, h);
+ this.context.stroke();
+ this.context.fill();
+
+ this.coords.push([x, y - (runningTotal > 0 ? 0 : Math.abs(h)), w, Math.abs(h)]);
+}
+
+ RGraph.NoShadow(this);
+
+ /**
+ * This draws the connecting lines
+ */
+ for (var i=1; i<this.coords.length; ++i) {
+ context.strokeStyle = 'gray';
+ context.beginPath();
+ if (this.data[i - 1] > 0) {
+ context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1]);
+ context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1]);
+ } else {
+ context.moveTo(this.coords[i - 1][0] + this.coords[i - 1][2], this.coords[i - 1][1] + this.coords[i - 1][3]);
+ context.lineTo(this.coords[i - 1][0] + this.coords[i - 1][2] + (2 * hmargin), this.coords[i - 1][1] + this.coords[i - 1][3]);
+ }
+ context.stroke();
+ }
+ }
+
+
+ /**
+ * Not used by the class during creating the graph, but is used by event handlers
+ * to get the coordinates (if any) of the selected bar
+ *
+ * @param object e The event object
+ */
+ RGraph.Waterfall.prototype.getBar = function (e)
+ {
+ var canvas = e.target;
+ var obj = e.target.__object__;
+ var mouseCoords = RGraph.getMouseXY(e);
+
+ /**
+ * Loop through the bars determining if the mouse is over a bar
+ */
+ for (var i=0; i<obj.coords.length; i++) {
+
+ var mouseX = mouseCoords[0];
+ var mouseY = mouseCoords[1];
+
+ var left = obj.coords[i][0];
+ var top = obj.coords[i][1];
+ var width = obj.coords[i][2];
+ var height = obj.coords[i][3];
+
+ if ( mouseX >= left
+ && mouseX <= left + width
+ && mouseY >= top
+ && mouseY <= top + height) {
+
+ return [obj, left, top, width, height, i];
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * The Waterfall is slightly different to Bar/Line charts so has this function to get the max value
+ */
+ RGraph.Waterfall.prototype.getMax = function (data)
+ {
+ var runningTotal = 0;
+ var max = 0;
+
+ for (var i=0; i<data.length; ++i) {
+ runningTotal += data[i];
+ max = Math.max(max, Math.abs(runningTotal));
+ }
+
+ return max;
+ } \ No newline at end of file