rgraph-rails 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.travis.yml +0 -1
- data/README.md +3 -3
- data/lib/rgraph-rails/version.rb +1 -1
- data/vendor/assets/javascripts/RGraph.bar.js +239 -3764
- data/vendor/assets/javascripts/RGraph.bipolar.js +115 -1986
- data/vendor/assets/javascripts/RGraph.common.annotate.js +35 -399
- data/vendor/assets/javascripts/RGraph.common.context.js +30 -600
- data/vendor/assets/javascripts/RGraph.common.core.js +403 -5187
- data/vendor/assets/javascripts/RGraph.common.csv.js +19 -275
- data/vendor/assets/javascripts/RGraph.common.deprecated.js +35 -454
- data/vendor/assets/javascripts/RGraph.common.dynamic.js +84 -1189
- data/vendor/assets/javascripts/RGraph.common.effects.js +90 -1548
- data/vendor/assets/javascripts/RGraph.common.key.js +54 -755
- data/vendor/assets/javascripts/RGraph.common.resizing.js +37 -567
- data/vendor/assets/javascripts/RGraph.common.sheets.js +29 -356
- data/vendor/assets/javascripts/RGraph.common.tooltips.js +32 -614
- data/vendor/assets/javascripts/RGraph.common.zoom.js +14 -223
- data/vendor/assets/javascripts/RGraph.cornergauge.js +71 -0
- data/vendor/assets/javascripts/RGraph.drawing.background.js +35 -620
- data/vendor/assets/javascripts/RGraph.drawing.circle.js +35 -576
- data/vendor/assets/javascripts/RGraph.drawing.image.js +52 -807
- data/vendor/assets/javascripts/RGraph.drawing.marker1.js +41 -717
- data/vendor/assets/javascripts/RGraph.drawing.marker2.js +37 -668
- data/vendor/assets/javascripts/RGraph.drawing.marker3.js +36 -563
- data/vendor/assets/javascripts/RGraph.drawing.poly.js +40 -608
- data/vendor/assets/javascripts/RGraph.drawing.rect.js +35 -597
- data/vendor/assets/javascripts/RGraph.drawing.text.js +34 -642
- data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +50 -809
- data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +51 -856
- data/vendor/assets/javascripts/RGraph.fuel.js +58 -964
- data/vendor/assets/javascripts/RGraph.funnel.js +55 -984
- data/vendor/assets/javascripts/RGraph.gantt.js +75 -1241
- data/vendor/assets/javascripts/RGraph.gauge.js +87 -1397
- data/vendor/assets/javascripts/RGraph.hbar.js +143 -2376
- data/vendor/assets/javascripts/RGraph.hprogress.js +80 -1397
- data/vendor/assets/javascripts/RGraph.line.js +241 -4162
- data/vendor/assets/javascripts/RGraph.meter.js +74 -1278
- metadata +3 -30
- data/vendor/assets/images/bg.png +0 -0
- data/vendor/assets/images/bullet.png +0 -0
- data/vendor/assets/images/facebook-large.png +0 -0
- data/vendor/assets/images/google-plus-large.png +0 -0
- data/vendor/assets/images/logo.png +0 -0
- data/vendor/assets/images/meter-image-sd-needle.png +0 -0
- data/vendor/assets/images/meter-image-sd.png +0 -0
- data/vendor/assets/images/meter-sketch-needle.png +0 -0
- data/vendor/assets/images/meter-sketch.png +0 -0
- data/vendor/assets/images/odometer-background.png +0 -0
- data/vendor/assets/images/rgraph.jpg +0 -0
- data/vendor/assets/images/title.png +0 -0
- data/vendor/assets/images/twitter-large.png +0 -0
- data/vendor/assets/javascripts/RGraph.modaldialog.js +0 -301
- data/vendor/assets/javascripts/RGraph.odo.js +0 -1265
- data/vendor/assets/javascripts/RGraph.pie.js +0 -2272
- data/vendor/assets/javascripts/RGraph.radar.js +0 -1847
- data/vendor/assets/javascripts/RGraph.rose.js +0 -1877
- data/vendor/assets/javascripts/RGraph.rscatter.js +0 -1425
- data/vendor/assets/javascripts/RGraph.scatter.js +0 -2970
- data/vendor/assets/javascripts/RGraph.semicircularprogress.js +0 -1015
- data/vendor/assets/javascripts/RGraph.thermometer.js +0 -1129
- data/vendor/assets/javascripts/RGraph.vprogress.js +0 -1452
- data/vendor/assets/javascripts/RGraph.waterfall.js +0 -1252
- data/vendor/assets/javascripts/financial-data.js +0 -1067
- data/vendor/assets/stylesheets/ModalDialog.css +0 -90
- data/vendor/assets/stylesheets/animations.css +0 -3347
- data/vendor/assets/stylesheets/website.css +0 -446
@@ -1,2970 +0,0 @@
|
|
1
|
-
// version: 2016-06-04
|
2
|
-
/**
|
3
|
-
* o--------------------------------------------------------------------------------o
|
4
|
-
* | This file is part of the RGraph package - you can learn more at: |
|
5
|
-
* | |
|
6
|
-
* | http://www.rgraph.net |
|
7
|
-
* | |
|
8
|
-
* | RGraph is dual licensed under the Open Source GPL (General Public License) |
|
9
|
-
* | v2.0 license and a commercial license which means that you're not bound by |
|
10
|
-
* | the terms of the GPL. The commercial license is just 99 GBP and you can |
|
11
|
-
* | read about it here: |
|
12
|
-
* | http://www.rgraph.net/license |
|
13
|
-
* o--------------------------------------------------------------------------------o
|
14
|
-
*/
|
15
|
-
|
16
|
-
RGraph = window.RGraph || {isRGraph: true};
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
/**
|
22
|
-
* The scatter graph constructor
|
23
|
-
*
|
24
|
-
* @param object canvas The cxanvas object
|
25
|
-
* @param array data The chart data
|
26
|
-
*/
|
27
|
-
RGraph.Scatter = function (conf)
|
28
|
-
{
|
29
|
-
/**
|
30
|
-
* Allow for object config style
|
31
|
-
*/
|
32
|
-
if ( typeof conf === 'object'
|
33
|
-
&& typeof conf.data === 'object'
|
34
|
-
&& typeof conf.id === 'string') {
|
35
|
-
|
36
|
-
var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
|
37
|
-
|
38
|
-
this.data = new Array(conf.data.length);
|
39
|
-
|
40
|
-
// Store the data set(s)
|
41
|
-
this.data = RGraph.arrayClone(conf.data);
|
42
|
-
|
43
|
-
|
44
|
-
// Account for just one dataset being given
|
45
|
-
if (typeof conf.data === 'object' && typeof conf.data[0] === 'object' && (typeof conf.data[0][0] === 'number' || typeof conf.data[0][0] === 'string')) {
|
46
|
-
var tmp = RGraph.arrayClone(conf.data);
|
47
|
-
conf.data = new Array();
|
48
|
-
conf.data[0] = RGraph.arrayClone(tmp);
|
49
|
-
|
50
|
-
this.data = RGraph.arrayClone(conf.data);
|
51
|
-
}
|
52
|
-
|
53
|
-
} else {
|
54
|
-
|
55
|
-
var conf = {id: conf};
|
56
|
-
conf.data = arguments[1];
|
57
|
-
|
58
|
-
|
59
|
-
this.data = [];
|
60
|
-
|
61
|
-
// Handle multiple datasets being given as one argument
|
62
|
-
if (arguments[1][0] && arguments[1][0][0] && typeof arguments[1][0][0] == 'object') {
|
63
|
-
// Store the data set(s)
|
64
|
-
for (var i=0; i<arguments[1].length; ++i) {
|
65
|
-
this.data[i] = RGraph.arrayClone(arguments[1][i]);
|
66
|
-
}
|
67
|
-
|
68
|
-
// Handle multiple data sets being supplied as seperate arguments
|
69
|
-
} else {
|
70
|
-
|
71
|
-
// Store the data set(s)
|
72
|
-
for (var i=1; i<arguments.length; ++i) {
|
73
|
-
this.data[i - 1] = RGraph.arrayClone(arguments[i]);
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
// If necessary convert X/Y values passed as strings to numbers
|
79
|
-
for (var i=0,len=this.data.length; i<len; ++i) { // Datasets
|
80
|
-
for (var j=0,len2=this.data[i].length; j<len2; ++j) { // Points
|
81
|
-
|
82
|
-
// Handle the conversion of X values
|
83
|
-
if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][0] === 'string') {
|
84
|
-
if (this.data[i][j][0].match(/^[.0-9]+$/)) {
|
85
|
-
this.data[i][j][0] = parseFloat(this.data[i][j][0]);
|
86
|
-
} else if (this.data[i][j][0] === '') {
|
87
|
-
this.data[i][j][0] = 0;
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
// Handle the conversion of Y values
|
92
|
-
if (typeof this.data[i][j] === 'object' && !RGraph.isNull(this.data[i][j]) && typeof this.data[i][j][1] === 'string') {
|
93
|
-
if (this.data[i][j][1].match(/[.0-9]+/)) {
|
94
|
-
this.data[i][j][1] = parseFloat(this.data[i][j][1]);
|
95
|
-
} else if (this.data[i][j][1] === '') {
|
96
|
-
this.data[i][j][1] = 0;
|
97
|
-
}
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
|
103
|
-
this.id = conf.id;
|
104
|
-
this.canvas = document.getElementById(this.id);
|
105
|
-
this.canvas.__object__ = this;
|
106
|
-
this.context = this.canvas.getContext ? this.canvas.getContext('2d') : null;
|
107
|
-
this.max = 0;
|
108
|
-
this.coords = [];
|
109
|
-
this.type = 'scatter';
|
110
|
-
this.isRGraph = true;
|
111
|
-
this.uid = RGraph.CreateUID();
|
112
|
-
this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
|
113
|
-
this.colorsParsed = false;
|
114
|
-
this.coordsText = [];
|
115
|
-
this.original_colors = [];
|
116
|
-
this.firstDraw = true; // After the first draw this will be false
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
// Handle multiple datasets being given as one argument
|
122
|
-
//if (arguments[1][0] && arguments[1][0][0] && typeof(arguments[1][0][0]) == 'object') {
|
123
|
-
// // Store the data set(s)
|
124
|
-
// for (var i=0; i<arguments[1].length; ++i) {
|
125
|
-
// this.data[i] = arguments[1][i];
|
126
|
-
// }
|
127
|
-
|
128
|
-
// Handle multiple data sets being supplied as seperate arguments
|
129
|
-
//} else {
|
130
|
-
// Store the data set(s)
|
131
|
-
//for (var i=1; i<arguments.length; ++i) {
|
132
|
-
// this.data[i - 1] = arguments[i];
|
133
|
-
//}
|
134
|
-
//}
|
135
|
-
|
136
|
-
|
137
|
-
// Various config properties
|
138
|
-
this.properties = {
|
139
|
-
'chart.background.barcolor1': 'rgba(0,0,0,0)',
|
140
|
-
'chart.background.barcolor2': 'rgba(0,0,0,0)',
|
141
|
-
'chart.background.grid': true,
|
142
|
-
'chart.background.grid.width': 1,
|
143
|
-
'chart.background.grid.color': '#ddd',
|
144
|
-
'chart.background.grid.hsize': 20,
|
145
|
-
'chart.background.grid.vsize': 20,
|
146
|
-
'chart.background.hbars': null,
|
147
|
-
'chart.background.vbars': null,
|
148
|
-
'chart.background.grid.vlines': true,
|
149
|
-
'chart.background.grid.hlines': true,
|
150
|
-
'chart.background.grid.border': true,
|
151
|
-
'chart.background.grid.autofit':true,
|
152
|
-
'chart.background.grid.autofit.align': true,
|
153
|
-
'chart.background.grid.autofit.numhlines': 5,
|
154
|
-
'chart.background.grid.autofit.numvlines': 20,
|
155
|
-
'chart.background.image': null,
|
156
|
-
'chart.background.image.stretch': true,
|
157
|
-
'chart.background.image.x': null,
|
158
|
-
'chart.background.image.y': null,
|
159
|
-
'chart.background.image.w': null,
|
160
|
-
'chart.background.image.h': null,
|
161
|
-
'chart.background.image.align': null,
|
162
|
-
'chart.background.color': null,
|
163
|
-
'chart.text.size': 12,
|
164
|
-
'chart.text.angle': 0,
|
165
|
-
'chart.text.color': 'black',
|
166
|
-
'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif',
|
167
|
-
'chart.text.accessible': true,
|
168
|
-
'chart.text.accessible.overflow': 'visible',
|
169
|
-
'chart.text.accessible.pointerevents': false,
|
170
|
-
'chart.tooltips': [], // Default must be an empty array
|
171
|
-
'chart.tooltips.effect': 'fade',
|
172
|
-
'chart.tooltips.event': 'onmousemove',
|
173
|
-
'chart.tooltips.hotspot': 3,
|
174
|
-
'chart.tooltips.css.class': 'RGraph_tooltip',
|
175
|
-
'chart.tooltips.highlight': true,
|
176
|
-
'chart.tooltips.coords.page': false,
|
177
|
-
'chart.units.pre': '',
|
178
|
-
'chart.units.post': '',
|
179
|
-
'chart.numyticks': 10,
|
180
|
-
'chart.tickmarks': 'cross',
|
181
|
-
'chart.tickmarks.image.halign': 'center',
|
182
|
-
'chart.tickmarks.image.valign': 'center',
|
183
|
-
'chart.tickmarks.image.offsetx': 0,
|
184
|
-
'chart.tickmarks.image.offsety': 0,
|
185
|
-
'chart.ticksize': 5,
|
186
|
-
'chart.numxticks': true,
|
187
|
-
'chart.xaxis': true,
|
188
|
-
'chart.gutter.left': 25,
|
189
|
-
'chart.gutter.right': 25,
|
190
|
-
'chart.gutter.top': 25,
|
191
|
-
'chart.gutter.bottom': 30,
|
192
|
-
'chart.xmin': 0,
|
193
|
-
'chart.xmax': 0,
|
194
|
-
'chart.ymax': null,
|
195
|
-
'chart.ymin': 0,
|
196
|
-
'chart.scale.decimals': 0,
|
197
|
-
'chart.scale.point': '.',
|
198
|
-
'chart.scale.thousand': ',',
|
199
|
-
'chart.scale.zerostart': true,
|
200
|
-
'chart.title': '',
|
201
|
-
'chart.title.background': null,
|
202
|
-
'chart.title.hpos': null,
|
203
|
-
'chart.title.vpos': null,
|
204
|
-
'chart.title.bold': true,
|
205
|
-
'chart.title.font': null,
|
206
|
-
'chart.title.xaxis': '',
|
207
|
-
'chart.title.xaxis.bold': true,
|
208
|
-
'chart.title.xaxis.size': null,
|
209
|
-
'chart.title.xaxis.font': null,
|
210
|
-
'chart.title.yaxis': '',
|
211
|
-
'chart.title.yaxis.bold': true,
|
212
|
-
'chart.title.yaxis.size': null,
|
213
|
-
'chart.title.yaxis.font': null,
|
214
|
-
'chart.title.yaxis.color': null,
|
215
|
-
'chart.title.xaxis.pos': null,
|
216
|
-
'chart.title.yaxis.pos': null,
|
217
|
-
'chart.title.yaxis.x': null,
|
218
|
-
'chart.title.yaxis.y': null,
|
219
|
-
'chart.title.xaxis.x': null,
|
220
|
-
'chart.title.xaxis.y': null,
|
221
|
-
'chart.title.x': null,
|
222
|
-
'chart.title.y': null,
|
223
|
-
'chart.title.halign': null,
|
224
|
-
'chart.title.valign': null,
|
225
|
-
'chart.labels': [],
|
226
|
-
'chart.labels.bold': false,
|
227
|
-
'chart.labels.color': null,
|
228
|
-
'chart.labels.ingraph': null,
|
229
|
-
'chart.labels.above': false,
|
230
|
-
'chart.labels.above.size': 8,
|
231
|
-
'chart.labels.above.decimals': 0,
|
232
|
-
'chart.labels.offsetx': 0,
|
233
|
-
'chart.labels.offsety': 0,
|
234
|
-
'chart.ylabels.offsetx': 0,
|
235
|
-
'chart.ylabels.offsety': 0,
|
236
|
-
'chart.ylabels': true,
|
237
|
-
'chart.ylabels.count': 5,
|
238
|
-
'chart.ylabels.invert': false,
|
239
|
-
'chart.ylabels.specific': null,
|
240
|
-
'chart.ylabels.inside': false,
|
241
|
-
'chart.contextmenu': null,
|
242
|
-
'chart.defaultcolor': 'black',
|
243
|
-
'chart.xaxispos': 'bottom',
|
244
|
-
'chart.yaxispos': 'left',
|
245
|
-
'chart.crosshairs': false,
|
246
|
-
'chart.crosshairs.color': '#333',
|
247
|
-
'chart.crosshairs.linewidth': 1,
|
248
|
-
'chart.crosshairs.coords': false,
|
249
|
-
'chart.crosshairs.coords.fixed':true,
|
250
|
-
'chart.crosshairs.coords.fadeout':false,
|
251
|
-
'chart.crosshairs.coords.labels.x': 'X',
|
252
|
-
'chart.crosshairs.coords.labels.y': 'Y',
|
253
|
-
'chart.crosshairs.hline': true,
|
254
|
-
'chart.crosshairs.vline': true,
|
255
|
-
'chart.annotatable': false,
|
256
|
-
'chart.annotate.color': 'black',
|
257
|
-
'chart.line': false,
|
258
|
-
'chart.line.linewidth': 1,
|
259
|
-
'chart.line.colors': ['green', 'red'],
|
260
|
-
'chart.line.shadow.color': 'rgba(0,0,0,0)',
|
261
|
-
'chart.line.shadow.blur': 2,
|
262
|
-
'chart.line.shadow.offsetx': 3,
|
263
|
-
'chart.line.shadow.offsety': 3,
|
264
|
-
'chart.line.stepped': false,
|
265
|
-
'chart.line.visible': true,
|
266
|
-
'chart.noaxes': false,
|
267
|
-
'chart.noyaxis': false,
|
268
|
-
'chart.key': null,
|
269
|
-
'chart.key.background': 'white',
|
270
|
-
'chart.key.position': 'graph',
|
271
|
-
'chart.key.halign': 'right',
|
272
|
-
'chart.key.shadow': false,
|
273
|
-
'chart.key.shadow.color': '#666',
|
274
|
-
'chart.key.shadow.blur': 3,
|
275
|
-
'chart.key.shadow.offsetx': 2,
|
276
|
-
'chart.key.shadow.offsety': 2,
|
277
|
-
'chart.key.position.gutter.boxed': false,
|
278
|
-
'chart.key.position.x': null,
|
279
|
-
'chart.key.position.y': null,
|
280
|
-
|
281
|
-
'chart.key.interactive': false,
|
282
|
-
'chart.key.interactive.highlight.chart.fill': 'rgba(255,0,0,0.9)',
|
283
|
-
'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
|
284
|
-
|
285
|
-
'chart.key.color.shape': 'square',
|
286
|
-
'chart.key.rounded': true,
|
287
|
-
'chart.key.linewidth': 1,
|
288
|
-
'chart.key.colors': null,
|
289
|
-
'chart.key.text.color': 'black',
|
290
|
-
'chart.axis.color': 'black',
|
291
|
-
'chart.zoom.factor': 1.5,
|
292
|
-
'chart.zoom.fade.in': true,
|
293
|
-
'chart.zoom.fade.out': true,
|
294
|
-
'chart.zoom.hdir': 'right',
|
295
|
-
'chart.zoom.vdir': 'down',
|
296
|
-
'chart.zoom.frames': 25,
|
297
|
-
'chart.zoom.delay': 16.666,
|
298
|
-
'chart.zoom.shadow': true,
|
299
|
-
'chart.zoom.background': true,
|
300
|
-
'chart.zoom.action': 'zoom',
|
301
|
-
'chart.boxplot.width': 1,
|
302
|
-
'chart.boxplot.capped': true,
|
303
|
-
'chart.resizable': false,
|
304
|
-
'chart.resize.handle.background': null,
|
305
|
-
'chart.xmin': 0,
|
306
|
-
'chart.labels.specific.align': 'left',
|
307
|
-
'chart.xscale': false,
|
308
|
-
'chart.xscale.units.pre': '',
|
309
|
-
'chart.xscale.units.post': '',
|
310
|
-
'chart.xscale.numlabels': 10,
|
311
|
-
'chart.xscale.formatter': null,
|
312
|
-
'chart.xscale.decimals': 0,
|
313
|
-
'chart.xscale.thousand': ',',
|
314
|
-
'chart.xscale.point': '.',
|
315
|
-
'chart.noendxtick': false,
|
316
|
-
'chart.noendytick': true,
|
317
|
-
'chart.events.mousemove': null,
|
318
|
-
'chart.events.click': null,
|
319
|
-
'chart.highlight.stroke': 'rgba(0,0,0,0)',
|
320
|
-
'chart.highlight.fill': 'rgba(255,255,255,0.7)',
|
321
|
-
'chart.clearto': 'rgba(0,0,0,0)',
|
322
|
-
'chart.animation.trace': false,
|
323
|
-
'chart.animation.trace.clip': 1
|
324
|
-
}
|
325
|
-
|
326
|
-
/**
|
327
|
-
* This allows the data points to be given as dates as well as numbers. Formats supported by RGraph.parseDate() are accepted.
|
328
|
-
*
|
329
|
-
* ALSO: unrelated but this loop is also used to convert null values to an
|
330
|
-
* empty array
|
331
|
-
*/
|
332
|
-
for (var i=0; i<this.data.length; ++i) {
|
333
|
-
for (var j=0; j<this.data[i].length; ++j) {
|
334
|
-
|
335
|
-
// Convert null data points to an empty erray
|
336
|
-
if ( RGraph.isNull(this.data[i][j]) ) {
|
337
|
-
this.data[i][j] = [];
|
338
|
-
}
|
339
|
-
|
340
|
-
// Allow for the X point to be dates
|
341
|
-
if (this.data[i][j] && typeof(this.data[i][j][0]) == 'string') {
|
342
|
-
this.data[i][j][0] = RGraph.parseDate(this.data[i][j][0]);
|
343
|
-
}
|
344
|
-
}
|
345
|
-
}
|
346
|
-
|
347
|
-
|
348
|
-
/**
|
349
|
-
* Now make the data_arr array - all the data as one big array
|
350
|
-
*/
|
351
|
-
this.data_arr = [];
|
352
|
-
|
353
|
-
for (var i=0; i<this.data.length; ++i) {
|
354
|
-
for (var j=0; j<this.data[i].length; ++j) {
|
355
|
-
this.data_arr.push(this.data[i][j]);
|
356
|
-
}
|
357
|
-
}
|
358
|
-
|
359
|
-
// Create the $ objects so that they can be used
|
360
|
-
for (var i=0; i<this.data_arr.length; ++i) {
|
361
|
-
this['$' + i] = {}
|
362
|
-
}
|
363
|
-
|
364
|
-
|
365
|
-
// Check for support
|
366
|
-
if (!this.canvas) {
|
367
|
-
alert('[SCATTER] No canvas support');
|
368
|
-
return;
|
369
|
-
}
|
370
|
-
|
371
|
-
|
372
|
-
/**
|
373
|
-
* Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
|
374
|
-
* done already
|
375
|
-
*/
|
376
|
-
if (!this.canvas.__rgraph_aa_translated__) {
|
377
|
-
this.context.translate(0.5,0.5);
|
378
|
-
|
379
|
-
this.canvas.__rgraph_aa_translated__ = true;
|
380
|
-
}
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
// Short variable names
|
385
|
-
var RG = RGraph,
|
386
|
-
ca = this.canvas,
|
387
|
-
co = ca.getContext('2d'),
|
388
|
-
prop = this.properties,
|
389
|
-
pa2 = RG.path2,
|
390
|
-
win = window,
|
391
|
-
doc = document,
|
392
|
-
ma = Math
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
/**
|
397
|
-
* "Decorate" the object with the generic effects if the effects library has been included
|
398
|
-
*/
|
399
|
-
if (RG.Effects && typeof RG.Effects.decorate === 'function') {
|
400
|
-
RG.Effects.decorate(this);
|
401
|
-
}
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
/**
|
410
|
-
* A simple setter
|
411
|
-
*
|
412
|
-
* @param string name The name of the property to set
|
413
|
-
* @param string value The value of the property
|
414
|
-
*/
|
415
|
-
this.set =
|
416
|
-
this.Set = function (name)
|
417
|
-
{
|
418
|
-
var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
|
419
|
-
|
420
|
-
/**
|
421
|
-
* the number of arguments is only one and it's an
|
422
|
-
* object - parse it for configuration data and return.
|
423
|
-
*/
|
424
|
-
if (arguments.length === 1 && typeof name === 'object') {
|
425
|
-
RG.parseObjectStyleConfig(this, name);
|
426
|
-
return this;
|
427
|
-
}
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
/**
|
432
|
-
* This should be done first - prepend the propertyy name with "chart." if necessary
|
433
|
-
*/
|
434
|
-
if (name.substr(0,6) != 'chart.') {
|
435
|
-
name = 'chart.' + name;
|
436
|
-
}
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
// Convert uppercase letters to dot+lower case letter
|
442
|
-
while(name.match(/([A-Z])/)) {
|
443
|
-
name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
|
444
|
-
}
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
/**
|
453
|
-
* BC for chart.xticks
|
454
|
-
*/
|
455
|
-
if (name == 'chart.xticks') {
|
456
|
-
name == 'chart.numxticks';
|
457
|
-
}
|
458
|
-
|
459
|
-
/**
|
460
|
-
* This is here because the key expects a name of "chart.colors"
|
461
|
-
*/
|
462
|
-
if (name == 'chart.line.colors') {
|
463
|
-
prop['chart.colors'] = value;
|
464
|
-
}
|
465
|
-
|
466
|
-
/**
|
467
|
-
* Allow compatibility with older property names
|
468
|
-
*/
|
469
|
-
if (name == 'chart.tooltip.hotspot') {
|
470
|
-
name = 'chart.tooltips.hotspot';
|
471
|
-
}
|
472
|
-
|
473
|
-
|
474
|
-
/**
|
475
|
-
* chart.yaxispos should be left or right
|
476
|
-
*/
|
477
|
-
if (name == 'chart.yaxispos' && value != 'left' && value != 'right') {
|
478
|
-
alert("[SCATTER] chart.yaxispos should be left or right. You've set it to: '" + value + "' Changing it to left");
|
479
|
-
value = 'left';
|
480
|
-
}
|
481
|
-
|
482
|
-
/**
|
483
|
-
* Check for xaxispos
|
484
|
-
*/
|
485
|
-
if (name == 'chart.xaxispos' ) {
|
486
|
-
if (value != 'bottom' && value != 'center') {
|
487
|
-
alert('[SCATTER] (' + this.id + ') chart.xaxispos should be center or bottom. Tried to set it to: ' + value + ' Changing it to center');
|
488
|
-
value = 'center';
|
489
|
-
}
|
490
|
-
}
|
491
|
-
|
492
|
-
/**
|
493
|
-
* Compatibility for chart.noxaxisoption
|
494
|
-
*/
|
495
|
-
if (name == 'chart.noxaxis' ) {
|
496
|
-
name = 'chart.xaxis';
|
497
|
-
value = !value;
|
498
|
-
}
|
499
|
-
prop[name.toLowerCase()] = value;
|
500
|
-
|
501
|
-
return this;
|
502
|
-
};
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
/**
|
508
|
-
* A simple getter
|
509
|
-
*
|
510
|
-
* @param string name The name of the property to set
|
511
|
-
*/
|
512
|
-
this.get =
|
513
|
-
this.Get = function (name)
|
514
|
-
{
|
515
|
-
/**
|
516
|
-
* This should be done first - prepend the property name with "chart." if necessary
|
517
|
-
*/
|
518
|
-
if (name.substr(0,6) != 'chart.') {
|
519
|
-
name = 'chart.' + name;
|
520
|
-
}
|
521
|
-
|
522
|
-
// Convert uppercase letters to dot+lower case letter
|
523
|
-
name = name.replace(/([A-Z])/g, function (str)
|
524
|
-
{
|
525
|
-
return '.' + String(RegExp.$1).toLowerCase()
|
526
|
-
});
|
527
|
-
|
528
|
-
return prop[name];
|
529
|
-
};
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
/**
|
535
|
-
* The function you call to draw the line chart
|
536
|
-
*/
|
537
|
-
this.draw =
|
538
|
-
this.Draw = function ()
|
539
|
-
{
|
540
|
-
// MUST be the first thing done!
|
541
|
-
if (typeof prop['chart.background.image'] === 'string') {
|
542
|
-
RG.DrawBackgroundImage(this);
|
543
|
-
}
|
544
|
-
|
545
|
-
|
546
|
-
/**
|
547
|
-
* Fire the onbeforedraw event
|
548
|
-
*/
|
549
|
-
RG.fireCustomEvent(this, 'onbeforedraw');
|
550
|
-
|
551
|
-
|
552
|
-
/**
|
553
|
-
* Parse the colors. This allows for simple gradient syntax
|
554
|
-
*/
|
555
|
-
if (!this.colorsParsed) {
|
556
|
-
this.parseColors();
|
557
|
-
|
558
|
-
// Don't want to do this again
|
559
|
-
this.colorsParsed = true;
|
560
|
-
}
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
/**
|
566
|
-
* Stop this growing uncontrollably
|
567
|
-
*/
|
568
|
-
this.coordsText = [];
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
/**
|
574
|
-
* This is new in May 2011 and facilitates indiviual gutter settings,
|
575
|
-
* eg chart.gutter.left
|
576
|
-
*/
|
577
|
-
this.gutterLeft = prop['chart.gutter.left'];
|
578
|
-
this.gutterRight = prop['chart.gutter.right'];
|
579
|
-
this.gutterTop = prop['chart.gutter.top'];
|
580
|
-
this.gutterBottom = prop['chart.gutter.bottom'];
|
581
|
-
|
582
|
-
// Go through all the data points and see if a tooltip has been given
|
583
|
-
this.hasTooltips = false;
|
584
|
-
var overHotspot = false;
|
585
|
-
|
586
|
-
// Reset the coords array
|
587
|
-
this.coords = [];
|
588
|
-
|
589
|
-
/**
|
590
|
-
* This facilitates the xmax, xmin and X values being dates
|
591
|
-
*/
|
592
|
-
if (typeof(prop['chart.xmin']) == 'string') prop['chart.xmin'] = RG.parseDate(prop['chart.xmin']);
|
593
|
-
if (typeof(prop['chart.xmax']) == 'string') prop['chart.xmax'] = RG.parseDate(prop['chart.xmax']);
|
594
|
-
|
595
|
-
/**
|
596
|
-
* Look for tooltips and populate chart.tooltips
|
597
|
-
*
|
598
|
-
* NB 26/01/2011 Updated so that chart.tooltips is ALWAYS populated
|
599
|
-
*/
|
600
|
-
if (!RGraph.ISOLD) {
|
601
|
-
this.Set('chart.tooltips', []);
|
602
|
-
for (var i=0,len=this.data.length; i<len; i+=1) {
|
603
|
-
for (var j =0,len2=this.data[i].length;j<len2; j+=1) {
|
604
|
-
|
605
|
-
if (this.data[i][j] && this.data[i][j][3]) {
|
606
|
-
prop['chart.tooltips'].push(this.data[i][j][3]);
|
607
|
-
this.hasTooltips = true;
|
608
|
-
} else {
|
609
|
-
prop['chart.tooltips'].push(null);
|
610
|
-
}
|
611
|
-
}
|
612
|
-
}
|
613
|
-
}
|
614
|
-
|
615
|
-
// Reset the maximum value
|
616
|
-
this.max = 0;
|
617
|
-
|
618
|
-
// Work out the maximum Y value
|
619
|
-
//if (prop['chart.ymax'] && prop['chart.ymax'] > 0) {
|
620
|
-
if (typeof prop['chart.ymax'] === 'number') {
|
621
|
-
|
622
|
-
this.max = prop['chart.ymax'];
|
623
|
-
this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
|
624
|
-
|
625
|
-
|
626
|
-
this.scale2 = RG.getScale2(this, {
|
627
|
-
'max':this.max,
|
628
|
-
'min':this.min,
|
629
|
-
'strict':true,
|
630
|
-
'scale.thousand':prop['chart.scale.thousand'],
|
631
|
-
'scale.point':prop['chart.scale.point'],
|
632
|
-
'scale.decimals':prop['chart.scale.decimals'],
|
633
|
-
'ylabels.count':prop['chart.ylabels.count'],
|
634
|
-
'scale.round':prop['chart.scale.round'],
|
635
|
-
'units.pre': prop['chart.units.pre'],
|
636
|
-
'units.post': prop['chart.units.post']
|
637
|
-
});
|
638
|
-
|
639
|
-
this.max = this.scale2.max;
|
640
|
-
this.min = this.scale2.min;
|
641
|
-
var decimals = prop['chart.scale.decimals'];
|
642
|
-
|
643
|
-
} else {
|
644
|
-
|
645
|
-
var i = 0;
|
646
|
-
var j = 0;
|
647
|
-
|
648
|
-
for (i=0,len=this.data.length; i<len; i+=1) {
|
649
|
-
for (j=0,len2=this.data[i].length; j<len2; j+=1) {
|
650
|
-
if (!RG.isNull(this.data[i][j]) && this.data[i][j][1] != null) {
|
651
|
-
this.max = Math.max(this.max, typeof(this.data[i][j][1]) == 'object' ? RG.array_max(this.data[i][j][1]) : Math.abs(this.data[i][j][1]));
|
652
|
-
}
|
653
|
-
}
|
654
|
-
}
|
655
|
-
|
656
|
-
this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
|
657
|
-
|
658
|
-
this.scale2 = RG.getScale2(this, {
|
659
|
-
'max':this.max,
|
660
|
-
'min':this.min,
|
661
|
-
'scale.thousand':prop['chart.scale.thousand'],
|
662
|
-
'scale.point':prop['chart.scale.point'],
|
663
|
-
'scale.decimals':prop['chart.scale.decimals'],
|
664
|
-
'ylabels.count':prop['chart.ylabels.count'],
|
665
|
-
'scale.round':prop['chart.scale.round'],
|
666
|
-
'units.pre': prop['chart.units.pre'],
|
667
|
-
'units.post': prop['chart.units.post']
|
668
|
-
});
|
669
|
-
|
670
|
-
this.max = this.scale2.max;
|
671
|
-
this.min = this.scale2.min;
|
672
|
-
}
|
673
|
-
|
674
|
-
this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
// Progressively Draw the chart
|
679
|
-
RG.background.Draw(this);
|
680
|
-
|
681
|
-
/**
|
682
|
-
* Draw any horizontal bars that have been specified
|
683
|
-
*/
|
684
|
-
if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length) {
|
685
|
-
RG.DrawBars(this);
|
686
|
-
}
|
687
|
-
|
688
|
-
/**
|
689
|
-
* Draw any vertical bars that have been specified
|
690
|
-
*/
|
691
|
-
if (prop['chart.background.vbars'] && prop['chart.background.vbars'].length) {
|
692
|
-
this.DrawVBars();
|
693
|
-
}
|
694
|
-
|
695
|
-
if (!prop['chart.noaxes']) {
|
696
|
-
this.DrawAxes();
|
697
|
-
}
|
698
|
-
|
699
|
-
this.DrawLabels();
|
700
|
-
|
701
|
-
// Clip the canvas so that the trace2 effect is facilitated
|
702
|
-
if (prop['chart.animation.trace']) {
|
703
|
-
co.save();
|
704
|
-
co.beginPath();
|
705
|
-
co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
|
706
|
-
co.clip();
|
707
|
-
}
|
708
|
-
|
709
|
-
for(i=0; i<this.data.length; ++i) {
|
710
|
-
this.DrawMarks(i);
|
711
|
-
|
712
|
-
// Set the shadow
|
713
|
-
co.shadowColor = prop['chart.line.shadow.color'];
|
714
|
-
co.shadowOffsetX = prop['chart.line.shadow.offsetx'];
|
715
|
-
co.shadowOffsetY = prop['chart.line.shadow.offsety'];
|
716
|
-
co.shadowBlur = prop['chart.line.shadow.blur'];
|
717
|
-
|
718
|
-
this.DrawLine(i);
|
719
|
-
|
720
|
-
// Turn the shadow off
|
721
|
-
RG.NoShadow(this);
|
722
|
-
}
|
723
|
-
|
724
|
-
|
725
|
-
if (prop['chart.line']) {
|
726
|
-
for (var i=0,len=this.data.length;i<len; i+=1) {
|
727
|
-
this.DrawMarks(i); // Call this again so the tickmarks appear over the line
|
728
|
-
}
|
729
|
-
}
|
730
|
-
|
731
|
-
if (prop['chart.animation.trace']) {
|
732
|
-
co.restore();
|
733
|
-
}
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
/**
|
738
|
-
* Setup the context menu if required
|
739
|
-
*/
|
740
|
-
if (prop['chart.contextmenu']) {
|
741
|
-
RG.ShowContext(this);
|
742
|
-
}
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
/**
|
747
|
-
* Draw the key if necessary
|
748
|
-
*/
|
749
|
-
if (prop['chart.key'] && prop['chart.key'].length) {
|
750
|
-
RG.DrawKey(this, prop['chart.key'], prop['chart.line.colors']);
|
751
|
-
}
|
752
|
-
|
753
|
-
|
754
|
-
/**
|
755
|
-
* Draw " above" labels if enabled
|
756
|
-
*/
|
757
|
-
if (prop['chart.labels.above']) {
|
758
|
-
this.DrawAboveLabels();
|
759
|
-
}
|
760
|
-
|
761
|
-
/**
|
762
|
-
* Draw the "in graph" labels, using the member function, NOT the shared function in RGraph.common.core.js
|
763
|
-
*/
|
764
|
-
this.DrawInGraphLabels(this);
|
765
|
-
|
766
|
-
|
767
|
-
/**
|
768
|
-
* This function enables resizing
|
769
|
-
*/
|
770
|
-
if (prop['chart.resizable']) {
|
771
|
-
RG.AllowResizing(this);
|
772
|
-
}
|
773
|
-
|
774
|
-
|
775
|
-
/**
|
776
|
-
* This installs the event listeners
|
777
|
-
*/
|
778
|
-
RG.InstallEventListeners(this);
|
779
|
-
|
780
|
-
|
781
|
-
/**
|
782
|
-
* Fire the onfirstdraw event
|
783
|
-
*/
|
784
|
-
if (this.firstDraw) {
|
785
|
-
RG.fireCustomEvent(this, 'onfirstdraw');
|
786
|
-
this.firstDraw = false;
|
787
|
-
this.firstDrawFunc();
|
788
|
-
}
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
/**
|
793
|
-
* Fire the RGraph ondraw event
|
794
|
-
*/
|
795
|
-
RG.FireCustomEvent(this, 'ondraw');
|
796
|
-
|
797
|
-
|
798
|
-
return this;
|
799
|
-
}
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
/**
|
805
|
-
* Draws the axes of the scatter graph
|
806
|
-
*/
|
807
|
-
this.drawAxes =
|
808
|
-
this.DrawAxes = function ()
|
809
|
-
{
|
810
|
-
var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
|
811
|
-
|
812
|
-
co.beginPath();
|
813
|
-
co.strokeStyle = prop['chart.axis.color'];
|
814
|
-
co.lineWidth = (prop['chart.axis.linewidth'] || 1) + 0.001; // Strange Chrome bug
|
815
|
-
|
816
|
-
// Draw the Y axis
|
817
|
-
if (prop['chart.noyaxis'] == false) {
|
818
|
-
if (prop['chart.yaxispos'] == 'left') {
|
819
|
-
co.moveTo(this.gutterLeft, this.gutterTop);
|
820
|
-
co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
|
821
|
-
} else {
|
822
|
-
co.moveTo(ca.width - this.gutterRight, this.gutterTop);
|
823
|
-
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
|
824
|
-
}
|
825
|
-
}
|
826
|
-
|
827
|
-
|
828
|
-
// Draw the X axis
|
829
|
-
if (prop['chart.xaxis']) {
|
830
|
-
if (prop['chart.xaxispos'] == 'center') {
|
831
|
-
co.moveTo(this.gutterLeft, Math.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
|
832
|
-
co.lineTo(ca.width - this.gutterRight, Math.round(this.gutterTop + ((ca.height - this.gutterTop - this.gutterBottom) / 2)));
|
833
|
-
} else {
|
834
|
-
co.moveTo(this.gutterLeft, ca.height - this.gutterBottom);
|
835
|
-
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
|
836
|
-
}
|
837
|
-
}
|
838
|
-
|
839
|
-
// Draw the Y tickmarks
|
840
|
-
if (prop['chart.noyaxis'] == false) {
|
841
|
-
var numyticks = prop['chart.numyticks'];
|
842
|
-
|
843
|
-
//for (y=this.gutterTop; y < ca.height - this.gutterBottom + (prop['chart.xaxispos'] == 'center' ? 1 : 0) ; y+=(graphHeight / numyticks)) {
|
844
|
-
for (i=0; i<numyticks; ++i) {
|
845
|
-
|
846
|
-
var y = ((ca.height - this.gutterTop - this.gutterBottom) / numyticks) * i;
|
847
|
-
y = y + this.gutterTop;
|
848
|
-
|
849
|
-
if (prop['chart.xaxispos'] == 'center' && i == (numyticks / 2)) {
|
850
|
-
continue;
|
851
|
-
}
|
852
|
-
|
853
|
-
if (prop['chart.yaxispos'] == 'left') {
|
854
|
-
co.moveTo(this.gutterLeft, Math.round(y));
|
855
|
-
co.lineTo(this.gutterLeft - 3, Math.round(y));
|
856
|
-
} else {
|
857
|
-
co.moveTo(ca.width - this.gutterRight +3, Math.round(y));
|
858
|
-
co.lineTo(ca.width - this.gutterRight, Math.round(y));
|
859
|
-
}
|
860
|
-
}
|
861
|
-
|
862
|
-
/**
|
863
|
-
* Draw the end Y tickmark if the X axis is in the centre
|
864
|
-
*/
|
865
|
-
if (prop['chart.numyticks'] > 0) {
|
866
|
-
if (prop['chart.xaxispos'] == 'center' && prop['chart.yaxispos'] == 'left') {
|
867
|
-
co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
|
868
|
-
co.lineTo(this.gutterLeft - 3, Math.round(ca.height - this.gutterBottom));
|
869
|
-
} else if (prop['chart.xaxispos'] == 'center') {
|
870
|
-
co.moveTo(ca.width - this.gutterRight + 3, Math.round(ca.height - this.gutterBottom));
|
871
|
-
co.lineTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
|
872
|
-
}
|
873
|
-
}
|
874
|
-
|
875
|
-
/**
|
876
|
-
* Draw an extra tick if the X axis isn't being shown
|
877
|
-
*/
|
878
|
-
if (prop['chart.xaxis'] == false && prop['chart.yaxispos'] == 'left') {
|
879
|
-
co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
|
880
|
-
co.lineTo(this.gutterLeft - 3, Math.round(ca.height - this.gutterBottom));
|
881
|
-
} else if (prop['chart.xaxis'] == false && prop['chart.yaxispos'] == 'right') {
|
882
|
-
co.moveTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
|
883
|
-
co.lineTo(ca.width - this.gutterRight + 3, Math.round(ca.height - this.gutterBottom));
|
884
|
-
}
|
885
|
-
}
|
886
|
-
|
887
|
-
|
888
|
-
/**
|
889
|
-
* Draw the X tickmarks
|
890
|
-
*/
|
891
|
-
if (prop['chart.numxticks'] > 0 && prop['chart.xaxis']) {
|
892
|
-
|
893
|
-
var x = 0;
|
894
|
-
var y = (prop['chart.xaxispos'] == 'center') ? this.gutterTop + (this.grapharea / 2) : (ca.height - this.gutterBottom);
|
895
|
-
this.xTickGap = (prop['chart.labels'] && prop['chart.labels'].length) ? ((ca.width - this.gutterLeft - this.gutterRight ) / prop['chart.labels'].length) : (ca.width - this.gutterLeft - this.gutterRight) / 10;
|
896
|
-
|
897
|
-
/**
|
898
|
-
* This allows the number of X tickmarks to be specified
|
899
|
-
*/
|
900
|
-
if (typeof(prop['chart.numxticks']) == 'number') {
|
901
|
-
this.xTickGap = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
|
902
|
-
}
|
903
|
-
|
904
|
-
|
905
|
-
for (x=(this.gutterLeft + (prop['chart.yaxispos'] == 'left' && prop['chart.noyaxis'] == false ? this.xTickGap : 0) );
|
906
|
-
x <= (ca.width - this.gutterRight - (prop['chart.yaxispos'] == 'left' || prop['chart.noyaxis'] == true ? -1 : 1));
|
907
|
-
x += this.xTickGap) {
|
908
|
-
|
909
|
-
if (prop['chart.yaxispos'] == 'left' && prop['chart.noendxtick'] == true && x == (ca.width - this.gutterRight) ) {
|
910
|
-
continue;
|
911
|
-
} else if (prop['chart.yaxispos'] == 'right' && prop['chart.noendxtick'] == true && x == this.gutterLeft) {
|
912
|
-
continue;
|
913
|
-
}
|
914
|
-
|
915
|
-
co.moveTo(Math.round(x), y - (prop['chart.xaxispos'] == 'center' ? 3 : 0));
|
916
|
-
co.lineTo(Math.round(x), y + 3);
|
917
|
-
}
|
918
|
-
|
919
|
-
}
|
920
|
-
|
921
|
-
co.stroke();
|
922
|
-
|
923
|
-
/**
|
924
|
-
* Reset the linewidth back to one
|
925
|
-
*/
|
926
|
-
co.lineWidth = 1;
|
927
|
-
};
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
/**
|
940
|
-
* Draws the labels on the scatter graph
|
941
|
-
*/
|
942
|
-
this.drawLabels =
|
943
|
-
this.DrawLabels = function ()
|
944
|
-
{
|
945
|
-
co.fillStyle = prop['chart.text.color'];
|
946
|
-
|
947
|
-
var font = prop['chart.text.font'],
|
948
|
-
xMin = prop['chart.xmin'],
|
949
|
-
xMax = prop['chart.xmax'],
|
950
|
-
yMax = this.scale2.max,
|
951
|
-
yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0,
|
952
|
-
text_size = prop['chart.text.size'],
|
953
|
-
units_pre = prop['chart.units.pre'],
|
954
|
-
units_post = prop['chart.units.post'],
|
955
|
-
numYLabels = prop['chart.ylabels.count'],
|
956
|
-
invert = prop['chart.ylabels.invert'],
|
957
|
-
inside = prop['chart.ylabels.inside'],
|
958
|
-
context = co,
|
959
|
-
canvas = ca,
|
960
|
-
boxed = false,
|
961
|
-
offsetx = prop['chart.ylabels.offsetx'],
|
962
|
-
offsety = prop['chart.ylabels.offsety']
|
963
|
-
|
964
|
-
this.halfTextHeight = text_size / 2;
|
965
|
-
|
966
|
-
|
967
|
-
this.halfGraphHeight = (ca.height - this.gutterTop - this.gutterBottom) / 2;
|
968
|
-
|
969
|
-
/**
|
970
|
-
* Draw the Y yaxis labels, be it at the top or center
|
971
|
-
*/
|
972
|
-
if (prop['chart.ylabels']) {
|
973
|
-
|
974
|
-
var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
|
975
|
-
var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
|
976
|
-
|
977
|
-
/**
|
978
|
-
* Now change the two things above if chart.ylabels.inside is specified
|
979
|
-
*/
|
980
|
-
if (inside) {
|
981
|
-
if (prop['chart.yaxispos'] == 'left') {
|
982
|
-
xPos = prop['chart.gutter.left'] + 5;
|
983
|
-
align = 'left';
|
984
|
-
boxed = true;
|
985
|
-
} else {
|
986
|
-
xPos = ca.width - prop['chart.gutter.right'] - 5;
|
987
|
-
align = 'right';
|
988
|
-
boxed = true;
|
989
|
-
}
|
990
|
-
}
|
991
|
-
|
992
|
-
if (prop['chart.xaxispos'] == 'center') {
|
993
|
-
|
994
|
-
|
995
|
-
/**
|
996
|
-
* Specific Y labels
|
997
|
-
*/
|
998
|
-
if (typeof(prop['chart.ylabels.specific']) == 'object' && prop['chart.ylabels.specific'] != null && prop['chart.ylabels.specific'].length) {
|
999
|
-
|
1000
|
-
var labels = prop['chart.ylabels.specific'];
|
1001
|
-
|
1002
|
-
if (prop['chart.ymin'] > 0) {
|
1003
|
-
labels = [];
|
1004
|
-
for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
|
1005
|
-
labels.push(prop['chart.ylabels.specific'][i]);
|
1006
|
-
}
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
for (var i=0; i<labels.length; ++i) {
|
1010
|
-
var y = this.gutterTop + (i * (this.grapharea / (labels.length * 2) ) );
|
1011
|
-
RG.Text2(this, {
|
1012
|
-
'font':font,
|
1013
|
-
'size':text_size,
|
1014
|
-
'x':xPos + offsetx,
|
1015
|
-
'y':y + offsety,
|
1016
|
-
'text':labels[i],
|
1017
|
-
'valign':'center',
|
1018
|
-
'halign':align,
|
1019
|
-
'bounding':boxed,
|
1020
|
-
'tag': 'labels.specific'
|
1021
|
-
});
|
1022
|
-
}
|
1023
|
-
|
1024
|
-
var reversed_labels = RG.array_reverse(labels);
|
1025
|
-
|
1026
|
-
for (var i=0; i<reversed_labels.length; ++i) {
|
1027
|
-
var y = this.gutterTop + (this.grapharea / 2) + ((i+1) * (this.grapharea / (labels.length * 2) ) );
|
1028
|
-
RG.Text2(this, {
|
1029
|
-
'font':font,
|
1030
|
-
'size':text_size,
|
1031
|
-
'x':xPos + offsetx,
|
1032
|
-
'y':y + offsety,
|
1033
|
-
'text':reversed_labels[i],
|
1034
|
-
'valign':'center',
|
1035
|
-
'halign':align,
|
1036
|
-
'bounding':boxed,
|
1037
|
-
'tag': 'labels.specific'
|
1038
|
-
});
|
1039
|
-
}
|
1040
|
-
|
1041
|
-
/**
|
1042
|
-
* Draw the center label if chart.ymin is specified
|
1043
|
-
*/
|
1044
|
-
if (prop['chart.ymin'] != 0) {
|
1045
|
-
RG.Text2(this, {
|
1046
|
-
'font':font,
|
1047
|
-
'size':text_size,
|
1048
|
-
'x':xPos + offsetx,
|
1049
|
-
'y':(this.grapharea / 2) + this.gutterTop + offsety,
|
1050
|
-
'text':prop['chart.ylabels.specific'][prop['chart.ylabels.specific'].length - 1],
|
1051
|
-
'valign':'center',
|
1052
|
-
'halign':align,
|
1053
|
-
'bounding':boxed,
|
1054
|
-
'tag': 'labels.specific'
|
1055
|
-
});
|
1056
|
-
}
|
1057
|
-
}
|
1058
|
-
|
1059
|
-
|
1060
|
-
if (!prop['chart.ylabels.specific'] && typeof numYLabels == 'number') {
|
1061
|
-
|
1062
|
-
/**
|
1063
|
-
* Draw the top half
|
1064
|
-
*/
|
1065
|
-
for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
|
1066
|
-
|
1067
|
-
//var value = ((this.max - this.min)/ numYLabels) * (i+1);
|
1068
|
-
//value = (invert ? this.max - value : value);
|
1069
|
-
//if (!invert) value += this.min;
|
1070
|
-
//value = value.toFixed(prop['chart.scale.decimals']);
|
1071
|
-
|
1072
|
-
if (!invert) {
|
1073
|
-
RG.Text2(this, {
|
1074
|
-
'font':font,
|
1075
|
-
'size': text_size,
|
1076
|
-
'x': xPos + offsetx,
|
1077
|
-
'y': this.gutterTop + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
|
1078
|
-
'valign': 'center',
|
1079
|
-
'halign':align,
|
1080
|
-
'bounding': boxed,
|
1081
|
-
'boundingFill': 'white',
|
1082
|
-
'text': this.scale2.labels[i],
|
1083
|
-
'tag': 'scale'
|
1084
|
-
});
|
1085
|
-
} else {
|
1086
|
-
RG.Text2(this, {
|
1087
|
-
'font':font,
|
1088
|
-
'size': text_size,
|
1089
|
-
'x': xPos + offsetx,
|
1090
|
-
'y': this.gutterTop + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
|
1091
|
-
'valign': 'center',
|
1092
|
-
'halign':align,
|
1093
|
-
'bounding': boxed,
|
1094
|
-
'boundingFill': 'white',
|
1095
|
-
'text': this.scale2.labels[this.scale2.labels.length - (i + 1)],
|
1096
|
-
'tag': 'scale'
|
1097
|
-
});
|
1098
|
-
}
|
1099
|
-
}
|
1100
|
-
|
1101
|
-
/**
|
1102
|
-
* Draw the bottom half
|
1103
|
-
*/
|
1104
|
-
for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
|
1105
|
-
|
1106
|
-
//var value = (((this.max - this.min)/ numYLabels) * i) + this.min;
|
1107
|
-
// value = (invert ? value : this.max - (value - this.min)).toFixed(prop['chart.scale.decimals']);
|
1108
|
-
|
1109
|
-
if (!invert) {
|
1110
|
-
RG.Text2(this, {
|
1111
|
-
'font':font,
|
1112
|
-
'size': text_size,
|
1113
|
-
'x': xPos + offsetx,
|
1114
|
-
'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - ((i/numYLabels) * this.halfGraphHeight) + offsety,
|
1115
|
-
'valign': 'center',
|
1116
|
-
'halign':align,
|
1117
|
-
'bounding': boxed,
|
1118
|
-
'boundingFill': 'white',
|
1119
|
-
'text': '-' + this.scale2.labels[len - (i+1)],
|
1120
|
-
'tag': 'scale'
|
1121
|
-
});
|
1122
|
-
} else {
|
1123
|
-
|
1124
|
-
// This ensures that the center label isn't drawn twice
|
1125
|
-
if (i == (len - 1)&& invert) {
|
1126
|
-
continue;
|
1127
|
-
}
|
1128
|
-
|
1129
|
-
RG.Text2(this, {
|
1130
|
-
'font':font,
|
1131
|
-
'size': text_size,
|
1132
|
-
'x': xPos + offsetx,
|
1133
|
-
'y': this.gutterTop + this.halfGraphHeight + this.halfGraphHeight - (((i + 1)/numYLabels) * this.halfGraphHeight) + offsety,
|
1134
|
-
'valign': 'center',
|
1135
|
-
'halign':align,
|
1136
|
-
'bounding': boxed,
|
1137
|
-
'boundingFill': 'white',
|
1138
|
-
'text': '-' + this.scale2.labels[i],
|
1139
|
-
'tag': 'scale'
|
1140
|
-
});
|
1141
|
-
}
|
1142
|
-
}
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
// If ymin is specified draw that
|
1148
|
-
if (!invert && (yMin > 0 || prop['chart.scale.zerostart'])) {
|
1149
|
-
RG.text2(this, {
|
1150
|
-
'font':font,
|
1151
|
-
'size': text_size,
|
1152
|
-
'x': xPos + offsetx,
|
1153
|
-
'y': this.gutterTop + this.halfGraphHeight + offsety,
|
1154
|
-
'valign': 'center',
|
1155
|
-
'halign':align,
|
1156
|
-
'bounding': boxed,
|
1157
|
-
'boundingFill': 'white',
|
1158
|
-
'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1159
|
-
'tag': 'scale'
|
1160
|
-
});
|
1161
|
-
}
|
1162
|
-
|
1163
|
-
if (invert) {
|
1164
|
-
RG.text2(this, {
|
1165
|
-
'font':font,
|
1166
|
-
'size': text_size,
|
1167
|
-
'x': xPos + offsetx,
|
1168
|
-
'y': this.gutterTop + offsety,
|
1169
|
-
'valign': 'center',
|
1170
|
-
'halign':align,
|
1171
|
-
'bounding': boxed,
|
1172
|
-
'boundingFill': 'white',
|
1173
|
-
'text': RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1174
|
-
'tag': 'scale'
|
1175
|
-
});
|
1176
|
-
|
1177
|
-
RG.text2(this, {
|
1178
|
-
'font':font,
|
1179
|
-
'size': text_size,
|
1180
|
-
'x': xPos + offsetx,
|
1181
|
-
'y': this.gutterTop + (this.halfGraphHeight * 2) + offsety,
|
1182
|
-
'valign': 'center',
|
1183
|
-
'halign':align,
|
1184
|
-
'bounding': boxed,
|
1185
|
-
'boundingFill': 'white',
|
1186
|
-
'text': '-' + RG.number_format(this, yMin.toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1187
|
-
'tag': 'scale'
|
1188
|
-
});
|
1189
|
-
}
|
1190
|
-
}
|
1191
|
-
|
1192
|
-
// X axis at the bottom
|
1193
|
-
} else {
|
1194
|
-
|
1195
|
-
var xPos = prop['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : ca.width - this.gutterRight + 5;
|
1196
|
-
var align = prop['chart.yaxispos'] == 'right' ? 'left' : 'right';
|
1197
|
-
|
1198
|
-
if (inside) {
|
1199
|
-
if (prop['chart.yaxispos'] == 'left') {
|
1200
|
-
xPos = prop['chart.gutter.left'] + 5;
|
1201
|
-
align = 'left';
|
1202
|
-
boxed = true;
|
1203
|
-
} else {
|
1204
|
-
xPos = ca.width - obj.gutterRight - 5;
|
1205
|
-
align = 'right';
|
1206
|
-
boxed = true;
|
1207
|
-
}
|
1208
|
-
}
|
1209
|
-
|
1210
|
-
/**
|
1211
|
-
* Specific Y labels
|
1212
|
-
*/
|
1213
|
-
if (typeof prop['chart.ylabels.specific'] == 'object' && prop['chart.ylabels.specific']) {
|
1214
|
-
|
1215
|
-
var labels = prop['chart.ylabels.specific'];
|
1216
|
-
|
1217
|
-
// Lose the last label
|
1218
|
-
if (prop['chart.ymin'] > 9999) {
|
1219
|
-
labels = [];
|
1220
|
-
for (var i=0; i<(prop['chart.ylabels.specific'].length - 1); ++i) {
|
1221
|
-
labels.push(prop['chart.ylabels.specific'][i]);
|
1222
|
-
}
|
1223
|
-
}
|
1224
|
-
|
1225
|
-
for (var i=0,len=labels.length; i<len; i+=1) {
|
1226
|
-
|
1227
|
-
var y = this.gutterTop + (i * (this.grapharea / (len - 1)) );
|
1228
|
-
|
1229
|
-
RG.Text2(this, {
|
1230
|
-
'font':font,
|
1231
|
-
'size':text_size,
|
1232
|
-
'x':xPos + offsetx,
|
1233
|
-
'y':y + offsety,
|
1234
|
-
'text':labels[i],
|
1235
|
-
'halign':align,
|
1236
|
-
'valign':'center',
|
1237
|
-
'bounding':boxed,
|
1238
|
-
'tag': 'scale'
|
1239
|
-
});
|
1240
|
-
}
|
1241
|
-
|
1242
|
-
/**
|
1243
|
-
* X axis at the bottom with a scale
|
1244
|
-
*/
|
1245
|
-
} else {
|
1246
|
-
|
1247
|
-
if (typeof(numYLabels) == 'number') {
|
1248
|
-
|
1249
|
-
if (invert) {
|
1250
|
-
|
1251
|
-
for (var i=0; i<numYLabels; ++i) {
|
1252
|
-
|
1253
|
-
//var value = ((this.max - this.min)/ numYLabels) * i;
|
1254
|
-
// value = value.toFixed(prop['chart.scale.decimals']);
|
1255
|
-
var interval = (ca.height - this.gutterTop - this.gutterBottom) / numYLabels;
|
1256
|
-
|
1257
|
-
RG.Text2(this, {
|
1258
|
-
'font':font,
|
1259
|
-
'size': text_size,
|
1260
|
-
'x': xPos + offsetx,
|
1261
|
-
'y': this.gutterTop + ((i+1) * interval) + offsety,
|
1262
|
-
'valign': 'center',
|
1263
|
-
'halign':align,
|
1264
|
-
'bounding': boxed,
|
1265
|
-
'boundingFill': 'white',
|
1266
|
-
'text': this.scale2.labels[i],
|
1267
|
-
'tag': 'scale'
|
1268
|
-
});
|
1269
|
-
}
|
1270
|
-
|
1271
|
-
|
1272
|
-
// No X axis being shown and there's no ymin. If ymin IS set its added further down
|
1273
|
-
if (!prop['chart.xaxis'] && !prop['chart.ymin']) {
|
1274
|
-
RG.Text2(this, {
|
1275
|
-
'font':font,
|
1276
|
-
'size': text_size,
|
1277
|
-
'x': xPos + offsetx,
|
1278
|
-
'y': this.gutterTop + offsety,
|
1279
|
-
'valign': 'center',
|
1280
|
-
'halign':align,
|
1281
|
-
'bounding': boxed,
|
1282
|
-
'boundingFill': 'white',
|
1283
|
-
'text': RG.number_format(this, (this.min).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1284
|
-
'tag': 'scale'
|
1285
|
-
});
|
1286
|
-
}
|
1287
|
-
|
1288
|
-
} else {
|
1289
|
-
for (var i=0,len=this.scale2.labels.length; i<len; i+=1) {
|
1290
|
-
|
1291
|
-
//var value = ((this.max - this.min)/ numYLabels) * (i+1);
|
1292
|
-
// value = (invert ? this.max - value : value);
|
1293
|
-
// if (!invert) value += this.min;
|
1294
|
-
// value = value.toFixed(prop['chart.scale.decimals']);
|
1295
|
-
|
1296
|
-
RG.Text2(this, {
|
1297
|
-
'font':font,
|
1298
|
-
'size': text_size,
|
1299
|
-
'x': xPos + offsetx,
|
1300
|
-
'y': this.gutterTop + this.grapharea - (((i + 1)/this.scale2.labels.length) * this.grapharea) + offsety,
|
1301
|
-
'valign': 'center',
|
1302
|
-
'halign':align,
|
1303
|
-
'bounding': boxed,
|
1304
|
-
'boundingFill': 'white',
|
1305
|
-
'text': this.scale2.labels[i],
|
1306
|
-
'tag': 'scale'
|
1307
|
-
});
|
1308
|
-
}
|
1309
|
-
|
1310
|
-
if (!prop['chart.xaxis'] && prop['chart.ymin'] == 0) {
|
1311
|
-
RG.Text2(this, {
|
1312
|
-
'font':font,
|
1313
|
-
'size': text_size,
|
1314
|
-
'x': xPos + offsetx,
|
1315
|
-
'y': ca.height - this.gutterBottom + offsety,
|
1316
|
-
'valign': 'center',
|
1317
|
-
'halign':align,
|
1318
|
-
'bounding': boxed,
|
1319
|
-
'boundingFill': 'white',
|
1320
|
-
'text': RG.number_format(this, (0).toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1321
|
-
'tag': 'scale'
|
1322
|
-
});
|
1323
|
-
}
|
1324
|
-
}
|
1325
|
-
}
|
1326
|
-
|
1327
|
-
if ( (prop['chart.ymin'] || prop['chart.scale.zerostart']) && !invert) {
|
1328
|
-
RG.text2(this, {
|
1329
|
-
'font':font,
|
1330
|
-
'size': text_size,
|
1331
|
-
'x': xPos + offsetx,
|
1332
|
-
'y': ca.height - this.gutterBottom + offsety,
|
1333
|
-
'valign': 'center',
|
1334
|
-
'halign':align,
|
1335
|
-
'bounding': boxed,
|
1336
|
-
'boundingFill': 'white',
|
1337
|
-
'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1338
|
-
'tag': 'scale'
|
1339
|
-
});
|
1340
|
-
} else if (invert) {
|
1341
|
-
RG.text2(this, {
|
1342
|
-
'font':font,
|
1343
|
-
'size': text_size,
|
1344
|
-
'x': xPos + offsetx,
|
1345
|
-
'y': this.gutterTop + offsety,
|
1346
|
-
'valign': 'center',
|
1347
|
-
'halign':align,
|
1348
|
-
'bounding': boxed,
|
1349
|
-
'boundingFill': 'white',
|
1350
|
-
'text': RG.number_format(this, prop['chart.ymin'].toFixed(prop['chart.scale.decimals']), units_pre, units_post),
|
1351
|
-
'tag': 'scale'
|
1352
|
-
});
|
1353
|
-
}
|
1354
|
-
}
|
1355
|
-
}
|
1356
|
-
}
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
/**
|
1362
|
-
* Draw an X scale
|
1363
|
-
*/
|
1364
|
-
if (prop['chart.xscale']) {
|
1365
|
-
|
1366
|
-
var numXLabels = prop['chart.xscale.numlabels'],
|
1367
|
-
y = ca.height - this.gutterBottom + 5 + (text_size / 2),
|
1368
|
-
units_pre_x = prop['chart.xscale.units.pre'],
|
1369
|
-
units_post_x = prop['chart.xscale.units.post'],
|
1370
|
-
decimals = prop['chart.xscale.decimals'],
|
1371
|
-
point = prop['chart.xscale.point'],
|
1372
|
-
thousand = prop['chart.xscale.thousand'],
|
1373
|
-
color = prop['chart.labels.color'],
|
1374
|
-
bold = prop['chart.labels.bold'],
|
1375
|
-
offsetx = prop['chart.labels.offsetx'],
|
1376
|
-
offsety = prop['chart.labels.offsety']
|
1377
|
-
|
1378
|
-
|
1379
|
-
if (!prop['chart.xmax']) {
|
1380
|
-
|
1381
|
-
var xmax = 0;
|
1382
|
-
var xmin = prop['chart.xmin'];
|
1383
|
-
|
1384
|
-
for (var ds=0,len=this.data.length; ds<len; ds+=1) {
|
1385
|
-
for (var point=0,len2=this.data[ds].length; point<len2; point+=1) {
|
1386
|
-
xmax = Math.max(xmax, this.data[ds][point][0]);
|
1387
|
-
}
|
1388
|
-
}
|
1389
|
-
} else {
|
1390
|
-
xmax = prop['chart.xmax'];
|
1391
|
-
xmin = prop['chart.xmin']
|
1392
|
-
}
|
1393
|
-
|
1394
|
-
this.xscale2 = RG.getScale2(this, {
|
1395
|
-
'max':xmax,
|
1396
|
-
'min': xmin,
|
1397
|
-
'scale.decimals': decimals,
|
1398
|
-
'scale.point': point,
|
1399
|
-
'scale.thousand': thousand,
|
1400
|
-
'units.pre': units_pre_x,
|
1401
|
-
'units.post': units_post_x,
|
1402
|
-
'ylabels.count': numXLabels,
|
1403
|
-
'strict': true
|
1404
|
-
});
|
1405
|
-
|
1406
|
-
this.Set('chart.xmax', this.xscale2.max);
|
1407
|
-
var interval = (ca.width - this.gutterLeft - this.gutterRight) / this.xscale2.labels.length;
|
1408
|
-
|
1409
|
-
for (var i=0,len=this.xscale2.labels.length; i<len; i+=1) {
|
1410
|
-
|
1411
|
-
var num = ( (prop['chart.xmax'] - prop['chart.xmin']) * ((i+1) / numXLabels)) + (xmin || 0),
|
1412
|
-
x = this.gutterLeft + ((i+1) * interval),
|
1413
|
-
|
1414
|
-
// Repeated a few lines down
|
1415
|
-
text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, num)) : this.xscale2.labels[i];
|
1416
|
-
|
1417
|
-
RG.text2(this, {
|
1418
|
-
'color': color,
|
1419
|
-
'font':font,
|
1420
|
-
'size': text_size,
|
1421
|
-
'bold': bold,
|
1422
|
-
'x': x + offsetx,
|
1423
|
-
'y': y + offsety,
|
1424
|
-
'valign': 'center',
|
1425
|
-
'halign':'center',
|
1426
|
-
'text':text,
|
1427
|
-
'tag': 'xscale'
|
1428
|
-
});
|
1429
|
-
}
|
1430
|
-
|
1431
|
-
// If the Y axis is on the right hand side - draw the left most X label
|
1432
|
-
// ** Always added now **
|
1433
|
-
|
1434
|
-
// Repeated a few lines up
|
1435
|
-
var text = typeof prop['chart.xscale.formatter'] === 'function' ? String(prop['chart.xscale.formatter'](this, prop['chart.xmin'])) : String(prop['chart.xmin']);
|
1436
|
-
|
1437
|
-
RG.text2(this, {
|
1438
|
-
'color': color,
|
1439
|
-
'font':font,
|
1440
|
-
'size': text_size,
|
1441
|
-
'bold':bold,
|
1442
|
-
'x': this.gutterLeft + offsetx,
|
1443
|
-
'y': y + offsety,
|
1444
|
-
'valign': 'center',
|
1445
|
-
'halign':'center',
|
1446
|
-
'text': text,
|
1447
|
-
'tag': 'xscale'
|
1448
|
-
});
|
1449
|
-
|
1450
|
-
/**
|
1451
|
-
* Draw X labels
|
1452
|
-
*/
|
1453
|
-
} else {
|
1454
|
-
|
1455
|
-
// Put the text on the X axis
|
1456
|
-
var graphArea = ca.width - this.gutterLeft - this.gutterRight;
|
1457
|
-
var xInterval = graphArea / prop['chart.labels'].length;
|
1458
|
-
var xPos = this.gutterLeft;
|
1459
|
-
var yPos = (ca.height - this.gutterBottom) + 3;
|
1460
|
-
var labels = prop['chart.labels'];
|
1461
|
-
var color = prop['chart.labels.color'];
|
1462
|
-
var bold = prop['chart.labels.bold'];
|
1463
|
-
var offsetx = prop['chart.labels.offsetx'];
|
1464
|
-
var offsety = prop['chart.labels.offsety'];
|
1465
|
-
|
1466
|
-
/**
|
1467
|
-
* Text angle
|
1468
|
-
*/
|
1469
|
-
var angle = 0;
|
1470
|
-
var valign = 'top';
|
1471
|
-
var halign = 'center';
|
1472
|
-
|
1473
|
-
if (prop['chart.text.angle'] > 0) {
|
1474
|
-
angle = -1 * prop['chart.text.angle'];
|
1475
|
-
valign = 'center';
|
1476
|
-
halign = 'right';
|
1477
|
-
yPos += 10;
|
1478
|
-
}
|
1479
|
-
|
1480
|
-
for (i=0; i<labels.length; ++i) {
|
1481
|
-
|
1482
|
-
if (typeof(labels[i]) == 'object') {
|
1483
|
-
|
1484
|
-
if (prop['chart.labels.specific.align'] == 'center') {
|
1485
|
-
var rightEdge = 0;
|
1486
|
-
|
1487
|
-
if (labels[i+1] && labels[i+1][1]) {
|
1488
|
-
rightEdge = labels[i+1][1];
|
1489
|
-
} else {
|
1490
|
-
rightEdge = prop['chart.xmax'];
|
1491
|
-
}
|
1492
|
-
|
1493
|
-
var offset = (this.getXCoord(rightEdge) - this.getXCoord(labels[i][1])) / 2;
|
1494
|
-
|
1495
|
-
} else {
|
1496
|
-
var offset = 5;
|
1497
|
-
}
|
1498
|
-
|
1499
|
-
|
1500
|
-
RG.text2(this, {
|
1501
|
-
'color': color,
|
1502
|
-
'font':font,
|
1503
|
-
'size': prop['chart.text.size'],
|
1504
|
-
'bold': bold,
|
1505
|
-
'x': this.getXCoord(labels[i][1]) + offset + offsetx,
|
1506
|
-
'y': yPos + offsety,
|
1507
|
-
'valign': valign,
|
1508
|
-
'halign':angle != 0 ? 'right' : (prop['chart.labels.specific.align'] == 'center' ? 'center' : 'left'),
|
1509
|
-
'text':String(labels[i][0]),
|
1510
|
-
'angle':angle,
|
1511
|
-
'marker':false,
|
1512
|
-
'tag': 'labels.specific'
|
1513
|
-
});
|
1514
|
-
|
1515
|
-
/**
|
1516
|
-
* Draw the gray indicator line
|
1517
|
-
*/
|
1518
|
-
co.beginPath();
|
1519
|
-
co.strokeStyle = '#bbb';
|
1520
|
-
co.moveTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom);
|
1521
|
-
co.lineTo(ma.round(this.gutterLeft + (graphArea * ((labels[i][1] - xMin)/ (prop['chart.xmax'] - xMin)))), ca.height - this.gutterBottom + 20);
|
1522
|
-
co.stroke();
|
1523
|
-
|
1524
|
-
} else {
|
1525
|
-
|
1526
|
-
RG.text2(this, {
|
1527
|
-
'color': color,
|
1528
|
-
'font':font,
|
1529
|
-
'size': prop['chart.text.size'],
|
1530
|
-
'bold': bold,
|
1531
|
-
'x': xPos + (xInterval / 2) + offsetx,
|
1532
|
-
'y': yPos + offsety,
|
1533
|
-
'valign': valign,
|
1534
|
-
'halign':halign,
|
1535
|
-
'text':String(labels[i]),
|
1536
|
-
'angle':angle,
|
1537
|
-
'tag': 'labels'
|
1538
|
-
});
|
1539
|
-
}
|
1540
|
-
|
1541
|
-
// Do this for the next time around
|
1542
|
-
xPos += xInterval;
|
1543
|
-
}
|
1544
|
-
|
1545
|
-
/**
|
1546
|
-
* Draw the final indicator line
|
1547
|
-
*/
|
1548
|
-
if (typeof(labels[0]) == 'object') {
|
1549
|
-
co.beginPath();
|
1550
|
-
co.strokeStyle = '#bbb';
|
1551
|
-
co.moveTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom);
|
1552
|
-
co.lineTo(this.gutterLeft + graphArea, ca.height - this.gutterBottom + 20);
|
1553
|
-
co.stroke();
|
1554
|
-
}
|
1555
|
-
}
|
1556
|
-
};
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
/**
|
1572
|
-
* Draws the actual scatter graph marks
|
1573
|
-
*
|
1574
|
-
* @param i integer The dataset index
|
1575
|
-
*/
|
1576
|
-
this.drawMarks =
|
1577
|
-
this.DrawMarks = function (i)
|
1578
|
-
{
|
1579
|
-
/**
|
1580
|
-
* Reset the coords array
|
1581
|
-
*/
|
1582
|
-
this.coords[i] = [];
|
1583
|
-
|
1584
|
-
/**
|
1585
|
-
* Plot the values
|
1586
|
-
*/
|
1587
|
-
var xmax = prop['chart.xmax'];
|
1588
|
-
var default_color = prop['chart.defaultcolor'];
|
1589
|
-
|
1590
|
-
for (var j=0,len=this.data[i].length; j<len; j+=1) {
|
1591
|
-
/**
|
1592
|
-
* This is here because tooltips are optional
|
1593
|
-
*/
|
1594
|
-
var data_points = this.data[i];
|
1595
|
-
|
1596
|
-
// Allow for null points
|
1597
|
-
if (RG.isNull(data_points[j])) {
|
1598
|
-
continue;
|
1599
|
-
}
|
1600
|
-
|
1601
|
-
var xCoord = data_points[j][0];
|
1602
|
-
var yCoord = data_points[j][1];
|
1603
|
-
var color = data_points[j][2] ? data_points[j][2] : default_color;
|
1604
|
-
var tooltip = (data_points[j] && data_points[j][3]) ? data_points[j][3] : null;
|
1605
|
-
|
1606
|
-
|
1607
|
-
this.DrawMark(
|
1608
|
-
i,
|
1609
|
-
xCoord,
|
1610
|
-
yCoord,
|
1611
|
-
xmax,
|
1612
|
-
this.scale2.max,
|
1613
|
-
color,
|
1614
|
-
tooltip,
|
1615
|
-
this.coords[i],
|
1616
|
-
data_points,
|
1617
|
-
j
|
1618
|
-
);
|
1619
|
-
}
|
1620
|
-
};
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
/**
|
1626
|
-
* Draws a single scatter mark
|
1627
|
-
*/
|
1628
|
-
this.drawMark =
|
1629
|
-
this.DrawMark = function (data_set_index, x, y, xMax, yMax, color, tooltip, coords, data, data_index)
|
1630
|
-
{
|
1631
|
-
var tickmarks = prop['chart.tickmarks'];
|
1632
|
-
var tickSize = prop['chart.ticksize'];
|
1633
|
-
var xMin = prop['chart.xmin'];
|
1634
|
-
var x = ((x - xMin) / (xMax - xMin)) * (ca.width - this.gutterLeft - this.gutterRight);
|
1635
|
-
var originalX = x;
|
1636
|
-
var originalY = y;
|
1637
|
-
|
1638
|
-
/**
|
1639
|
-
* This allows chart.tickmarks to be an array
|
1640
|
-
*/
|
1641
|
-
if (tickmarks && typeof(tickmarks) == 'object') {
|
1642
|
-
tickmarks = tickmarks[data_set_index];
|
1643
|
-
}
|
1644
|
-
|
1645
|
-
|
1646
|
-
/**
|
1647
|
-
* This allows chart.ticksize to be an array
|
1648
|
-
*/
|
1649
|
-
if (typeof(tickSize) == 'object') {
|
1650
|
-
var tickSize = tickSize[data_set_index];
|
1651
|
-
var halfTickSize = tickSize / 2;
|
1652
|
-
} else {
|
1653
|
-
var halfTickSize = tickSize / 2;
|
1654
|
-
}
|
1655
|
-
|
1656
|
-
|
1657
|
-
/**
|
1658
|
-
* This bit is for boxplots only
|
1659
|
-
*/
|
1660
|
-
if ( y
|
1661
|
-
&& typeof(y) == 'object'
|
1662
|
-
&& typeof(y[0]) == 'number'
|
1663
|
-
&& typeof(y[1]) == 'number'
|
1664
|
-
&& typeof(y[2]) == 'number'
|
1665
|
-
&& typeof(y[3]) == 'number'
|
1666
|
-
&& typeof(y[4]) == 'number'
|
1667
|
-
) {
|
1668
|
-
|
1669
|
-
//var yMin = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
|
1670
|
-
this.Set('chart.boxplot', true);
|
1671
|
-
//this.graphheight = ca.height - this.gutterTop - this.gutterBottom;
|
1672
|
-
|
1673
|
-
//if (prop['chart.xaxispos'] == 'center') {
|
1674
|
-
// this.graphheight /= 2;
|
1675
|
-
//}
|
1676
|
-
|
1677
|
-
|
1678
|
-
var y0 = this.getYCoord(y[0]);//(this.graphheight) - ((y[4] - yMin) / (yMax - yMin)) * (this.graphheight);
|
1679
|
-
var y1 = this.getYCoord(y[1]);//(this.graphheight) - ((y[3] - yMin) / (yMax - yMin)) * (this.graphheight);
|
1680
|
-
var y2 = this.getYCoord(y[2]);//(this.graphheight) - ((y[2] - yMin) / (yMax - yMin)) * (this.graphheight);
|
1681
|
-
var y3 = this.getYCoord(y[3]);//(this.graphheight) - ((y[1] - yMin) / (yMax - yMin)) * (this.graphheight);
|
1682
|
-
var y4 = this.getYCoord(y[4]);//(this.graphheight) - ((y[0] - yMin) / (yMax - yMin)) * (this.graphheight);
|
1683
|
-
|
1684
|
-
|
1685
|
-
var col1 = y[5];
|
1686
|
-
var col2 = y[6];
|
1687
|
-
|
1688
|
-
var boxWidth = typeof(y[7]) == 'number' ? y[7] : prop['chart.boxplot.width'];
|
1689
|
-
|
1690
|
-
//var y = this.graphheight - y2;
|
1691
|
-
|
1692
|
-
} else {
|
1693
|
-
|
1694
|
-
/**
|
1695
|
-
* The new way of getting the Y coord. This function (should) handle everything
|
1696
|
-
*/
|
1697
|
-
var yCoord = this.getYCoord(y);
|
1698
|
-
}
|
1699
|
-
|
1700
|
-
//if (prop['chart.xaxispos'] == 'center'] {
|
1701
|
-
// y /= 2;
|
1702
|
-
// y += this.halfGraphHeight;
|
1703
|
-
//
|
1704
|
-
// if (prop['chart.ylabels.invert']) {
|
1705
|
-
// p(y)
|
1706
|
-
// }
|
1707
|
-
//}
|
1708
|
-
|
1709
|
-
/**
|
1710
|
-
* Account for the X axis being at the centre
|
1711
|
-
*/
|
1712
|
-
// This is so that points are on the graph, and not the gutter - which helps
|
1713
|
-
x += this.gutterLeft;
|
1714
|
-
//y = ca.height - this.gutterBottom - y;
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
co.beginPath();
|
1720
|
-
|
1721
|
-
// Color
|
1722
|
-
co.strokeStyle = color;
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
/**
|
1727
|
-
* Boxplots
|
1728
|
-
*/
|
1729
|
-
if (prop['chart.boxplot']) {
|
1730
|
-
|
1731
|
-
// boxWidth is a scale value, so convert it to a pixel vlue
|
1732
|
-
boxWidth = (boxWidth / prop['chart.xmax']) * (ca.width -this.gutterLeft - this.gutterRight);
|
1733
|
-
|
1734
|
-
var halfBoxWidth = boxWidth / 2;
|
1735
|
-
|
1736
|
-
if (prop['chart.line.visible']) {
|
1737
|
-
co.beginPath();
|
1738
|
-
|
1739
|
-
//TODO Make this color configurable
|
1740
|
-
co.strokeRect(x - halfBoxWidth, y1, boxWidth, y3 - y1);
|
1741
|
-
|
1742
|
-
// Draw the upper coloured box if a value is specified
|
1743
|
-
if (col1) {
|
1744
|
-
co.fillStyle = col1;
|
1745
|
-
co.fillRect(x - halfBoxWidth, y1, boxWidth, y2 - y1);
|
1746
|
-
}
|
1747
|
-
|
1748
|
-
// Draw the lower coloured box if a value is specified
|
1749
|
-
if (col2) {
|
1750
|
-
co.fillStyle = col2;
|
1751
|
-
co.fillRect(x - halfBoxWidth, y2, boxWidth, y3 - y2);
|
1752
|
-
}
|
1753
|
-
co.stroke();
|
1754
|
-
|
1755
|
-
// Now draw the whiskers
|
1756
|
-
co.beginPath();
|
1757
|
-
if (prop['chart.boxplot.capped']) {
|
1758
|
-
co.moveTo(x - halfBoxWidth, Math.round(y0));
|
1759
|
-
co.lineTo(x + halfBoxWidth, Math.round(y0));
|
1760
|
-
}
|
1761
|
-
|
1762
|
-
co.moveTo(Math.round(x), y0);
|
1763
|
-
co.lineTo(Math.round(x ), y1);
|
1764
|
-
|
1765
|
-
if (prop['chart.boxplot.capped']) {
|
1766
|
-
co.moveTo(x - halfBoxWidth, Math.round(y4));
|
1767
|
-
co.lineTo(x + halfBoxWidth, Math.round(y4));
|
1768
|
-
}
|
1769
|
-
|
1770
|
-
co.moveTo(Math.round(x), y4);
|
1771
|
-
co.lineTo(Math.round(x), y3);
|
1772
|
-
|
1773
|
-
co.stroke();
|
1774
|
-
}
|
1775
|
-
}
|
1776
|
-
|
1777
|
-
|
1778
|
-
/**
|
1779
|
-
* Draw the tickmark, but not for boxplots
|
1780
|
-
*/
|
1781
|
-
if (prop['chart.line.visible'] && typeof(y) == 'number' && !y0 && !y1 && !y2 && !y3 && !y4) {
|
1782
|
-
|
1783
|
-
if (tickmarks == 'circle') {
|
1784
|
-
co.arc(x, yCoord, halfTickSize, 0, 6.28, 0);
|
1785
|
-
co.fillStyle = color;
|
1786
|
-
co.fill();
|
1787
|
-
|
1788
|
-
} else if (tickmarks == 'plus') {
|
1789
|
-
|
1790
|
-
co.moveTo(x, yCoord - halfTickSize);
|
1791
|
-
co.lineTo(x, yCoord + halfTickSize);
|
1792
|
-
co.moveTo(x - halfTickSize, yCoord);
|
1793
|
-
co.lineTo(x + halfTickSize, yCoord);
|
1794
|
-
co.stroke();
|
1795
|
-
|
1796
|
-
} else if (tickmarks == 'square') {
|
1797
|
-
co.strokeStyle = color;
|
1798
|
-
co.fillStyle = color;
|
1799
|
-
co.fillRect(
|
1800
|
-
x - halfTickSize,
|
1801
|
-
yCoord - halfTickSize,
|
1802
|
-
tickSize,
|
1803
|
-
tickSize
|
1804
|
-
);
|
1805
|
-
//co.fill();
|
1806
|
-
|
1807
|
-
} else if (tickmarks == 'cross') {
|
1808
|
-
|
1809
|
-
co.moveTo(x - halfTickSize, yCoord - halfTickSize);
|
1810
|
-
co.lineTo(x + halfTickSize, yCoord + halfTickSize);
|
1811
|
-
co.moveTo(x + halfTickSize, yCoord - halfTickSize);
|
1812
|
-
co.lineTo(x - halfTickSize, yCoord + halfTickSize);
|
1813
|
-
|
1814
|
-
co.stroke();
|
1815
|
-
|
1816
|
-
/**
|
1817
|
-
* Diamond shape tickmarks
|
1818
|
-
*/
|
1819
|
-
} else if (tickmarks == 'diamond') {
|
1820
|
-
co.fillStyle = co.strokeStyle;
|
1821
|
-
|
1822
|
-
co.moveTo(x, yCoord - halfTickSize);
|
1823
|
-
co.lineTo(x + halfTickSize, yCoord);
|
1824
|
-
co.lineTo(x, yCoord + halfTickSize);
|
1825
|
-
co.lineTo(x - halfTickSize, yCoord);
|
1826
|
-
co.lineTo(x, yCoord - halfTickSize);
|
1827
|
-
|
1828
|
-
co.fill();
|
1829
|
-
co.stroke();
|
1830
|
-
|
1831
|
-
/**
|
1832
|
-
* Custom tickmark style
|
1833
|
-
*/
|
1834
|
-
} else if (typeof(tickmarks) == 'function') {
|
1835
|
-
|
1836
|
-
var graphWidth = ca.width - this.gutterLeft - this.gutterRight
|
1837
|
-
var graphheight = ca.height - this.gutterTop - this.gutterBottom;
|
1838
|
-
var xVal = ((x - this.gutterLeft) / graphWidth) * xMax;
|
1839
|
-
var yVal = ((graphheight - (yCoord - this.gutterTop)) / graphheight) * yMax;
|
1840
|
-
|
1841
|
-
tickmarks(this, data, x, yCoord, xVal, yVal, xMax, yMax, color, data_set_index, data_index)
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
/**
|
1860
|
-
* Image based tickmark
|
1861
|
-
*/
|
1862
|
-
// lineData, xPos, yPos, color, isShadow, prevX, prevY, tickmarks, index
|
1863
|
-
} else if (
|
1864
|
-
typeof tickmarks === 'string' &&
|
1865
|
-
(
|
1866
|
-
tickmarks.substr(0, 6) === 'image:' ||
|
1867
|
-
tickmarks.substr(0, 5) === 'data:' ||
|
1868
|
-
tickmarks.substr(0, 1) === '/' ||
|
1869
|
-
tickmarks.substr(0, 3) === '../' ||
|
1870
|
-
tickmarks.substr(0, 7) === 'images/'
|
1871
|
-
)
|
1872
|
-
) {
|
1873
|
-
|
1874
|
-
var img = new Image();
|
1875
|
-
|
1876
|
-
if (tickmarks.substr(0, 6) === 'image:') {
|
1877
|
-
img.src = tickmarks.substr(6);
|
1878
|
-
} else {
|
1879
|
-
img.src = tickmarks;
|
1880
|
-
}
|
1881
|
-
|
1882
|
-
|
1883
|
-
img.onload = function ()
|
1884
|
-
{
|
1885
|
-
if (prop['chart.tickmarks.image.halign'] === 'center') x -= (this.width / 2);
|
1886
|
-
if (prop['chart.tickmarks.image.halign'] === 'right') x -= this.width;
|
1887
|
-
|
1888
|
-
if (prop['chart.tickmarks.image.valign'] === 'center') yCoord -= (this.height / 2);
|
1889
|
-
if (prop['chart.tickmarks.image.valign'] === 'bottom') yCoord -= this.height;
|
1890
|
-
|
1891
|
-
x += prop['chart.tickmarks.image.offsetx'];
|
1892
|
-
yCoord += prop['chart.tickmarks.image.offsety'];
|
1893
|
-
|
1894
|
-
co.drawImage(this, x, yCoord);
|
1895
|
-
}
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
/**
|
1902
|
-
* No tickmarks
|
1903
|
-
*/
|
1904
|
-
} else if (tickmarks === null) {
|
1905
|
-
|
1906
|
-
/**
|
1907
|
-
* Unknown tickmark type
|
1908
|
-
*/
|
1909
|
-
} else {
|
1910
|
-
alert('[SCATTER] (' + this.id + ') Unknown tickmark style: ' + tickmarks );
|
1911
|
-
}
|
1912
|
-
}
|
1913
|
-
|
1914
|
-
/**
|
1915
|
-
* Add the tickmark to the coords array
|
1916
|
-
*/
|
1917
|
-
if ( prop['chart.boxplot']
|
1918
|
-
&& typeof y0 === 'number'
|
1919
|
-
&& typeof y1 === 'number'
|
1920
|
-
&& typeof y2 === 'number'
|
1921
|
-
&& typeof y3 === 'number'
|
1922
|
-
&& typeof y4 === 'number') {
|
1923
|
-
|
1924
|
-
x = [x - halfBoxWidth, x + halfBoxWidth];
|
1925
|
-
yCoord = [y0, y1, y2, y3, y4];
|
1926
|
-
}
|
1927
|
-
|
1928
|
-
coords.push([x, yCoord, tooltip]);
|
1929
|
-
};
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
/**
|
1935
|
-
* Draws an optional line connecting the tick marks.
|
1936
|
-
*
|
1937
|
-
* @param i The index of the dataset to use
|
1938
|
-
*/
|
1939
|
-
this.drawLine =
|
1940
|
-
this.DrawLine = function (i)
|
1941
|
-
{
|
1942
|
-
if (typeof(prop['chart.line.visible']) == 'boolean' && prop['chart.line.visible'] == false) {
|
1943
|
-
return;
|
1944
|
-
}
|
1945
|
-
|
1946
|
-
if (prop['chart.line'] && this.coords[i].length >= 2) {
|
1947
|
-
|
1948
|
-
if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
|
1949
|
-
co.setLineDash(prop['chart.line.dash']);
|
1950
|
-
}
|
1951
|
-
|
1952
|
-
co.lineCap = 'round';
|
1953
|
-
co.lineJoin = 'round';
|
1954
|
-
co.lineWidth = this.getLineWidth(i);// i is the index of the set of coordinates
|
1955
|
-
co.strokeStyle = prop['chart.line.colors'][i];
|
1956
|
-
|
1957
|
-
co.beginPath();
|
1958
|
-
|
1959
|
-
var prevY = null;
|
1960
|
-
var currY = null;
|
1961
|
-
|
1962
|
-
for (var j=0,len=this.coords[i].length; j<len; j+=1) {
|
1963
|
-
|
1964
|
-
|
1965
|
-
var xPos = this.coords[i][j][0];
|
1966
|
-
var yPos = this.coords[i][j][1];
|
1967
|
-
|
1968
|
-
if (j > 0) prevY = this.coords[i][j - 1][1];
|
1969
|
-
currY = yPos;
|
1970
|
-
|
1971
|
-
if (j == 0 || RG.is_null(prevY) || RG.is_null(currY)) {
|
1972
|
-
co.moveTo(xPos, yPos);
|
1973
|
-
} else {
|
1974
|
-
|
1975
|
-
// Stepped?
|
1976
|
-
var stepped = prop['chart.line.stepped'];
|
1977
|
-
|
1978
|
-
if ( (typeof stepped == 'boolean' && stepped)
|
1979
|
-
|| (typeof stepped == 'object' && stepped[i])
|
1980
|
-
) {
|
1981
|
-
co.lineTo(this.coords[i][j][0], this.coords[i][j - 1][1]);
|
1982
|
-
}
|
1983
|
-
|
1984
|
-
co.lineTo(xPos, yPos);
|
1985
|
-
}
|
1986
|
-
}
|
1987
|
-
co.stroke();
|
1988
|
-
|
1989
|
-
/**
|
1990
|
-
* Set the linedash back to the default
|
1991
|
-
*/
|
1992
|
-
if (prop['chart.line.dash'] && typeof co.setLineDash === 'function') {
|
1993
|
-
co.setLineDash([1,0]);
|
1994
|
-
}
|
1995
|
-
}
|
1996
|
-
|
1997
|
-
/**
|
1998
|
-
* Set the linewidth back to 1
|
1999
|
-
*/
|
2000
|
-
co.lineWidth = 1;
|
2001
|
-
};
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
/**
|
2007
|
-
* Returns the linewidth
|
2008
|
-
*
|
2009
|
-
* @param number i The index of the "line" (/set of coordinates)
|
2010
|
-
*/
|
2011
|
-
this.getLineWidth =
|
2012
|
-
this.GetLineWidth = function (i)
|
2013
|
-
{
|
2014
|
-
var linewidth = prop['chart.line.linewidth'];
|
2015
|
-
|
2016
|
-
if (typeof linewidth == 'number') {
|
2017
|
-
return linewidth;
|
2018
|
-
|
2019
|
-
} else if (typeof linewidth == 'object') {
|
2020
|
-
if (linewidth[i]) {
|
2021
|
-
return linewidth[i];
|
2022
|
-
} else {
|
2023
|
-
return linewidth[0];
|
2024
|
-
}
|
2025
|
-
|
2026
|
-
alert('[SCATTER] Error! chart.linewidth should be a single number or an array of one or more numbers');
|
2027
|
-
}
|
2028
|
-
};
|
2029
|
-
|
2030
|
-
|
2031
|
-
|
2032
|
-
|
2033
|
-
/**
|
2034
|
-
* Draws vertical bars. Line chart doesn't use a horizontal scale, hence this function
|
2035
|
-
* is not common
|
2036
|
-
*/
|
2037
|
-
this.drawVBars =
|
2038
|
-
this.DrawVBars = function ()
|
2039
|
-
{
|
2040
|
-
|
2041
|
-
var vbars = prop['chart.background.vbars'];
|
2042
|
-
var graphWidth = ca.width - this.gutterLeft - this.gutterRight;
|
2043
|
-
|
2044
|
-
if (vbars) {
|
2045
|
-
|
2046
|
-
var xmax = prop['chart.xmax'];
|
2047
|
-
var xmin = prop['chart.xmin'];
|
2048
|
-
|
2049
|
-
for (var i=0,len=vbars.length; i<len; i+=1) {
|
2050
|
-
|
2051
|
-
var key = i;
|
2052
|
-
var value = vbars[key];
|
2053
|
-
|
2054
|
-
/**
|
2055
|
-
* Accomodate date/time values
|
2056
|
-
*/
|
2057
|
-
if (typeof value[0] == 'string') value[0] = RG.parseDate(value[0]);
|
2058
|
-
if (typeof value[1] == 'string') value[1] = RG.parseDate(value[1]) - value[0];
|
2059
|
-
|
2060
|
-
var x = (( (value[0] - xmin) / (xmax - xmin) ) * graphWidth) + this.gutterLeft;
|
2061
|
-
var width = (value[1] / (xmax - xmin) ) * graphWidth;
|
2062
|
-
|
2063
|
-
co.fillStyle = value[2];
|
2064
|
-
co.fillRect(x, this.gutterTop, width, (ca.height - this.gutterTop - this.gutterBottom));
|
2065
|
-
}
|
2066
|
-
}
|
2067
|
-
};
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
/**
|
2073
|
-
* Draws in-graph labels.
|
2074
|
-
*
|
2075
|
-
* @param object obj The graph object
|
2076
|
-
*/
|
2077
|
-
this.drawInGraphLabels =
|
2078
|
-
this.DrawInGraphLabels = function (obj)
|
2079
|
-
{
|
2080
|
-
var labels = obj.Get('chart.labels.ingraph');
|
2081
|
-
var labels_processed = [];
|
2082
|
-
|
2083
|
-
if (!labels) {
|
2084
|
-
return;
|
2085
|
-
}
|
2086
|
-
|
2087
|
-
// Defaults
|
2088
|
-
var fgcolor = 'black';
|
2089
|
-
var bgcolor = 'white';
|
2090
|
-
var direction = 1;
|
2091
|
-
|
2092
|
-
/**
|
2093
|
-
* Preprocess the labels array. Numbers are expanded
|
2094
|
-
*/
|
2095
|
-
for (var i=0,len=labels.length; i<len; i+=1) {
|
2096
|
-
if (typeof(labels[i]) == 'number') {
|
2097
|
-
for (var j=0; j<labels[i]; ++j) {
|
2098
|
-
labels_processed.push(null);
|
2099
|
-
}
|
2100
|
-
} else if (typeof(labels[i]) == 'string' || typeof(labels[i]) == 'object') {
|
2101
|
-
labels_processed.push(labels[i]);
|
2102
|
-
|
2103
|
-
} else {
|
2104
|
-
labels_processed.push('');
|
2105
|
-
}
|
2106
|
-
}
|
2107
|
-
|
2108
|
-
/**
|
2109
|
-
* Turn off any shadow
|
2110
|
-
*/
|
2111
|
-
RG.NoShadow(obj);
|
2112
|
-
|
2113
|
-
if (labels_processed && labels_processed.length > 0) {
|
2114
|
-
|
2115
|
-
var i=0;
|
2116
|
-
|
2117
|
-
for (var set=0; set<obj.coords.length; ++set) {
|
2118
|
-
for (var point = 0; point<obj.coords[set].length; ++point) {
|
2119
|
-
if (labels_processed[i]) {
|
2120
|
-
var x = obj.coords[set][point][0];
|
2121
|
-
var y = obj.coords[set][point][1];
|
2122
|
-
var length = typeof(labels_processed[i][4]) == 'number' ? labels_processed[i][4] : 25;
|
2123
|
-
|
2124
|
-
var text_x = x;
|
2125
|
-
var text_y = y - 5 - length;
|
2126
|
-
|
2127
|
-
co.moveTo(x, y - 5);
|
2128
|
-
co.lineTo(x, y - 5 - length);
|
2129
|
-
|
2130
|
-
co.stroke();
|
2131
|
-
co.beginPath();
|
2132
|
-
|
2133
|
-
// This draws the arrow
|
2134
|
-
co.moveTo(x, y - 5);
|
2135
|
-
co.lineTo(x - 3, y - 10);
|
2136
|
-
co.lineTo(x + 3, y - 10);
|
2137
|
-
co.closePath();
|
2138
|
-
|
2139
|
-
|
2140
|
-
co.beginPath();
|
2141
|
-
// Fore ground color
|
2142
|
-
co.fillStyle = (typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][1]) == 'string') ? labels_processed[i][1] : 'black';
|
2143
|
-
RG.text2(this, {
|
2144
|
-
'font':obj.Get('chart.text.font'),
|
2145
|
-
'size':obj.Get('chart.text.size'),
|
2146
|
-
'x':text_x,
|
2147
|
-
'y':text_y,
|
2148
|
-
'text':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][0]) == 'string') ? labels_processed[i][0] : labels_processed[i],
|
2149
|
-
'valign':'bottom',
|
2150
|
-
'halign':'center',
|
2151
|
-
'bounding':true,
|
2152
|
-
'bounding.fill':(typeof(labels_processed[i]) == 'object' && typeof(labels_processed[i][2]) == 'string') ? labels_processed[i][2] : 'white',
|
2153
|
-
'tag': 'labels.ingraph'
|
2154
|
-
});
|
2155
|
-
co.fill();
|
2156
|
-
}
|
2157
|
-
|
2158
|
-
i++;
|
2159
|
-
}
|
2160
|
-
}
|
2161
|
-
}
|
2162
|
-
};
|
2163
|
-
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2167
|
-
/**
|
2168
|
-
* This function makes it much easier to get the (if any) point that is currently being hovered over.
|
2169
|
-
*
|
2170
|
-
* @param object e The event object
|
2171
|
-
*/
|
2172
|
-
this.getShape =
|
2173
|
-
this.getPoint = function (e)
|
2174
|
-
{
|
2175
|
-
var mouseXY = RG.getMouseXY(e);
|
2176
|
-
var mouseX = mouseXY[0];
|
2177
|
-
var mouseY = mouseXY[1];
|
2178
|
-
var overHotspot = false;
|
2179
|
-
var offset = prop['chart.tooltips.hotspot']; // This is how far the hotspot extends
|
2180
|
-
|
2181
|
-
for (var set=0,len=this.coords.length; set<len; ++set) {
|
2182
|
-
for (var i=0,len2=this.coords[set].length; i<len2; ++i) {
|
2183
|
-
|
2184
|
-
var x = this.coords[set][i][0];
|
2185
|
-
var y = this.coords[set][i][1];
|
2186
|
-
var tooltip = this.data[set][i][3];
|
2187
|
-
|
2188
|
-
if (typeof(y) == 'number') {
|
2189
|
-
if (mouseX <= (x + offset) &&
|
2190
|
-
mouseX >= (x - offset) &&
|
2191
|
-
mouseY <= (y + offset) &&
|
2192
|
-
mouseY >= (y - offset)) {
|
2193
|
-
|
2194
|
-
var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
|
2195
|
-
var index_adjusted = i;
|
2196
|
-
|
2197
|
-
for (var ds=(set-1); ds >=0; --ds) {
|
2198
|
-
index_adjusted += this.data[ds].length;
|
2199
|
-
}
|
2200
|
-
|
2201
|
-
return {
|
2202
|
-
0: this, 1: x, 2: y, 3: set, 4: i, 5: this.data[set][i][3],
|
2203
|
-
'object': this, 'x': x, 'y': y, 'dataset': set, 'index': i, 'tooltip': tooltip, 'index_adjusted': index_adjusted
|
2204
|
-
};
|
2205
|
-
}
|
2206
|
-
} else if (RG.is_null(y)) {
|
2207
|
-
// Nothing to see here
|
2208
|
-
|
2209
|
-
} else {
|
2210
|
-
|
2211
|
-
var mark = this.data[set][i];
|
2212
|
-
|
2213
|
-
/**
|
2214
|
-
* Determine the width
|
2215
|
-
*/
|
2216
|
-
var width = prop['chart.boxplot.width'];
|
2217
|
-
|
2218
|
-
if (typeof(mark[1][7]) == 'number') {
|
2219
|
-
width = mark[1][7];
|
2220
|
-
}
|
2221
|
-
|
2222
|
-
if ( typeof(x) == 'object'
|
2223
|
-
&& mouseX > x[0]
|
2224
|
-
&& mouseX < x[1]
|
2225
|
-
&& mouseY < y[1]
|
2226
|
-
&& mouseY > y[3]
|
2227
|
-
) {
|
2228
|
-
|
2229
|
-
var tooltip = RG.parseTooltipText(this.data[set][i][3], 0);
|
2230
|
-
|
2231
|
-
return {
|
2232
|
-
0: this, 1: x[0], 2: x[1] - x[0], 3: y[1], 4: y[3] - y[1], 5: set, 6: i, 7: this.data[set][i][3],
|
2233
|
-
'object': this, 'x': x[0], 'y': y[1], 'width': x[1] - x[0], 'height': y[3] - y[1], 'dataset': set, 'index': i, 'tooltip': tooltip
|
2234
|
-
};
|
2235
|
-
}
|
2236
|
-
}
|
2237
|
-
}
|
2238
|
-
}
|
2239
|
-
};
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
/**
|
2245
|
-
* Draws the above line labels
|
2246
|
-
*/
|
2247
|
-
this.drawAboveLabels =
|
2248
|
-
this.DrawAboveLabels = function ()
|
2249
|
-
{
|
2250
|
-
var size = prop['chart.labels.above.size'];
|
2251
|
-
var font = prop['chart.text.font'];
|
2252
|
-
var units_pre = prop['chart.units.pre'];
|
2253
|
-
var units_post = prop['chart.units.post'];
|
2254
|
-
|
2255
|
-
|
2256
|
-
for (var set=0,len=this.coords.length; set<len; ++set) {
|
2257
|
-
for (var point=0,len2=this.coords[set].length; point<len2; ++point) {
|
2258
|
-
|
2259
|
-
var x_val = this.data[set][point][0];
|
2260
|
-
var y_val = this.data[set][point][1];
|
2261
|
-
|
2262
|
-
if (!RG.is_null(y_val)) {
|
2263
|
-
|
2264
|
-
// Use the top most value from a box plot
|
2265
|
-
if (RG.is_array(y_val)) {
|
2266
|
-
var max = 0;
|
2267
|
-
for (var i=0; i<y_val; ++i) {
|
2268
|
-
max = Math.max(max, y_val[i]);
|
2269
|
-
}
|
2270
|
-
|
2271
|
-
y_val = max;
|
2272
|
-
}
|
2273
|
-
|
2274
|
-
var x_pos = this.coords[set][point][0];
|
2275
|
-
var y_pos = this.coords[set][point][1];
|
2276
|
-
|
2277
|
-
RG.Text2(this, {
|
2278
|
-
'font':font,
|
2279
|
-
'size':size,
|
2280
|
-
'x':x_pos,
|
2281
|
-
'y':y_pos - 5 - size,
|
2282
|
-
'text':x_val.toFixed(prop['chart.labels.above.decimals']) + ', ' + y_val.toFixed(prop['chart.labels.above.decimals']),
|
2283
|
-
'valign':'center',
|
2284
|
-
'halign':'center',
|
2285
|
-
'bounding':true,
|
2286
|
-
'boundingFill':'rgba(255, 255, 255, 0.7)',
|
2287
|
-
'tag': 'labels.above'
|
2288
|
-
});
|
2289
|
-
}
|
2290
|
-
}
|
2291
|
-
}
|
2292
|
-
};
|
2293
|
-
|
2294
|
-
|
2295
|
-
|
2296
|
-
|
2297
|
-
/**
|
2298
|
-
* When you click on the chart, this method can return the Y value at that point. It works for any point on the
|
2299
|
-
* chart (that is inside the gutters) - not just points within the Bars.
|
2300
|
-
*
|
2301
|
-
* @param object e The event object
|
2302
|
-
*/
|
2303
|
-
this.getYValue =
|
2304
|
-
this.getValue = function (arg)
|
2305
|
-
{
|
2306
|
-
if (arg.length == 2) {
|
2307
|
-
var mouseX = arg[0];
|
2308
|
-
var mouseY = arg[1];
|
2309
|
-
} else {
|
2310
|
-
var mouseCoords = RG.getMouseXY(arg);
|
2311
|
-
var mouseX = mouseCoords[0];
|
2312
|
-
var mouseY = mouseCoords[1];
|
2313
|
-
}
|
2314
|
-
|
2315
|
-
var obj = this;
|
2316
|
-
|
2317
|
-
if ( mouseY < this.gutterTop
|
2318
|
-
|| mouseY > (ca.height - this.gutterBottom)
|
2319
|
-
|| mouseX < this.gutterLeft
|
2320
|
-
|| mouseX > (ca.width - this.gutterRight)
|
2321
|
-
) {
|
2322
|
-
return null;
|
2323
|
-
}
|
2324
|
-
|
2325
|
-
if (prop['chart.xaxispos'] == 'center') {
|
2326
|
-
var value = (((this.grapharea / 2) - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
|
2327
|
-
value *= 2;
|
2328
|
-
|
2329
|
-
|
2330
|
-
// Account for each side of the X axis
|
2331
|
-
if (value >= 0) {
|
2332
|
-
value += this.min
|
2333
|
-
|
2334
|
-
if (prop['chart.ylabels.invert']) {
|
2335
|
-
value -= this.min;
|
2336
|
-
value = this.max - value;
|
2337
|
-
}
|
2338
|
-
|
2339
|
-
} else {
|
2340
|
-
|
2341
|
-
value -= this.min;
|
2342
|
-
if (prop['chart.ylabels.invert']) {
|
2343
|
-
value += this.min;
|
2344
|
-
value = this.max + value;
|
2345
|
-
value *= -1;
|
2346
|
-
}
|
2347
|
-
}
|
2348
|
-
|
2349
|
-
} else {
|
2350
|
-
|
2351
|
-
var value = ((this.grapharea - (mouseY - this.gutterTop)) / this.grapharea) * (this.max - this.min)
|
2352
|
-
value += this.min;
|
2353
|
-
|
2354
|
-
if (prop['chart.ylabels.invert']) {
|
2355
|
-
value -= this.min;
|
2356
|
-
value = this.max - value;
|
2357
|
-
}
|
2358
|
-
}
|
2359
|
-
|
2360
|
-
return value;
|
2361
|
-
};
|
2362
|
-
|
2363
|
-
|
2364
|
-
|
2365
|
-
|
2366
|
-
/**
|
2367
|
-
* When you click on the chart, this method can return the X value at that point.
|
2368
|
-
*
|
2369
|
-
* @param mixed arg This can either be an event object or the X coordinate
|
2370
|
-
* @param number If specifying the X coord as the first arg then this should be the Y coord
|
2371
|
-
*/
|
2372
|
-
this.getXValue = function (arg)
|
2373
|
-
{
|
2374
|
-
if (arg.length == 2) {
|
2375
|
-
var mouseX = arg[0];
|
2376
|
-
var mouseY = arg[1];
|
2377
|
-
} else {
|
2378
|
-
var mouseXY = RG.getMouseXY(arg);
|
2379
|
-
var mouseX = mouseXY[0];
|
2380
|
-
var mouseY = mouseXY[1];
|
2381
|
-
}
|
2382
|
-
var obj = this;
|
2383
|
-
|
2384
|
-
if ( mouseY < this.gutterTop
|
2385
|
-
|| mouseY > (ca.height - this.gutterBottom)
|
2386
|
-
|| mouseX < this.gutterLeft
|
2387
|
-
|| mouseX > (ca.width - this.gutterRight)
|
2388
|
-
) {
|
2389
|
-
return null;
|
2390
|
-
}
|
2391
|
-
|
2392
|
-
var width = (ca.width - this.gutterLeft - this.gutterRight);
|
2393
|
-
var value = ((mouseX - this.gutterLeft) / width) * (prop['chart.xmax'] - prop['chart.xmin'])
|
2394
|
-
value += prop['chart.xmin'];
|
2395
|
-
|
2396
|
-
return value;
|
2397
|
-
};
|
2398
|
-
|
2399
|
-
|
2400
|
-
|
2401
|
-
|
2402
|
-
/**
|
2403
|
-
* Each object type has its own Highlight() function which highlights the appropriate shape
|
2404
|
-
*
|
2405
|
-
* @param object shape The shape to highlight
|
2406
|
-
*/
|
2407
|
-
this.highlight =
|
2408
|
-
this.Highlight = function (shape)
|
2409
|
-
{
|
2410
|
-
if (typeof prop['chart.highlight.style'] === 'function') {
|
2411
|
-
(prop['chart.highlight.style'])(shape);
|
2412
|
-
} else {
|
2413
|
-
// Boxplot highlight
|
2414
|
-
if (shape['height']) {
|
2415
|
-
RG.Highlight.Rect(this, shape);
|
2416
|
-
|
2417
|
-
// Point highlight
|
2418
|
-
} else {
|
2419
|
-
RG.Highlight.Point(this, shape);
|
2420
|
-
}
|
2421
|
-
}
|
2422
|
-
};
|
2423
|
-
|
2424
|
-
|
2425
|
-
|
2426
|
-
|
2427
|
-
/**
|
2428
|
-
* The getObjectByXY() worker method. Don't call this call:
|
2429
|
-
*
|
2430
|
-
* RG.ObjectRegistry.getObjectByXY(e)
|
2431
|
-
*
|
2432
|
-
* @param object e The event object
|
2433
|
-
*/
|
2434
|
-
this.getObjectByXY = function (e)
|
2435
|
-
{
|
2436
|
-
var mouseXY = RG.getMouseXY(e);
|
2437
|
-
|
2438
|
-
if (
|
2439
|
-
mouseXY[0] > (this.gutterLeft - 3)
|
2440
|
-
&& mouseXY[0] < (ca.width - this.gutterRight + 3)
|
2441
|
-
&& mouseXY[1] > (this.gutterTop - 3)
|
2442
|
-
&& mouseXY[1] < ((ca.height - this.gutterBottom) + 3)
|
2443
|
-
) {
|
2444
|
-
|
2445
|
-
return this;
|
2446
|
-
}
|
2447
|
-
};
|
2448
|
-
|
2449
|
-
|
2450
|
-
|
2451
|
-
|
2452
|
-
/**
|
2453
|
-
* This function can be used when the canvas is clicked on (or similar - depending on the event)
|
2454
|
-
* to retrieve the relevant X coordinate for a particular value.
|
2455
|
-
*
|
2456
|
-
* @param int value The value to get the X coordinate for
|
2457
|
-
*/
|
2458
|
-
this.getXCoord = function (value)
|
2459
|
-
{
|
2460
|
-
if (typeof value != 'number' && typeof value != 'string') {
|
2461
|
-
return null;
|
2462
|
-
}
|
2463
|
-
|
2464
|
-
// Allow for date strings to be passed
|
2465
|
-
if (typeof value === 'string') {
|
2466
|
-
value = RG.parseDate(value);
|
2467
|
-
}
|
2468
|
-
|
2469
|
-
var xmin = prop['chart.xmin'];
|
2470
|
-
var xmax = prop['chart.xmax'];
|
2471
|
-
var x;
|
2472
|
-
|
2473
|
-
if (value < xmin) return null;
|
2474
|
-
if (value > xmax) return null;
|
2475
|
-
|
2476
|
-
var gutterRight = this.gutterRight;
|
2477
|
-
var gutterLeft = this.gutterLeft;
|
2478
|
-
|
2479
|
-
if (prop['chart.yaxispos'] == 'right') {
|
2480
|
-
x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
|
2481
|
-
x = (ca.width - gutterRight - x);
|
2482
|
-
} else {
|
2483
|
-
x = ((value - xmin) / (xmax - xmin)) * (ca.width - gutterLeft - gutterRight);
|
2484
|
-
x = x + gutterLeft;
|
2485
|
-
}
|
2486
|
-
|
2487
|
-
return x;
|
2488
|
-
};
|
2489
|
-
|
2490
|
-
|
2491
|
-
|
2492
|
-
|
2493
|
-
/**
|
2494
|
-
* This function positions a tooltip when it is displayed
|
2495
|
-
*
|
2496
|
-
* @param obj object The chart object
|
2497
|
-
* @param int x The X coordinate specified for the tooltip
|
2498
|
-
* @param int y The Y coordinate specified for the tooltip
|
2499
|
-
* @param objec tooltip The tooltips DIV element
|
2500
|
-
*/
|
2501
|
-
this.positionTooltip = function (obj, x, y, tooltip, idx)
|
2502
|
-
{
|
2503
|
-
var shape = RG.Registry.Get('chart.tooltip.shape');
|
2504
|
-
var dataset = shape['dataset'];
|
2505
|
-
var index = shape['index'];
|
2506
|
-
var coordX = obj.coords[dataset][index][0]
|
2507
|
-
var coordY = obj.coords[dataset][index][1]
|
2508
|
-
var canvasXY = RG.getCanvasXY(obj.canvas);
|
2509
|
-
var mouseXY = RG.getMouseXY(window.event);
|
2510
|
-
var gutterLeft = obj.gutterLeft;
|
2511
|
-
var gutterTop = obj.gutterTop;
|
2512
|
-
var width = tooltip.offsetWidth;
|
2513
|
-
var height = tooltip.offsetHeight;
|
2514
|
-
tooltip.style.left = 0;
|
2515
|
-
tooltip.style.top = 0;
|
2516
|
-
|
2517
|
-
// Is the coord a boxplot
|
2518
|
-
//var isBoxplot = typeof(coordY) == 'object' ? true : false;
|
2519
|
-
|
2520
|
-
// Show any overflow (ie the arrow)
|
2521
|
-
tooltip.style.overflow = '';
|
2522
|
-
|
2523
|
-
|
2524
|
-
// Reposition the tooltip if at the edges:
|
2525
|
-
|
2526
|
-
// LEFT edge //////////////////////////////////////////////////////////////////
|
2527
|
-
if (canvasXY[0] + mouseXY[0] - (width / 2) < 0) {
|
2528
|
-
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.1) + 'px';
|
2529
|
-
tooltip.style.top = window.event.pageY - height - 5 + 'px';
|
2530
|
-
|
2531
|
-
// RIGHT edge //////////////////////////////////////////////////////////////////
|
2532
|
-
} else if ((canvasXY[0] + (coordX[0] || coordX) + (width / 2)) > doc.body.offsetWidth) {
|
2533
|
-
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width * 0.9) + 'px';
|
2534
|
-
tooltip.style.top = window.event.pageY - height - 5 + 'px';
|
2535
|
-
|
2536
|
-
// Default positioning - CENTERED //////////////////////////////////////////////////////////////////
|
2537
|
-
} else {
|
2538
|
-
tooltip.style.left = canvasXY[0] + mouseXY[0] - (width / 2) + 'px';
|
2539
|
-
tooltip.style.top = window.event.pageY - height - 5 + 'px';
|
2540
|
-
}
|
2541
|
-
};
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
|
2546
|
-
/**
|
2547
|
-
* Returns the applicable Y COORDINATE when given a Y value
|
2548
|
-
*
|
2549
|
-
* @param int value The value to use
|
2550
|
-
* @return int The appropriate Y coordinate
|
2551
|
-
*/
|
2552
|
-
this.getYCoord =
|
2553
|
-
this.getYCoordFromValue = function (value)
|
2554
|
-
{
|
2555
|
-
if (typeof(value) != 'number') {
|
2556
|
-
return null;
|
2557
|
-
}
|
2558
|
-
|
2559
|
-
var invert = prop['chart.ylabels.invert'];
|
2560
|
-
var xaxispos = prop['chart.xaxispos'];
|
2561
|
-
var graphHeight = ca.height - this.gutterTop - this.gutterBottom;
|
2562
|
-
var halfGraphHeight = graphHeight / 2;
|
2563
|
-
var ymax = this.max;
|
2564
|
-
var ymin = prop['chart.ymin'];
|
2565
|
-
var coord = 0;
|
2566
|
-
|
2567
|
-
if (value > ymax || (prop['chart.xaxispos'] == 'bottom' && value < ymin) || (prop['chart.xaxispos'] == 'center' && ((value > 0 && value < ymin) || (value < 0 && value > (-1 * ymin))))) {
|
2568
|
-
return null;
|
2569
|
-
}
|
2570
|
-
|
2571
|
-
/**
|
2572
|
-
* This calculates scale values if the X axis is in the center
|
2573
|
-
*/
|
2574
|
-
if (xaxispos == 'center') {
|
2575
|
-
|
2576
|
-
coord = ((Math.abs(value) - ymin) / (ymax - ymin)) * halfGraphHeight;
|
2577
|
-
|
2578
|
-
if (invert) {
|
2579
|
-
coord = halfGraphHeight - coord;
|
2580
|
-
}
|
2581
|
-
|
2582
|
-
if (value < 0) {
|
2583
|
-
coord += this.gutterTop;
|
2584
|
-
coord += halfGraphHeight;
|
2585
|
-
} else {
|
2586
|
-
coord = halfGraphHeight - coord;
|
2587
|
-
coord += this.gutterTop;
|
2588
|
-
}
|
2589
|
-
|
2590
|
-
/**
|
2591
|
-
* And this calculates scale values when the X axis is at the bottom
|
2592
|
-
*/
|
2593
|
-
} else {
|
2594
|
-
|
2595
|
-
coord = ((value - ymin) / (ymax - ymin)) * graphHeight;
|
2596
|
-
|
2597
|
-
if (invert) {
|
2598
|
-
coord = graphHeight - coord;
|
2599
|
-
}
|
2600
|
-
|
2601
|
-
// Invert the coordinate because the Y scale starts at the top
|
2602
|
-
coord = graphHeight - coord;
|
2603
|
-
|
2604
|
-
// And add on the top gutter
|
2605
|
-
coord = this.gutterTop + coord;
|
2606
|
-
}
|
2607
|
-
|
2608
|
-
return coord;
|
2609
|
-
};
|
2610
|
-
|
2611
|
-
|
2612
|
-
|
2613
|
-
|
2614
|
-
|
2615
|
-
|
2616
|
-
/**
|
2617
|
-
* A helper class that helps facilitatesbubble charts
|
2618
|
-
*/
|
2619
|
-
RG.Scatter.Bubble = function (scatter, min, max, width, data)
|
2620
|
-
{
|
2621
|
-
this.scatter = scatter;
|
2622
|
-
this.min = min;
|
2623
|
-
this.max = max;
|
2624
|
-
this.width = width;
|
2625
|
-
this.data = data;
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
2629
|
-
/**
|
2630
|
-
* A setter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
|
2631
|
-
*/
|
2632
|
-
this.set =
|
2633
|
-
this.Set = function (name, value)
|
2634
|
-
{
|
2635
|
-
this.scatter.Set(name, value);
|
2636
|
-
|
2637
|
-
return this;
|
2638
|
-
};
|
2639
|
-
|
2640
|
-
|
2641
|
-
|
2642
|
-
/**
|
2643
|
-
* A getter for the Bubble chart class - it just acts as a "passthru" to the Scatter object
|
2644
|
-
*/
|
2645
|
-
this.get =
|
2646
|
-
this.Get = function (name)
|
2647
|
-
{
|
2648
|
-
this.scatter.Get(name);
|
2649
|
-
};
|
2650
|
-
|
2651
|
-
|
2652
|
-
|
2653
|
-
|
2654
|
-
/**
|
2655
|
-
* Tha Bubble chart draw function
|
2656
|
-
*/
|
2657
|
-
this.draw =
|
2658
|
-
this.Draw = function ()
|
2659
|
-
{
|
2660
|
-
var bubble_min = this.min;
|
2661
|
-
var bubble_max = this.max;
|
2662
|
-
var bubble_data = this.data;
|
2663
|
-
var bubble_max_width = this.width;
|
2664
|
-
|
2665
|
-
// This custom ondraw event listener draws the bubbles
|
2666
|
-
this.scatter.ondraw = function (obj)
|
2667
|
-
{
|
2668
|
-
// Loop through all the points (first dataset)
|
2669
|
-
for (var i=0; i<obj.coords[0].length; ++i) {
|
2670
|
-
|
2671
|
-
bubble_data[i] = Math.max(bubble_data[i], bubble_min);
|
2672
|
-
bubble_data[i] = Math.min(bubble_data[i], bubble_max);
|
2673
|
-
|
2674
|
-
var r = ((bubble_data[i] - bubble_min) / (bubble_max - bubble_min) ) * bubble_max_width;
|
2675
|
-
|
2676
|
-
co.beginPath();
|
2677
|
-
co.fillStyle = RG.RadialGradient(obj,
|
2678
|
-
obj.coords[0][i][0] + (r / 2.5),
|
2679
|
-
obj.coords[0][i][1] - (r / 2.5),
|
2680
|
-
0,
|
2681
|
-
obj.coords[0][i][0] + (r / 2.5),
|
2682
|
-
obj.coords[0][i][1] - (r / 2.5),
|
2683
|
-
r,
|
2684
|
-
'white',
|
2685
|
-
obj.data[0][i][2] ? obj.data[0][i][2] : obj.properties['chart.defaultcolor']
|
2686
|
-
);
|
2687
|
-
co.arc(obj.coords[0][i][0], obj.coords[0][i][1], r, 0, RG.TWOPI, false);
|
2688
|
-
co.fill();
|
2689
|
-
}
|
2690
|
-
}
|
2691
|
-
|
2692
|
-
return this.scatter.Draw();
|
2693
|
-
};
|
2694
|
-
};
|
2695
|
-
|
2696
|
-
|
2697
|
-
|
2698
|
-
|
2699
|
-
|
2700
|
-
/**
|
2701
|
-
* This allows for easy specification of gradients
|
2702
|
-
*/
|
2703
|
-
this.parseColors = function ()
|
2704
|
-
{
|
2705
|
-
// Save the original colors so that they can be restored when the canvas is reset
|
2706
|
-
if (this.original_colors.length === 0) {
|
2707
|
-
this.original_colors['data'] = RG.array_clone(this.data);
|
2708
|
-
this.original_colors['chart.background.vbars'] = RG.array_clone(prop['chart.background.vbars']);
|
2709
|
-
this.original_colors['chart.background.hbars'] = RG.array_clone(prop['chart.background.hbars']);
|
2710
|
-
this.original_colors['chart.line.colors'] = RG.array_clone(prop['chart.line.colors']);
|
2711
|
-
this.original_colors['chart.defaultcolor'] = RG.array_clone(prop['chart.defaultcolor']);
|
2712
|
-
this.original_colors['chart.crosshairs.color'] = RG.array_clone(prop['chart.crosshairs.color']);
|
2713
|
-
this.original_colors['chart.highlight.stroke'] = RG.array_clone(prop['chart.highlight.stroke']);
|
2714
|
-
this.original_colors['chart.highlight.fill'] = RG.array_clone(prop['chart.highlight.fill']);
|
2715
|
-
this.original_colors['chart.background.barcolor1'] = RG.array_clone(prop['chart.background.barcolor1']);
|
2716
|
-
this.original_colors['chart.background.barcolor2'] = RG.array_clone(prop['chart.background.barcolor2']);
|
2717
|
-
this.original_colors['chart.background.grid.color'] = RG.array_clone(prop['chart.background.grid.color']);
|
2718
|
-
this.original_colors['chart.background.color'] = RG.array_clone(prop['chart.background.color']);
|
2719
|
-
this.original_colors['chart.axis.color'] = RG.array_clone(prop['chart.axis.color']);
|
2720
|
-
}
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
2724
|
-
|
2725
|
-
|
2726
|
-
// Colors
|
2727
|
-
var data = this.data;
|
2728
|
-
if (data) {
|
2729
|
-
for (var dataset=0; dataset<data.length; ++dataset) {
|
2730
|
-
for (var i=0; i<this.data[dataset].length; ++i) {
|
2731
|
-
|
2732
|
-
// Boxplots
|
2733
|
-
if (this.data[dataset][i] && typeof(this.data[dataset][i][1]) == 'object' && this.data[dataset][i][1]) {
|
2734
|
-
|
2735
|
-
if (typeof(this.data[dataset][i][1][5]) == 'string') this.data[dataset][i][1][5] = this.parseSingleColorForGradient(this.data[dataset][i][1][5]);
|
2736
|
-
if (typeof(this.data[dataset][i][1][6]) == 'string') this.data[dataset][i][1][6] = this.parseSingleColorForGradient(this.data[dataset][i][1][6]);
|
2737
|
-
}
|
2738
|
-
|
2739
|
-
if (!RG.isNull(this.data[dataset][i])) {
|
2740
|
-
this.data[dataset][i][2] = this.parseSingleColorForGradient(this.data[dataset][i][2]);
|
2741
|
-
}
|
2742
|
-
}
|
2743
|
-
}
|
2744
|
-
}
|
2745
|
-
|
2746
|
-
// Parse HBars
|
2747
|
-
var hbars = prop['chart.background.hbars'];
|
2748
|
-
if (hbars) {
|
2749
|
-
for (i=0; i<hbars.length; ++i) {
|
2750
|
-
hbars[i][2] = this.parseSingleColorForGradient(hbars[i][2]);
|
2751
|
-
}
|
2752
|
-
}
|
2753
|
-
|
2754
|
-
// Parse HBars
|
2755
|
-
var vbars = prop['chart.background.vbars'];
|
2756
|
-
if (vbars) {
|
2757
|
-
for (i=0; i<vbars.length; ++i) {
|
2758
|
-
vbars[i][2] = this.parseSingleColorForGradient(vbars[i][2]);
|
2759
|
-
}
|
2760
|
-
}
|
2761
|
-
|
2762
|
-
// Parse line colors
|
2763
|
-
var colors = prop['chart.line.colors'];
|
2764
|
-
if (colors) {
|
2765
|
-
for (i=0; i<colors.length; ++i) {
|
2766
|
-
colors[i] = this.parseSingleColorForGradient(colors[i]);
|
2767
|
-
}
|
2768
|
-
}
|
2769
|
-
|
2770
|
-
prop['chart.defaultcolor'] = this.parseSingleColorForGradient(prop['chart.defaultcolor']);
|
2771
|
-
prop['chart.crosshairs.color'] = this.parseSingleColorForGradient(prop['chart.crosshairs.color']);
|
2772
|
-
prop['chart.highlight.stroke'] = this.parseSingleColorForGradient(prop['chart.highlight.stroke']);
|
2773
|
-
prop['chart.highlight.fill'] = this.parseSingleColorForGradient(prop['chart.highlight.fill']);
|
2774
|
-
prop['chart.background.barcolor1'] = this.parseSingleColorForGradient(prop['chart.background.barcolor1']);
|
2775
|
-
prop['chart.background.barcolor2'] = this.parseSingleColorForGradient(prop['chart.background.barcolor2']);
|
2776
|
-
prop['chart.background.grid.color'] = this.parseSingleColorForGradient(prop['chart.background.grid.color']);
|
2777
|
-
prop['chart.background.color'] = this.parseSingleColorForGradient(prop['chart.background.color']);
|
2778
|
-
prop['chart.axis.color'] = this.parseSingleColorForGradient(prop['chart.axis.color']);
|
2779
|
-
};
|
2780
|
-
|
2781
|
-
|
2782
|
-
|
2783
|
-
|
2784
|
-
/**
|
2785
|
-
* Use this function to reset the object to the post-constructor state. Eg reset colors if
|
2786
|
-
* need be etc
|
2787
|
-
*/
|
2788
|
-
this.reset = function ()
|
2789
|
-
{
|
2790
|
-
};
|
2791
|
-
|
2792
|
-
|
2793
|
-
|
2794
|
-
|
2795
|
-
/**
|
2796
|
-
* This parses a single color value for a gradient
|
2797
|
-
*/
|
2798
|
-
this.parseSingleColorForGradient = function (color)
|
2799
|
-
{
|
2800
|
-
if (!color || typeof(color) != 'string') {
|
2801
|
-
return color;
|
2802
|
-
}
|
2803
|
-
|
2804
|
-
if (color.match(/^gradient\((.*)\)$/i)) {
|
2805
|
-
|
2806
|
-
var parts = RegExp.$1.split(':');
|
2807
|
-
|
2808
|
-
// Create the gradient
|
2809
|
-
var grad = co.createLinearGradient(0,ca.height - prop['chart.gutter.bottom'], 0, prop['chart.gutter.top']);
|
2810
|
-
|
2811
|
-
var diff = 1 / (parts.length - 1);
|
2812
|
-
|
2813
|
-
grad.addColorStop(0, RG.trim(parts[0]));
|
2814
|
-
|
2815
|
-
for (var j=1; j<parts.length; ++j) {
|
2816
|
-
grad.addColorStop(j * diff, RG.trim(parts[j]));
|
2817
|
-
}
|
2818
|
-
}
|
2819
|
-
|
2820
|
-
return grad ? grad : color;
|
2821
|
-
};
|
2822
|
-
|
2823
|
-
|
2824
|
-
|
2825
|
-
|
2826
|
-
/**
|
2827
|
-
* This function handles highlighting an entire data-series for the interactive
|
2828
|
-
* key
|
2829
|
-
*
|
2830
|
-
* @param int index The index of the data series to be highlighted
|
2831
|
-
*/
|
2832
|
-
this.interactiveKeyHighlight = function (index)
|
2833
|
-
{
|
2834
|
-
if (this.coords && this.coords[index] && this.coords[index].length) {
|
2835
|
-
this.coords[index].forEach(function (value, idx, arr)
|
2836
|
-
{
|
2837
|
-
co.beginPath();
|
2838
|
-
co.fillStyle = prop['chart.key.interactive.highlight.chart.fill'];
|
2839
|
-
co.arc(value[0], value[1], prop['chart.ticksize'] + 3, 0, RG.TWOPI, false);
|
2840
|
-
co.fill();
|
2841
|
-
});
|
2842
|
-
}
|
2843
|
-
};
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
/**
|
2849
|
-
* Using a function to add events makes it easier to facilitate method chaining
|
2850
|
-
*
|
2851
|
-
* @param string type The type of even to add
|
2852
|
-
* @param function func
|
2853
|
-
*/
|
2854
|
-
this.on = function (type, func)
|
2855
|
-
{
|
2856
|
-
if (type.substr(0,2) !== 'on') {
|
2857
|
-
type = 'on' + type;
|
2858
|
-
}
|
2859
|
-
|
2860
|
-
this[type] = func;
|
2861
|
-
|
2862
|
-
return this;
|
2863
|
-
};
|
2864
|
-
|
2865
|
-
|
2866
|
-
|
2867
|
-
|
2868
|
-
/**
|
2869
|
-
* This function runs once only
|
2870
|
-
* (put at the end of the file (before any effects))
|
2871
|
-
*/
|
2872
|
-
this.firstDrawFunc = function ()
|
2873
|
-
{
|
2874
|
-
};
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
2878
|
-
|
2879
|
-
/**
|
2880
|
-
* Trace2
|
2881
|
-
*
|
2882
|
-
* This is a new version of the Trace effect which no longer requires jQuery and is more compatible
|
2883
|
-
* with other effects (eg Expand). This new effect is considerably simpler and less code.
|
2884
|
-
*
|
2885
|
-
* @param object Options for the effect. Currently only "frames" is available.
|
2886
|
-
* @param int A function that is called when the ffect is complete
|
2887
|
-
*/
|
2888
|
-
this.trace =
|
2889
|
-
this.trace2 = function ()
|
2890
|
-
{
|
2891
|
-
var obj = this,
|
2892
|
-
callback = arguments[2],
|
2893
|
-
opt = arguments[0] || {},
|
2894
|
-
frames = opt.frames || 30,
|
2895
|
-
frame = 0,
|
2896
|
-
callback = arguments[1] || function () {}
|
2897
|
-
|
2898
|
-
obj.Set('animationTrace', true);
|
2899
|
-
obj.Set('animationTraceClip', 0);
|
2900
|
-
|
2901
|
-
function iterator ()
|
2902
|
-
{
|
2903
|
-
RG.clear(obj.canvas);
|
2904
|
-
|
2905
|
-
RG.redrawCanvas(obj.canvas);
|
2906
|
-
|
2907
|
-
if (frame++ < frames) {
|
2908
|
-
obj.set('animationTraceClip', frame / frames);
|
2909
|
-
RG.Effects.updateCanvas(iterator);
|
2910
|
-
} else {
|
2911
|
-
callback(obj);
|
2912
|
-
}
|
2913
|
-
}
|
2914
|
-
|
2915
|
-
iterator();
|
2916
|
-
|
2917
|
-
return this;
|
2918
|
-
};
|
2919
|
-
|
2920
|
-
|
2921
|
-
|
2922
|
-
|
2923
|
-
/**
|
2924
|
-
* This helps the Gantt reset colors when the reset function is called.
|
2925
|
-
* It handles going through the data and resetting the colors.
|
2926
|
-
*/
|
2927
|
-
this.resetColorsToOriginalValues = function ()
|
2928
|
-
{
|
2929
|
-
/**
|
2930
|
-
* Copy the original colors over for single-event-per-line data
|
2931
|
-
*/
|
2932
|
-
for (var i=0,len=this.original_colors['data'].length; i<len; ++i) {
|
2933
|
-
for (var j=0,len2=this.original_colors['data'][i].length; j<len2;++j) {
|
2934
|
-
|
2935
|
-
// The color for the point
|
2936
|
-
this.data[i][j][2] = RG.array_clone(this.original_colors['data'][i][j][2]);
|
2937
|
-
|
2938
|
-
// Handle boxplots
|
2939
|
-
if (typeof this.data[i][j][1] === 'object') {
|
2940
|
-
this.data[i][j][1][5] = RG.array_clone(this.original_colors['data'][i][j][1][5]);
|
2941
|
-
this.data[i][j][1][6] = RG.array_clone(this.original_colors['data'][i][j][1][6]);
|
2942
|
-
}
|
2943
|
-
}
|
2944
|
-
}
|
2945
|
-
};
|
2946
|
-
|
2947
|
-
|
2948
|
-
|
2949
|
-
|
2950
|
-
RG.att(ca);
|
2951
|
-
|
2952
|
-
|
2953
|
-
|
2954
|
-
|
2955
|
-
/**
|
2956
|
-
* Register the object
|
2957
|
-
*/
|
2958
|
-
RG.Register(this);
|
2959
|
-
|
2960
|
-
|
2961
|
-
|
2962
|
-
|
2963
|
-
/**
|
2964
|
-
* This is the 'end' of the constructor so if the first argument
|
2965
|
-
* contains configuration data - handle that.
|
2966
|
-
*/
|
2967
|
-
if (parseConfObjectForOptions) {
|
2968
|
-
RG.parseObjectStyleConfig(this, conf.options);
|
2969
|
-
}
|
2970
|
-
};
|