rgraph-rails 1.0.1
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/rgraph-rails/version.rb +3 -0
- data/lib/rgraph-rails.rb +8 -0
- data/license.txt +19 -0
- data/rgraph-rails.gemspec +26 -0
- 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.bar.js +3246 -0
- data/vendor/assets/javascripts/RGraph.bipolar.js +2003 -0
- data/vendor/assets/javascripts/RGraph.common.annotate.js +399 -0
- data/vendor/assets/javascripts/RGraph.common.context.js +600 -0
- data/vendor/assets/javascripts/RGraph.common.core.js +4751 -0
- data/vendor/assets/javascripts/RGraph.common.csv.js +275 -0
- data/vendor/assets/javascripts/RGraph.common.deprecated.js +454 -0
- data/vendor/assets/javascripts/RGraph.common.dynamic.js +1194 -0
- data/vendor/assets/javascripts/RGraph.common.effects.js +1524 -0
- data/vendor/assets/javascripts/RGraph.common.key.js +735 -0
- data/vendor/assets/javascripts/RGraph.common.resizing.js +550 -0
- data/vendor/assets/javascripts/RGraph.common.tooltips.js +605 -0
- data/vendor/assets/javascripts/RGraph.common.zoom.js +223 -0
- data/vendor/assets/javascripts/RGraph.drawing.background.js +636 -0
- data/vendor/assets/javascripts/RGraph.drawing.circle.js +579 -0
- data/vendor/assets/javascripts/RGraph.drawing.image.js +810 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker1.js +710 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker2.js +672 -0
- data/vendor/assets/javascripts/RGraph.drawing.marker3.js +568 -0
- data/vendor/assets/javascripts/RGraph.drawing.poly.js +623 -0
- data/vendor/assets/javascripts/RGraph.drawing.rect.js +603 -0
- data/vendor/assets/javascripts/RGraph.drawing.text.js +648 -0
- data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +815 -0
- data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +860 -0
- data/vendor/assets/javascripts/RGraph.fuel.js +965 -0
- data/vendor/assets/javascripts/RGraph.funnel.js +988 -0
- data/vendor/assets/javascripts/RGraph.gantt.js +1242 -0
- data/vendor/assets/javascripts/RGraph.gauge.js +1391 -0
- data/vendor/assets/javascripts/RGraph.hbar.js +1794 -0
- data/vendor/assets/javascripts/RGraph.hprogress.js +1307 -0
- data/vendor/assets/javascripts/RGraph.line.js +3940 -0
- data/vendor/assets/javascripts/RGraph.meter.js +1242 -0
- data/vendor/assets/javascripts/RGraph.modaldialog.js +292 -0
- data/vendor/assets/javascripts/RGraph.odo.js +1265 -0
- data/vendor/assets/javascripts/RGraph.pie.js +1979 -0
- data/vendor/assets/javascripts/RGraph.radar.js +1840 -0
- data/vendor/assets/javascripts/RGraph.rose.js +1860 -0
- data/vendor/assets/javascripts/RGraph.rscatter.js +1332 -0
- data/vendor/assets/javascripts/RGraph.scatter.js +3029 -0
- data/vendor/assets/javascripts/RGraph.thermometer.js +1131 -0
- data/vendor/assets/javascripts/RGraph.vprogress.js +1326 -0
- data/vendor/assets/javascripts/RGraph.waterfall.js +1252 -0
- data/vendor/assets/javascripts/financial-data.js +1067 -0
- data/vendor/assets/stylesheets/ModalDialog.css +90 -0
- data/vendor/assets/stylesheets/animations.css +3347 -0
- data/vendor/assets/stylesheets/website.css +402 -0
- metadata +175 -0
|
@@ -0,0 +1,4751 @@
|
|
|
1
|
+
// version: 2015-11-02
|
|
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
|
+
// Module pattern
|
|
19
|
+
(function (win, doc, undefined)
|
|
20
|
+
{
|
|
21
|
+
var RG = RGraph,
|
|
22
|
+
ua = navigator.userAgent,
|
|
23
|
+
ma = Math;
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialise the various objects
|
|
30
|
+
*/
|
|
31
|
+
RG.Highlight = {};
|
|
32
|
+
RG.Registry = {};
|
|
33
|
+
RG.Registry.store = [];
|
|
34
|
+
RG.Registry.store['chart.event.handlers'] = [];
|
|
35
|
+
RG.Registry.store['__rgraph_event_listeners__'] = []; // Used in the new system for tooltips
|
|
36
|
+
RG.Background = {};
|
|
37
|
+
RG.background = {};
|
|
38
|
+
RG.objects = [];
|
|
39
|
+
RG.Resizing = {};
|
|
40
|
+
RG.events = [];
|
|
41
|
+
RG.cursor = [];
|
|
42
|
+
RG.Effects = RG.Effects || {};
|
|
43
|
+
RG.cache = [];
|
|
44
|
+
|
|
45
|
+
RG.ObjectRegistry = {};
|
|
46
|
+
RG.ObjectRegistry.objects = {};
|
|
47
|
+
RG.ObjectRegistry.objects.byUID = [];
|
|
48
|
+
RG.ObjectRegistry.objects.byCanvasID = [];
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Some "constants". The ua variable is navigator.userAgent (definedabove)
|
|
55
|
+
*/
|
|
56
|
+
RG.PI = ma.PI;
|
|
57
|
+
RG.HALFPI = RG.PI / 2;
|
|
58
|
+
RG.TWOPI = RG.PI * 2;
|
|
59
|
+
|
|
60
|
+
RG.ISFF = ua.indexOf('Firefox') != -1;
|
|
61
|
+
RG.ISOPERA = ua.indexOf('Opera') != -1;
|
|
62
|
+
RG.ISCHROME = ua.indexOf('Chrome') != -1;
|
|
63
|
+
RG.ISSAFARI = ua.indexOf('Safari') != -1 && !RG.ISCHROME;
|
|
64
|
+
RG.ISWEBKIT = ua.indexOf('WebKit') != -1;
|
|
65
|
+
|
|
66
|
+
RG.ISIE = ua.indexOf('Trident') > 0 || navigator.userAgent.indexOf('MSIE') > 0;
|
|
67
|
+
RG.ISIE6 = ua.indexOf('MSIE 6') > 0;
|
|
68
|
+
RG.ISIE7 = ua.indexOf('MSIE 7') > 0;
|
|
69
|
+
RG.ISIE8 = ua.indexOf('MSIE 8') > 0;
|
|
70
|
+
RG.ISIE9 = ua.indexOf('MSIE 9') > 0;
|
|
71
|
+
RG.ISIE10 = ua.indexOf('MSIE 10') > 0;
|
|
72
|
+
RG.ISOLD = RGraph.ISIE6 || RGraph.ISIE7 || RGraph.ISIE8; // MUST be here
|
|
73
|
+
|
|
74
|
+
RG.ISIE11UP = ua.indexOf('MSIE') == -1 && ua.indexOf('Trident') > 0;
|
|
75
|
+
RG.ISIE10UP = RG.ISIE10 || RG.ISIE11UP;
|
|
76
|
+
RG.ISIE9UP = RG.ISIE9 || RG.ISIE10UP;
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Returns five values which are used as a nice scale
|
|
83
|
+
*
|
|
84
|
+
* @param max int The maximum value of the graph
|
|
85
|
+
* @param obj object The graph object
|
|
86
|
+
* @return array An appropriate scale
|
|
87
|
+
*/
|
|
88
|
+
RG.getScale = function (max, obj)
|
|
89
|
+
{
|
|
90
|
+
/**
|
|
91
|
+
* Special case for 0
|
|
92
|
+
*/
|
|
93
|
+
if (max == 0) {
|
|
94
|
+
return ['0.2', '0.4', '0.6', '0.8', '1.0'];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var original_max = max;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Manually do decimals
|
|
101
|
+
*/
|
|
102
|
+
if (max <= 1) {
|
|
103
|
+
if (max > 0.5) {
|
|
104
|
+
return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)];
|
|
105
|
+
|
|
106
|
+
} else if (max >= 0.1) {
|
|
107
|
+
return obj.Get('chart.scale.round') ? [0.2,0.4,0.6,0.8,1] : [0.1,0.2,0.3,0.4,0.5];
|
|
108
|
+
|
|
109
|
+
} else {
|
|
110
|
+
|
|
111
|
+
var tmp = max;
|
|
112
|
+
var exp = 0;
|
|
113
|
+
|
|
114
|
+
while (tmp < 1.01) {
|
|
115
|
+
exp += 1;
|
|
116
|
+
tmp *= 10;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp];
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if (max <= ('5e-' + exp)) {
|
|
123
|
+
ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return ret;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Take off any decimals
|
|
131
|
+
if (String(max).indexOf('.') > 0) {
|
|
132
|
+
max = String(max).replace(/\.\d+$/, '');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var interval = ma.pow(10, Number(String(Number(max)).length - 1));
|
|
136
|
+
var topValue = interval;
|
|
137
|
+
|
|
138
|
+
while (topValue < max) {
|
|
139
|
+
topValue += (interval / 2);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Handles cases where the max is (for example) 50.5
|
|
143
|
+
if (Number(original_max) > Number(topValue)) {
|
|
144
|
+
topValue += (interval / 2);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Custom if the max is greater than 5 and less than 10
|
|
148
|
+
if (max < 10) {
|
|
149
|
+
topValue = (Number(original_max) <= 5 ? 5 : 10);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Added 02/11/2010 to create "nicer" scales
|
|
154
|
+
*/
|
|
155
|
+
if (obj && typeof(obj.Get('chart.scale.round')) == 'boolean' && obj.Get('chart.scale.round')) {
|
|
156
|
+
topValue = 10 * interval;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return [topValue * 0.2, topValue * 0.4, topValue * 0.6, topValue * 0.8, topValue];
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Returns an appropriate scale. The return value is actualy an object consisting of:
|
|
167
|
+
* scale.max
|
|
168
|
+
* scale.min
|
|
169
|
+
* scale.scale
|
|
170
|
+
*
|
|
171
|
+
* @param obj object The graph object
|
|
172
|
+
* @param prop object An object consisting of configuration properties
|
|
173
|
+
* @return object An object containg scale information
|
|
174
|
+
*/
|
|
175
|
+
RG.getScale2 = function (obj, opt)
|
|
176
|
+
{
|
|
177
|
+
var ca = obj.canvas,
|
|
178
|
+
co = obj.context,
|
|
179
|
+
prop = obj.properties,
|
|
180
|
+
numlabels = typeof opt['ylabels.count'] == 'number' ? opt['ylabels.count'] : 5,
|
|
181
|
+
units_pre = typeof opt['units.pre'] == 'string' ? opt['units.pre'] : '',
|
|
182
|
+
units_post = typeof opt['units.post'] == 'string' ? opt['units.post'] : '',
|
|
183
|
+
max = Number(opt['max']),
|
|
184
|
+
min = typeof opt['min'] == 'number' ? opt['min'] : 0,
|
|
185
|
+
strict = opt['strict'],
|
|
186
|
+
decimals = Number(opt['scale.decimals']), // Sometimes the default is null
|
|
187
|
+
point = opt['scale.point'], // Default is a string in all chart libraries so no need to cast it
|
|
188
|
+
thousand = opt['scale.thousand'], // Default is a string in all chart libraries so no need to cast it
|
|
189
|
+
original_max = max,
|
|
190
|
+
round = opt['scale.round'],
|
|
191
|
+
scale = {'max':1,'labels':[]}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Special case for 0
|
|
197
|
+
*
|
|
198
|
+
* ** Must be first **
|
|
199
|
+
*/
|
|
200
|
+
if (!max) {
|
|
201
|
+
|
|
202
|
+
var max = 1;
|
|
203
|
+
|
|
204
|
+
var scale = {max:1,min:0,labels:[]};
|
|
205
|
+
|
|
206
|
+
for (var i=0; i<numlabels; ++i) {
|
|
207
|
+
var label = ((((max - min) / numlabels) + min) * (i + 1)).toFixed(decimals);
|
|
208
|
+
scale.labels.push(units_pre + label + units_post);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Manually do decimals
|
|
213
|
+
*/
|
|
214
|
+
} else if (max <= 1 && !strict) {
|
|
215
|
+
|
|
216
|
+
if (max > 0.5) {
|
|
217
|
+
|
|
218
|
+
max = 1;
|
|
219
|
+
min = min;
|
|
220
|
+
scale.min = min;
|
|
221
|
+
|
|
222
|
+
for (var i=0; i<numlabels; ++i) {
|
|
223
|
+
var label = ((((max - min) / numlabels) * (i + 1)) + min).toFixed(decimals);
|
|
224
|
+
|
|
225
|
+
scale.labels.push(units_pre + label + units_post);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
} else if (max >= 0.1) {
|
|
229
|
+
|
|
230
|
+
max = 0.5;
|
|
231
|
+
min = min;
|
|
232
|
+
scale = {'max': 0.5, 'min':min,'labels':[]}
|
|
233
|
+
|
|
234
|
+
for (var i=0; i<numlabels; ++i) {
|
|
235
|
+
var label = ((((max - min) / numlabels) + min) * (i + 1)).toFixed(decimals);
|
|
236
|
+
scale.labels.push(units_pre + label + units_post);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
} else {
|
|
240
|
+
|
|
241
|
+
scale = {'min':min,'labels':[]}
|
|
242
|
+
var max_str = String(max);
|
|
243
|
+
|
|
244
|
+
if (max_str.indexOf('e') > 0) {
|
|
245
|
+
var numdecimals = ma.abs(max_str.substring(max_str.indexOf('e') + 1));
|
|
246
|
+
} else {
|
|
247
|
+
var numdecimals = String(max).length - 2;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var max = 1 / ma.pow(10,numdecimals - 1);
|
|
251
|
+
|
|
252
|
+
for (var i=0; i<numlabels; ++i) {
|
|
253
|
+
var label = ((((max - min) / numlabels) + min) * (i + 1));
|
|
254
|
+
label = label.toExponential();
|
|
255
|
+
label = label.split(/e/);
|
|
256
|
+
label[0] = ma.round(label[0]);
|
|
257
|
+
label = label.join('e');
|
|
258
|
+
|
|
259
|
+
scale.labels.push(label);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
//This makes the top scale value of the format 10e-2 instead of 1e-1
|
|
263
|
+
tmp = scale.labels[scale.labels.length - 1].split(/e/);
|
|
264
|
+
tmp[0] += 0;
|
|
265
|
+
tmp[1] = Number(tmp[1]) - 1;
|
|
266
|
+
tmp = tmp[0] + 'e' + tmp[1];
|
|
267
|
+
scale.labels[scale.labels.length - 1] = tmp;
|
|
268
|
+
|
|
269
|
+
// Add the units
|
|
270
|
+
for (var i=0; i<scale.labels.length ; ++i) {
|
|
271
|
+
scale.labels[i] = units_pre + scale.labels[i] + units_post;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
scale.max = Number(max);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
} else if (!strict) {
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Now comes the scale handling for integer values
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
// This accomodates decimals by rounding the max up to the next integer
|
|
286
|
+
max = ma.ceil(max);
|
|
287
|
+
|
|
288
|
+
var interval = ma.pow(10, ma.max(1, Number(String(Number(max) - Number(min)).length - 1)) );
|
|
289
|
+
|
|
290
|
+
var topValue = interval;
|
|
291
|
+
|
|
292
|
+
while (topValue < max) {
|
|
293
|
+
topValue += (interval / 2);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Handles cases where the max is (for example) 50.5
|
|
297
|
+
if (Number(original_max) > Number(topValue)) {
|
|
298
|
+
topValue += (interval / 2);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Custom if the max is greater than 5 and less than 10
|
|
302
|
+
if (max <= 10) {
|
|
303
|
+
topValue = (Number(original_max) <= 5 ? 5 : 10);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
// Added 02/11/2010 to create "nicer" scales
|
|
308
|
+
if (obj && typeof(round) == 'boolean' && round) {
|
|
309
|
+
topValue = 10 * interval;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
scale.max = topValue;
|
|
313
|
+
|
|
314
|
+
// Now generate the scale. Temporarily set the objects chart.scale.decimal and chart.scale.point to those
|
|
315
|
+
//that we've been given as the number_format functuion looks at those instead of using argumrnts.
|
|
316
|
+
var tmp_point = prop['chart.scale.point'];
|
|
317
|
+
var tmp_thousand = prop['chart.scale.thousand'];
|
|
318
|
+
|
|
319
|
+
obj.Set('chart.scale.thousand', thousand);
|
|
320
|
+
obj.Set('chart.scale.point', point);
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
for (var i=0; i<numlabels; ++i) {
|
|
324
|
+
scale.labels.push( RG.number_format(obj, ((((i+1) / numlabels) * (topValue - min)) + min).toFixed(decimals), units_pre, units_post) );
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
obj.Set('chart.scale.thousand', tmp_thousand);
|
|
328
|
+
obj.Set('chart.scale.point', tmp_point);
|
|
329
|
+
|
|
330
|
+
} else if (typeof(max) == 'number' && strict) {
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* ymax is set and also strict
|
|
334
|
+
*/
|
|
335
|
+
for (var i=0; i<numlabels; ++i) {
|
|
336
|
+
scale.labels.push( RG.number_format(obj, ((((i+1) / numlabels) * (max - min)) + min).toFixed(decimals), units_pre, units_post) );
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ???
|
|
340
|
+
scale.max = max;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
scale.units_pre = units_pre;
|
|
345
|
+
scale.units_post = units_post;
|
|
346
|
+
scale.point = point;
|
|
347
|
+
scale.decimals = decimals;
|
|
348
|
+
scale.thousand = thousand;
|
|
349
|
+
scale.numlabels = numlabels;
|
|
350
|
+
scale.round = Boolean(round);
|
|
351
|
+
scale.min = min;
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
return scale;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Makes a clone of an object
|
|
362
|
+
*
|
|
363
|
+
* @param obj val The object to clone
|
|
364
|
+
*/
|
|
365
|
+
RG.arrayClone =
|
|
366
|
+
RG.array_clone = function (obj)
|
|
367
|
+
{
|
|
368
|
+
if(obj === null || typeof obj !== 'object') {
|
|
369
|
+
return obj;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var temp = [];
|
|
373
|
+
|
|
374
|
+
for (var i=0,len=obj.length;i<len; ++i) {
|
|
375
|
+
|
|
376
|
+
if (typeof obj[i] === 'number') {
|
|
377
|
+
temp[i] = (function (arg) {return Number(arg);})(obj[i]);
|
|
378
|
+
|
|
379
|
+
} else if (typeof obj[i] === 'string') {
|
|
380
|
+
temp[i] = (function (arg) {return String(arg);})(obj[i]);
|
|
381
|
+
|
|
382
|
+
} else if (typeof obj[i] === 'function') {
|
|
383
|
+
temp[i] = obj[i];
|
|
384
|
+
|
|
385
|
+
} else {
|
|
386
|
+
temp[i] = RG.array_clone(obj[i]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return temp;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Returns the maximum numeric value which is in an array
|
|
398
|
+
*
|
|
399
|
+
* @param array arr The array (can also be a number, in which case it's returned as-is)
|
|
400
|
+
* @param int Whether to ignore signs (ie negative/positive)
|
|
401
|
+
* @return int The maximum value in the array
|
|
402
|
+
*/
|
|
403
|
+
RG.arrayMax =
|
|
404
|
+
RG.array_max = function (arr)
|
|
405
|
+
{
|
|
406
|
+
var max = null,
|
|
407
|
+
ma = Math
|
|
408
|
+
|
|
409
|
+
if (typeof arr === 'number') {
|
|
410
|
+
return arr;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (RG.isNull(arr)) {
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (var i=0,len=arr.length; i<len; ++i) {
|
|
418
|
+
if (typeof arr[i] === 'number') {
|
|
419
|
+
|
|
420
|
+
var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
|
|
421
|
+
|
|
422
|
+
if (typeof max === 'number') {
|
|
423
|
+
max = ma.max(max, val);
|
|
424
|
+
} else {
|
|
425
|
+
max = val;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return max;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Returns the minimum numeric value which is in an array
|
|
438
|
+
*
|
|
439
|
+
* @param array arr The array (can also be a number, in which case it's returned as-is)
|
|
440
|
+
* @param int Whether to ignore signs (ie negative/positive)
|
|
441
|
+
* @return int The minimum value in the array
|
|
442
|
+
*/
|
|
443
|
+
RG.arrayMin = function (arr)
|
|
444
|
+
{
|
|
445
|
+
var max = null,
|
|
446
|
+
ma = Math;
|
|
447
|
+
|
|
448
|
+
if (typeof arr === 'number') {
|
|
449
|
+
return arr;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (RG.isNull(arr)) {
|
|
453
|
+
return 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
for (var i=0,len=arr.length; i<len; ++i) {
|
|
457
|
+
if (typeof arr[i] === 'number') {
|
|
458
|
+
|
|
459
|
+
var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
|
|
460
|
+
|
|
461
|
+
if (typeof min === 'number') {
|
|
462
|
+
min = ma.min(min, val);
|
|
463
|
+
} else {
|
|
464
|
+
min = val;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return min;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Returns the maximum value which is in an array
|
|
477
|
+
*
|
|
478
|
+
* @param array arr The array
|
|
479
|
+
* @param int len The length to pad the array to
|
|
480
|
+
* @param mixed The value to use to pad the array (optional)
|
|
481
|
+
*/
|
|
482
|
+
RG.arrayPad =
|
|
483
|
+
RG.array_pad = function (arr, len)
|
|
484
|
+
{
|
|
485
|
+
if (arr.length < len) {
|
|
486
|
+
var val = arguments[2] ? arguments[2] : null;
|
|
487
|
+
|
|
488
|
+
for (var i=arr.length; i<len; i+=1) {
|
|
489
|
+
arr[i] = val;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return arr;
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* An array sum function
|
|
501
|
+
*
|
|
502
|
+
* @param array arr The array to calculate the total of
|
|
503
|
+
* @return int The summed total of the arrays elements
|
|
504
|
+
*/
|
|
505
|
+
RG.arraySum =
|
|
506
|
+
RG.array_sum = function (arr)
|
|
507
|
+
{
|
|
508
|
+
// Allow integers
|
|
509
|
+
if (typeof arr === 'number') {
|
|
510
|
+
return arr;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Account for null
|
|
514
|
+
if (RG.is_null(arr)) {
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
var i, sum, len = arr.length;
|
|
519
|
+
|
|
520
|
+
for(i=0,sum=0;i<len;sum+=arr[i++]);
|
|
521
|
+
|
|
522
|
+
return sum;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Takes any number of arguments and adds them to one big linear array
|
|
530
|
+
* which is then returned
|
|
531
|
+
*
|
|
532
|
+
* @param ... mixed The data to linearise. You can strings, booleans, numbers or arrays
|
|
533
|
+
*/
|
|
534
|
+
RG.arrayLinearize =
|
|
535
|
+
RG.array_linearize = function ()
|
|
536
|
+
{
|
|
537
|
+
var arr = [],
|
|
538
|
+
args = arguments
|
|
539
|
+
|
|
540
|
+
for (var i=0,len=args.length; i<len; ++i) {
|
|
541
|
+
|
|
542
|
+
if (typeof args[i] === 'object' && args[i]) {
|
|
543
|
+
for (var j=0,len2=args[i].length; j<len2; ++j) {
|
|
544
|
+
var sub = RG.array_linearize(args[i][j]);
|
|
545
|
+
|
|
546
|
+
for (var k=0,len3=sub.length; k<len3; ++k) {
|
|
547
|
+
arr.push(sub[k]);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
} else {
|
|
551
|
+
arr.push(args[i]);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return arr;
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Takes one off the front of the given array and returns the new array.
|
|
563
|
+
*
|
|
564
|
+
* @param array arr The array from which to take one off the front of array
|
|
565
|
+
*
|
|
566
|
+
* @return array The new array
|
|
567
|
+
*/
|
|
568
|
+
RG.arrayShift =
|
|
569
|
+
RG.array_shift = function(arr)
|
|
570
|
+
{
|
|
571
|
+
var ret = [];
|
|
572
|
+
|
|
573
|
+
for(var i=1,len=arr.length; i<len; ++i) {
|
|
574
|
+
ret.push(arr[i]);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return ret;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Reverses the order of an array
|
|
585
|
+
*
|
|
586
|
+
* @param array arr The array to reverse
|
|
587
|
+
*/
|
|
588
|
+
RG.arrayReverse =
|
|
589
|
+
RG.array_reverse = function (arr)
|
|
590
|
+
{
|
|
591
|
+
var newarr=[];
|
|
592
|
+
|
|
593
|
+
for(var i=arr.length - 1; i>=0; i-=1) {
|
|
594
|
+
newarr.push(arr[i]);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return newarr;
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Clears the canvas by setting the width. You can specify a colour if you wish.
|
|
605
|
+
*
|
|
606
|
+
* @param object canvas The canvas to clear
|
|
607
|
+
* @param mixed Usually a color string to use to clear the canvas
|
|
608
|
+
* with - could also be a gradient object
|
|
609
|
+
*/
|
|
610
|
+
RG.clear =
|
|
611
|
+
RG.Clear = function (ca)
|
|
612
|
+
{
|
|
613
|
+
var obj = ca.__object__,
|
|
614
|
+
co = ca.getContext('2d'),
|
|
615
|
+
color = arguments[1] || (obj && obj.get('clearto'))
|
|
616
|
+
|
|
617
|
+
if (!ca) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
RG.FireCustomEvent(obj, 'onbeforeclear');
|
|
622
|
+
|
|
623
|
+
if (RG.ISIE8 && !color) {
|
|
624
|
+
color = 'white';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Can now clear the canvas back to fully transparent
|
|
629
|
+
*/
|
|
630
|
+
if (!color || (color && color === 'rgba(0,0,0,0)' || color === 'transparent')) {
|
|
631
|
+
|
|
632
|
+
co.clearRect(0,0,ca.width, ca.height);
|
|
633
|
+
|
|
634
|
+
// Reset the globalCompositeOperation
|
|
635
|
+
co.globalCompositeOperation = 'source-over';
|
|
636
|
+
|
|
637
|
+
} else {
|
|
638
|
+
|
|
639
|
+
co.fillStyle = color;
|
|
640
|
+
co.beginPath();
|
|
641
|
+
|
|
642
|
+
if (RG.ISIE8) {
|
|
643
|
+
co.fillRect(0,0,ca.width,ca.height);
|
|
644
|
+
} else {
|
|
645
|
+
co.fillRect(-10,-10,ca.width + 20,ca.height + 20);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
co.fill();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
//if (RG.ClearAnnotations) {
|
|
652
|
+
//RG.ClearAnnotations(ca.id);
|
|
653
|
+
//}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* This removes any background image that may be present
|
|
657
|
+
*/
|
|
658
|
+
if (RG.Registry.Get('chart.background.image.' + ca.id)) {
|
|
659
|
+
var img = RG.Registry.Get('chart.background.image.' + ca.id);
|
|
660
|
+
img.style.position = 'absolute';
|
|
661
|
+
img.style.left = '-10000px';
|
|
662
|
+
img.style.top = '-10000px';
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* This hides the tooltip that is showing IF it has the same canvas ID as
|
|
667
|
+
* that which is being cleared
|
|
668
|
+
*/
|
|
669
|
+
if (RG.Registry.Get('chart.tooltip') && obj.get('chart.tooltips.nohideonclear') !== true) {
|
|
670
|
+
RG.HideTooltip(ca);
|
|
671
|
+
//RG.Redraw();
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
//
|
|
677
|
+
// Hide all DOM text by positioning it outside the canvas
|
|
678
|
+
//
|
|
679
|
+
//for (i in RG.cache) {
|
|
680
|
+
// if (typeof i === 'string' && i.indexOf('-text-') > 0) {
|
|
681
|
+
// RG.cache[i].style.left = '-100px';
|
|
682
|
+
// RG.cache[i].style.top = '-100px';
|
|
683
|
+
// }
|
|
684
|
+
//}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Set the cursor to default
|
|
688
|
+
*/
|
|
689
|
+
ca.style.cursor = 'default';
|
|
690
|
+
|
|
691
|
+
RG.FireCustomEvent(obj, 'onclear');
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Draws the title of the graph
|
|
699
|
+
*
|
|
700
|
+
* @param object canvas The canvas object
|
|
701
|
+
* @param string text The title to write
|
|
702
|
+
* @param integer gutter The size of the gutter
|
|
703
|
+
* @param integer The center X point (optional - if not given it will be generated from the canvas width)
|
|
704
|
+
* @param integer Size of the text. If not given it will be 14
|
|
705
|
+
* @param object An optional object which has canvas and context properties to use instead of those on
|
|
706
|
+
* the obj argument (so as to enable caching)
|
|
707
|
+
*/
|
|
708
|
+
RG.drawTitle =
|
|
709
|
+
RG.DrawTitle = function (obj, text, gutterTop)
|
|
710
|
+
{
|
|
711
|
+
var ca = canvas = obj.canvas,
|
|
712
|
+
co = context = obj.context,
|
|
713
|
+
prop = obj.properties,
|
|
714
|
+
gutterLeft = prop['chart.gutter.left'],
|
|
715
|
+
gutterRight = prop['chart.gutter.right'],
|
|
716
|
+
gutterTop = gutterTop,
|
|
717
|
+
gutterBottom = prop['chart.gutter.bottom'],
|
|
718
|
+
size = arguments[4] ? arguments[4] : 12,
|
|
719
|
+
bold = prop['chart.title.bold'],
|
|
720
|
+
centerx = (arguments[3] ? arguments[3] : ((ca.width - gutterLeft - gutterRight) / 2) + gutterLeft),
|
|
721
|
+
keypos = prop['chart.key.position'],
|
|
722
|
+
vpos = prop['chart.title.vpos'],
|
|
723
|
+
hpos = prop['chart.title.hpos'],
|
|
724
|
+
bgcolor = prop['chart.title.background'],
|
|
725
|
+
x = prop['chart.title.x'],
|
|
726
|
+
y = prop['chart.title.y'],
|
|
727
|
+
halign = 'center',
|
|
728
|
+
valign = 'center'
|
|
729
|
+
|
|
730
|
+
// Account for 3D effect by faking the key position
|
|
731
|
+
if (obj.type == 'bar' && prop['chart.variant'] == '3d') {
|
|
732
|
+
keypos = 'gutter';
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
co.beginPath();
|
|
736
|
+
co.fillStyle = prop['chart.text.color'] ? prop['chart.text.color'] : 'black';
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Vertically center the text if the key is not present
|
|
744
|
+
*/
|
|
745
|
+
if (keypos && keypos != 'gutter') {
|
|
746
|
+
var valign = 'center';
|
|
747
|
+
|
|
748
|
+
} else if (!keypos) {
|
|
749
|
+
var valign = 'center';
|
|
750
|
+
|
|
751
|
+
} else {
|
|
752
|
+
var valign = 'bottom';
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
// if chart.title.vpos is a number, use that
|
|
760
|
+
if (typeof prop['chart.title.vpos'] === 'number') {
|
|
761
|
+
vpos = prop['chart.title.vpos'] * gutterTop;
|
|
762
|
+
|
|
763
|
+
if (prop['chart.xaxispos'] === 'top') {
|
|
764
|
+
vpos = prop['chart.title.vpos'] * gutterBottom + gutterTop + (ca.height - gutterTop - gutterBottom);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
} else {
|
|
768
|
+
vpos = gutterTop - size - 5;
|
|
769
|
+
|
|
770
|
+
if (prop['chart.xaxispos'] === 'top') {
|
|
771
|
+
vpos = ca.height - gutterBottom + size + 5;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
// if chart.title.hpos is a number, use that. It's multiplied with the (entire) canvas width
|
|
779
|
+
if (typeof hpos === 'number') {
|
|
780
|
+
centerx = hpos * ca.width;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Now the chart.title.x and chart.title.y settings override (is set) the above
|
|
785
|
+
*/
|
|
786
|
+
if (typeof x === 'number') centerx = x;
|
|
787
|
+
if (typeof y === 'number') vpos = y;
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Horizontal alignment can now (Jan 2013) be specified
|
|
794
|
+
*/
|
|
795
|
+
if (typeof prop['chart.title.halign'] === 'string') {
|
|
796
|
+
halign = prop['chart.title.halign'];
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Vertical alignment can now (Jan 2013) be specified
|
|
801
|
+
*/
|
|
802
|
+
if (typeof prop['chart.title.valign'] === 'string') {
|
|
803
|
+
valign = prop['chart.title.valign'];
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
// Set the colour
|
|
811
|
+
if (typeof prop['chart.title.color'] !== null) {
|
|
812
|
+
var oldColor = co.fillStyle
|
|
813
|
+
var newColor = prop['chart.title.color'];
|
|
814
|
+
co.fillStyle = newColor ? newColor : 'black';
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Default font is Arial
|
|
822
|
+
*/
|
|
823
|
+
var font = prop['chart.text.font'];
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Override the default font with chart.title.font
|
|
830
|
+
*/
|
|
831
|
+
if (typeof prop['chart.title.font'] === 'string') {
|
|
832
|
+
font = prop['chart.title.font'];
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Draw the title
|
|
840
|
+
*/
|
|
841
|
+
RG.Text2(co, {
|
|
842
|
+
'font':font,
|
|
843
|
+
'size':size,
|
|
844
|
+
'x':centerx,
|
|
845
|
+
'y':vpos,
|
|
846
|
+
'text':text,
|
|
847
|
+
'valign':valign,
|
|
848
|
+
'halign':halign,
|
|
849
|
+
'bounding':bgcolor != null,
|
|
850
|
+
'bounding.fill':bgcolor,
|
|
851
|
+
'bold':bold,
|
|
852
|
+
'tag':'title'
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// Reset the fill colour
|
|
856
|
+
co.fillStyle = oldColor;
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Gets the mouse X/Y coordinates relative to the canvas
|
|
864
|
+
*
|
|
865
|
+
* @param object e The event object. As such this method should be used in an event listener.
|
|
866
|
+
*/
|
|
867
|
+
RG.getMouseXY = function(e)
|
|
868
|
+
{
|
|
869
|
+
var el = e.target;
|
|
870
|
+
var ca = el;
|
|
871
|
+
var caStyle = ca.style;
|
|
872
|
+
var offsetX = 0;
|
|
873
|
+
var offsetY = 0;
|
|
874
|
+
var x;
|
|
875
|
+
var y;
|
|
876
|
+
var ISFIXED = (ca.style.position == 'fixed');
|
|
877
|
+
var borderLeft = parseInt(caStyle.borderLeftWidth) || 0;
|
|
878
|
+
var borderTop = parseInt(caStyle.borderTopWidth) || 0;
|
|
879
|
+
var paddingLeft = parseInt(caStyle.paddingLeft) || 0
|
|
880
|
+
var paddingTop = parseInt(caStyle.paddingTop) || 0
|
|
881
|
+
var additionalX = borderLeft + paddingLeft;
|
|
882
|
+
var additionalY = borderTop + paddingTop;
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
if (typeof e.offsetX === 'number' && typeof e.offsetY === 'number') {
|
|
886
|
+
|
|
887
|
+
if (ISFIXED) {
|
|
888
|
+
if (RG.ISOPERA) {
|
|
889
|
+
x = e.offsetX;
|
|
890
|
+
y = e.offsetY;
|
|
891
|
+
|
|
892
|
+
} else if (RG.ISWEBKIT) {
|
|
893
|
+
x = e.offsetX - paddingLeft - borderLeft;
|
|
894
|
+
y = e.offsetY - paddingTop - borderTop;
|
|
895
|
+
|
|
896
|
+
} else if (RG.ISIE) {
|
|
897
|
+
x = e.offsetX - paddingLeft;
|
|
898
|
+
y = e.offsetY - paddingTop;
|
|
899
|
+
|
|
900
|
+
} else {
|
|
901
|
+
x = e.offsetX;
|
|
902
|
+
y = e.offsetY;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
} else {
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
if (!RG.ISIE && !RG.ISOPERA) {
|
|
914
|
+
x = e.offsetX - borderLeft - paddingLeft;
|
|
915
|
+
y = e.offsetY - borderTop - paddingTop;
|
|
916
|
+
|
|
917
|
+
} else if (RG.ISIE) {
|
|
918
|
+
x = e. offsetX - paddingLeft;
|
|
919
|
+
y = e.offsetY - paddingTop;
|
|
920
|
+
|
|
921
|
+
} else {
|
|
922
|
+
x = e.offsetX;
|
|
923
|
+
y = e.offsetY;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
} else {
|
|
928
|
+
|
|
929
|
+
if (typeof el.offsetParent !== 'undefined') {
|
|
930
|
+
do {
|
|
931
|
+
offsetX += el.offsetLeft;
|
|
932
|
+
offsetY += el.offsetTop;
|
|
933
|
+
} while ((el = el.offsetParent));
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
x = e.pageX - offsetX - additionalX;
|
|
937
|
+
y = e.pageY - offsetY - additionalY;
|
|
938
|
+
|
|
939
|
+
x -= (2 * (parseInt(document.body.style.borderLeftWidth) || 0));
|
|
940
|
+
y -= (2 * (parseInt(document.body.style.borderTopWidth) || 0));
|
|
941
|
+
|
|
942
|
+
//x += (parseInt(caStyle.borderLeftWidth) || 0);
|
|
943
|
+
//y += (parseInt(caStyle.borderTopWidth) || 0);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// We return a javascript array with x and y defined
|
|
947
|
+
return [x, y];
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* This function returns a two element array of the canvas x/y position in
|
|
955
|
+
* relation to the page
|
|
956
|
+
*
|
|
957
|
+
* @param object canvas
|
|
958
|
+
*/
|
|
959
|
+
RG.getCanvasXY = function (canvas)
|
|
960
|
+
{
|
|
961
|
+
var x = 0;
|
|
962
|
+
var y = 0;
|
|
963
|
+
var el = canvas; // !!!
|
|
964
|
+
|
|
965
|
+
do {
|
|
966
|
+
|
|
967
|
+
x += el.offsetLeft;
|
|
968
|
+
y += el.offsetTop;
|
|
969
|
+
|
|
970
|
+
// ACCOUNT FOR TABLES IN wEBkIT
|
|
971
|
+
if (el.tagName.toLowerCase() == 'table' && (RG.ISCHROME || RG.ISSAFARI)) {
|
|
972
|
+
x += parseInt(el.border) || 0;
|
|
973
|
+
y += parseInt(el.border) || 0;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
el = el.offsetParent;
|
|
977
|
+
|
|
978
|
+
} while (el && el.tagName.toLowerCase() != 'body');
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
var paddingLeft = canvas.style.paddingLeft ? parseInt(canvas.style.paddingLeft) : 0;
|
|
982
|
+
var paddingTop = canvas.style.paddingTop ? parseInt(canvas.style.paddingTop) : 0;
|
|
983
|
+
var borderLeft = canvas.style.borderLeftWidth ? parseInt(canvas.style.borderLeftWidth) : 0;
|
|
984
|
+
var borderTop = canvas.style.borderTopWidth ? parseInt(canvas.style.borderTopWidth) : 0;
|
|
985
|
+
|
|
986
|
+
if (navigator.userAgent.indexOf('Firefox') > 0) {
|
|
987
|
+
x += parseInt(document.body.style.borderLeftWidth) || 0;
|
|
988
|
+
y += parseInt(document.body.style.borderTopWidth) || 0;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return [x + paddingLeft + borderLeft, y + paddingTop + borderTop];
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* This function determines whther a canvas is fixed (CSS positioning) or not. If not it returns
|
|
999
|
+
* false. If it is then the element that is fixed is returned (it may be a parent of the canvas).
|
|
1000
|
+
*
|
|
1001
|
+
* @return Either false or the fixed positioned element
|
|
1002
|
+
*/
|
|
1003
|
+
RG.isFixed = function (canvas)
|
|
1004
|
+
{
|
|
1005
|
+
var obj = canvas;
|
|
1006
|
+
var i = 0;
|
|
1007
|
+
|
|
1008
|
+
while (obj && obj.tagName.toLowerCase() != 'body' && i < 99) {
|
|
1009
|
+
|
|
1010
|
+
if (obj.style.position == 'fixed') {
|
|
1011
|
+
return obj;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
obj = obj.offsetParent;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return false;
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Registers a graph object (used when the canvas is redrawn)
|
|
1025
|
+
*
|
|
1026
|
+
* @param object obj The object to be registered
|
|
1027
|
+
*/
|
|
1028
|
+
RG.register =
|
|
1029
|
+
RG.Register = function (obj)
|
|
1030
|
+
{
|
|
1031
|
+
// Checking this property ensures the object is only registered once
|
|
1032
|
+
if (!obj.Get('chart.noregister')) {
|
|
1033
|
+
// As of 21st/1/2012 the object registry is now used
|
|
1034
|
+
RGraph.ObjectRegistry.Add(obj);
|
|
1035
|
+
obj.Set('chart.noregister', true);
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Causes all registered objects to be redrawn
|
|
1044
|
+
*
|
|
1045
|
+
* @param string An optional color to use to clear the canvas
|
|
1046
|
+
*/
|
|
1047
|
+
RG.redraw =
|
|
1048
|
+
RG.Redraw = function ()
|
|
1049
|
+
{
|
|
1050
|
+
var objectRegistry = RGraph.ObjectRegistry.objects.byCanvasID;
|
|
1051
|
+
|
|
1052
|
+
// Get all of the canvas tags on the page
|
|
1053
|
+
var tags = document.getElementsByTagName('canvas');
|
|
1054
|
+
|
|
1055
|
+
for (var i=0,len=tags.length; i<len; ++i) {
|
|
1056
|
+
if (tags[i].__object__ && tags[i].__object__.isRGraph) {
|
|
1057
|
+
|
|
1058
|
+
// Only clear the canvas if it's not Trace'ing - this applies to the Line/Scatter Trace effects
|
|
1059
|
+
if (!tags[i].noclear) {
|
|
1060
|
+
RGraph.clear(tags[i], arguments[0] ? arguments[0] : null);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Go through the object registry and redraw *all* of the canvas'es that have been registered
|
|
1066
|
+
for (var i=0,len=objectRegistry.length; i<len; ++i) {
|
|
1067
|
+
if (objectRegistry[i]) {
|
|
1068
|
+
var id = objectRegistry[i][0];
|
|
1069
|
+
objectRegistry[i][1].Draw();
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Causes all registered objects ON THE GIVEN CANVAS to be redrawn
|
|
1079
|
+
*
|
|
1080
|
+
* @param canvas object The canvas object to redraw
|
|
1081
|
+
* @param bool Optional boolean which defaults to true and determines whether to clear the canvas
|
|
1082
|
+
*/
|
|
1083
|
+
RG.redrawCanvas =
|
|
1084
|
+
RG.RedrawCanvas = function (ca)
|
|
1085
|
+
{
|
|
1086
|
+
var objects = RG.ObjectRegistry.getObjectsByCanvasID(ca.id);
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* First clear the canvas
|
|
1090
|
+
*/
|
|
1091
|
+
if (!arguments[1] || (typeof arguments[1] === 'boolean' && !arguments[1] == false) ) {
|
|
1092
|
+
var color = arguments[2] || ca.__object__.get('clearto') || 'transparent';
|
|
1093
|
+
RG.clear(ca, color);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Now redraw all the charts associated with that canvas
|
|
1098
|
+
*/
|
|
1099
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
1100
|
+
if (objects[i]) {
|
|
1101
|
+
if (objects[i] && objects[i].isRGraph) { // Is it an RGraph object ??
|
|
1102
|
+
objects[i].Draw();
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* This function draws the background for the bar chart, line chart and scatter chart.
|
|
1113
|
+
*
|
|
1114
|
+
* @param object obj The graph object
|
|
1115
|
+
*/
|
|
1116
|
+
RG.Background.draw =
|
|
1117
|
+
RG.background.draw =
|
|
1118
|
+
RG.background.Draw = function (obj)
|
|
1119
|
+
{
|
|
1120
|
+
var func = function (obj, canvas, context)
|
|
1121
|
+
{
|
|
1122
|
+
var ca = canvas,
|
|
1123
|
+
co = context,
|
|
1124
|
+
prop = obj.properties,
|
|
1125
|
+
|
|
1126
|
+
height = 0,
|
|
1127
|
+
gutterLeft = obj.gutterLeft,
|
|
1128
|
+
gutterRight = obj.gutterRight,
|
|
1129
|
+
gutterTop = obj.gutterTop,
|
|
1130
|
+
gutterBottom = obj.gutterBottom,
|
|
1131
|
+
variant = prop['chart.variant']
|
|
1132
|
+
|
|
1133
|
+
co.fillStyle = prop['chart.text.color'];
|
|
1134
|
+
|
|
1135
|
+
// If it's a bar and 3D variant, translate
|
|
1136
|
+
if (variant == '3d') {
|
|
1137
|
+
co.save();
|
|
1138
|
+
co.translate(prop['chart.variant.threed.offsetx'], -1 * prop['chart.variant.threed.offsety']);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// X axis title
|
|
1142
|
+
if (typeof prop['chart.title.xaxis'] === 'string' && prop['chart.title.xaxis'].length) {
|
|
1143
|
+
|
|
1144
|
+
var size = prop['chart.text.size'] + 2;
|
|
1145
|
+
var font = prop['chart.text.font'];
|
|
1146
|
+
var bold = prop['chart.title.xaxis.bold'];
|
|
1147
|
+
|
|
1148
|
+
if (typeof(prop['chart.title.xaxis.size']) == 'number') {
|
|
1149
|
+
size = prop['chart.title.xaxis.size'];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (typeof(prop['chart.title.xaxis.font']) == 'string') {
|
|
1153
|
+
font = prop['chart.title.xaxis.font'];
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
var hpos = ((ca.width - gutterLeft - gutterRight) / 2) + gutterLeft;
|
|
1157
|
+
var vpos = ca.height - gutterBottom + 25;
|
|
1158
|
+
|
|
1159
|
+
if (typeof prop['chart.title.xaxis.pos'] === 'number') {
|
|
1160
|
+
vpos = ca.height - (gutterBottom * prop['chart.title.xaxis.pos']);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
// Specifically specified X/Y positions
|
|
1167
|
+
if (typeof prop['chart.title.xaxis.x'] === 'number') {
|
|
1168
|
+
hpos = prop['chart.title.xaxis.x'];
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (typeof prop['chart.title.xaxis.y'] === 'number') {
|
|
1172
|
+
vpos = prop['chart.title.xaxis.y'];
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
RG.Text2(co, {
|
|
1179
|
+
'font':font,
|
|
1180
|
+
'size':size,
|
|
1181
|
+
'x':hpos,
|
|
1182
|
+
'y':vpos,
|
|
1183
|
+
'text':prop['chart.title.xaxis'],
|
|
1184
|
+
'halign':'center',
|
|
1185
|
+
'valign':'center',
|
|
1186
|
+
'bold':bold,
|
|
1187
|
+
'tag': 'title xaxis'
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// Y axis title
|
|
1192
|
+
if (typeof(prop['chart.title.yaxis']) == 'string' && prop['chart.title.yaxis'].length) {
|
|
1193
|
+
|
|
1194
|
+
var size = prop['chart.text.size'] + 2;
|
|
1195
|
+
var font = prop['chart.text.font'];
|
|
1196
|
+
var angle = 270;
|
|
1197
|
+
var bold = prop['chart.title.yaxis.bold'];
|
|
1198
|
+
var color = prop['chart.title.yaxis.color'];
|
|
1199
|
+
|
|
1200
|
+
if (typeof(prop['chart.title.yaxis.pos']) == 'number') {
|
|
1201
|
+
var yaxis_title_pos = prop['chart.title.yaxis.pos'] * gutterLeft;
|
|
1202
|
+
} else {
|
|
1203
|
+
var yaxis_title_pos = ((gutterLeft - 25) / gutterLeft) * gutterLeft;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (typeof prop['chart.title.yaxis.size'] === 'number') {
|
|
1207
|
+
size = prop['chart.title.yaxis.size'];
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (typeof prop['chart.title.yaxis.font'] === 'string') {
|
|
1211
|
+
font = prop['chart.title.yaxis.font'];
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if ( prop['chart.title.yaxis.align'] == 'right'
|
|
1215
|
+
|| prop['chart.title.yaxis.position'] == 'right'
|
|
1216
|
+
|| (obj.type === 'hbar' && prop['chart.yaxispos'] === 'right' && typeof prop['chart.title.yaxis.align'] === 'undefined' && typeof prop['chart.title.yaxis.position'] === 'undefined')
|
|
1217
|
+
) {
|
|
1218
|
+
|
|
1219
|
+
angle = 90;
|
|
1220
|
+
yaxis_title_pos = prop['chart.title.yaxis.pos'] ? (ca.width - gutterRight) + (prop['chart.title.yaxis.pos'] * gutterRight) :
|
|
1221
|
+
ca.width - gutterRight + prop['chart.text.size'] + 5;
|
|
1222
|
+
} else {
|
|
1223
|
+
yaxis_title_pos = yaxis_title_pos;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
var y = ((ca.height - gutterTop - gutterBottom) / 2) + gutterTop;
|
|
1227
|
+
|
|
1228
|
+
// Specifically specified X/Y positions
|
|
1229
|
+
if (typeof prop['chart.title.yaxis.x'] === 'number') {
|
|
1230
|
+
yaxis_title_pos = prop['chart.title.yaxis.x'];
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (typeof prop['chart.title.yaxis.y'] === 'number') {
|
|
1234
|
+
y = prop['chart.title.yaxis.y'];
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
co.fillStyle = color;
|
|
1238
|
+
RG.text2(co, {
|
|
1239
|
+
'font':font,
|
|
1240
|
+
'size':size,
|
|
1241
|
+
'x':yaxis_title_pos,
|
|
1242
|
+
'y':y,
|
|
1243
|
+
'valign':'center',
|
|
1244
|
+
'halign':'center',
|
|
1245
|
+
'angle':angle,
|
|
1246
|
+
'bold':bold,
|
|
1247
|
+
'text':prop['chart.title.yaxis'],
|
|
1248
|
+
'tag':'title yaxis'
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* If the background color is spec ified - draw that. It's a rectangle that fills the
|
|
1254
|
+
* entire area within the gutters
|
|
1255
|
+
*/
|
|
1256
|
+
var bgcolor = prop['chart.background.color'];
|
|
1257
|
+
if (bgcolor) {
|
|
1258
|
+
co.fillStyle = bgcolor;
|
|
1259
|
+
co.fillRect(gutterLeft + 0.5, gutterTop + 0.5, ca.width - gutterLeft - gutterRight, ca.height - gutterTop - gutterBottom);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* Draw horizontal background bars
|
|
1282
|
+
*/
|
|
1283
|
+
var numbars = (prop['chart.ylabels.count'] || 5);
|
|
1284
|
+
var barHeight = (ca.height - gutterBottom - gutterTop) / numbars;
|
|
1285
|
+
|
|
1286
|
+
co.beginPath();
|
|
1287
|
+
co.fillStyle = prop['chart.background.barcolor1'];
|
|
1288
|
+
co.strokeStyle = co.fillStyle;
|
|
1289
|
+
height = (ca.height - gutterBottom);
|
|
1290
|
+
|
|
1291
|
+
for (var i=0; i<numbars; i+=2) {
|
|
1292
|
+
co.rect(gutterLeft,
|
|
1293
|
+
(i * barHeight) + gutterTop,
|
|
1294
|
+
ca.width - gutterLeft - gutterRight,
|
|
1295
|
+
barHeight
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
co.fill();
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
|
|
1302
|
+
co.beginPath();
|
|
1303
|
+
co.fillStyle = prop['chart.background.barcolor2'];
|
|
1304
|
+
co.strokeStyle = co.fillStyle;
|
|
1305
|
+
|
|
1306
|
+
for (var i=1; i<numbars; i+=2) {
|
|
1307
|
+
co.rect(
|
|
1308
|
+
gutterLeft,
|
|
1309
|
+
(i * barHeight) + gutterTop,
|
|
1310
|
+
ca.width - gutterLeft - gutterRight,
|
|
1311
|
+
barHeight
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
co.fill();
|
|
1316
|
+
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
// Draw the background grid
|
|
1341
|
+
if (prop['chart.background.grid']) {
|
|
1342
|
+
|
|
1343
|
+
// If autofit is specified, use the .numhlines and .numvlines along with the width to work
|
|
1344
|
+
// out the hsize and vsize
|
|
1345
|
+
if (prop['chart.background.grid.autofit']) {
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Align the grid to the tickmarks
|
|
1349
|
+
*/
|
|
1350
|
+
if (prop['chart.background.grid.autofit.align']) {
|
|
1351
|
+
|
|
1352
|
+
// Align the horizontal lines
|
|
1353
|
+
if (obj.type === 'hbar') {
|
|
1354
|
+
obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// Align the vertical lines for the line
|
|
1358
|
+
if (obj.type === 'line') {
|
|
1359
|
+
if (typeof prop['chart.background.grid.autofit.numvlines'] === 'number') {
|
|
1360
|
+
// Nada
|
|
1361
|
+
} else if (prop['chart.labels'] && prop['chart.labels'].length) {
|
|
1362
|
+
obj.Set('chart.background.grid.autofit.numvlines', prop['chart.labels'].length - 1);
|
|
1363
|
+
} else {
|
|
1364
|
+
obj.Set('chart.background.grid.autofit.numvlines', obj.data[0].length - 1);
|
|
1365
|
+
}
|
|
1366
|
+
} else if (obj.type === 'waterfall') {
|
|
1367
|
+
obj.set(
|
|
1368
|
+
'backgroundGridAutofitNumvlines',
|
|
1369
|
+
obj.data.length + (prop['chart.total'] ? 1 : 0)
|
|
1370
|
+
);
|
|
1371
|
+
|
|
1372
|
+
|
|
1373
|
+
// Align the vertical lines for the bar, Scatter
|
|
1374
|
+
} else if ( (
|
|
1375
|
+
obj.type === 'bar' ||
|
|
1376
|
+
obj.type === 'scatter'
|
|
1377
|
+
)
|
|
1378
|
+
|
|
1379
|
+
&& (
|
|
1380
|
+
(prop['chart.labels'] && prop['chart.labels'].length)
|
|
1381
|
+
|| obj.type === 'bar'
|
|
1382
|
+
)
|
|
1383
|
+
) {
|
|
1384
|
+
|
|
1385
|
+
var len = (prop['chart.labels'] && prop['chart.labels'].length) || obj.data.length;
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
obj.set({
|
|
1389
|
+
backgroundGridAutofitNumvlines: len
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
// Gantt
|
|
1393
|
+
} else if (obj.type === 'gantt') {
|
|
1394
|
+
|
|
1395
|
+
if (typeof obj.get('chart.background.grid.autofit.numvlines') === 'number') {
|
|
1396
|
+
// Nothing to do here
|
|
1397
|
+
} else {
|
|
1398
|
+
obj.set('chart.background.grid.autofit.numvlines', prop['chart.xmax']);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
|
|
1402
|
+
|
|
1403
|
+
// HBar
|
|
1404
|
+
} else if (obj.type === 'hbar' && RG.isNull(prop['chart.background.grid.autofit.numhlines']) ) {
|
|
1405
|
+
obj.set('chart.background.grid.autofit.numhlines', obj.data.length);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
var vsize = ((ca.width - gutterLeft - gutterRight)) / prop['chart.background.grid.autofit.numvlines'];
|
|
1410
|
+
var hsize = (ca.height - gutterTop - gutterBottom) / prop['chart.background.grid.autofit.numhlines'];
|
|
1411
|
+
|
|
1412
|
+
obj.Set('chart.background.grid.vsize', vsize);
|
|
1413
|
+
obj.Set('chart.background.grid.hsize', hsize);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
co.beginPath();
|
|
1417
|
+
co.lineWidth = prop['chart.background.grid.width'] ? prop['chart.background.grid.width'] : 1;
|
|
1418
|
+
co.strokeStyle = prop['chart.background.grid.color'];
|
|
1419
|
+
|
|
1420
|
+
// Dashed background grid
|
|
1421
|
+
if (prop['chart.background.grid.dashed'] && typeof co.setLineDash == 'function') {
|
|
1422
|
+
co.setLineDash([3,5]);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Dotted background grid
|
|
1426
|
+
if (prop['chart.background.grid.dotted'] && typeof co.setLineDash == 'function') {
|
|
1427
|
+
co.setLineDash([1,3]);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
co.beginPath();
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
// Draw the horizontal lines
|
|
1434
|
+
if (prop['chart.background.grid.hlines']) {
|
|
1435
|
+
height = (ca.height - gutterBottom)
|
|
1436
|
+
var hsize = prop['chart.background.grid.hsize'];
|
|
1437
|
+
for (y=gutterTop; y<=height; y+=hsize) {
|
|
1438
|
+
context.moveTo(gutterLeft, ma.round(y));
|
|
1439
|
+
context.lineTo(ca.width - gutterRight, ma.round(y));
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (prop['chart.background.grid.vlines']) {
|
|
1444
|
+
// Draw the vertical lines
|
|
1445
|
+
var width = (ca.width - gutterRight)
|
|
1446
|
+
var vsize = prop['chart.background.grid.vsize'];
|
|
1447
|
+
|
|
1448
|
+
for (x=gutterLeft; x<=width; x+=vsize) {
|
|
1449
|
+
co.moveTo(ma.round(x), gutterTop);
|
|
1450
|
+
co.lineTo(ma.round(x), ca.height - gutterBottom);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
if (prop['chart.background.grid.border']) {
|
|
1455
|
+
// Make sure a rectangle, the same colour as the grid goes around the graph
|
|
1456
|
+
co.strokeStyle = prop['chart.background.grid.color'];
|
|
1457
|
+
co.strokeRect(ma.round(gutterLeft), ma.round(gutterTop), ca.width - gutterLeft - gutterRight, ca.height - gutterTop - gutterBottom);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
co.stroke();
|
|
1462
|
+
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
// Necessary to ensure the gris drawn before continuing
|
|
1466
|
+
co.beginPath();
|
|
1467
|
+
co.closePath();
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
|
|
1471
|
+
// If it's a bar and 3D variant, translate
|
|
1472
|
+
if (variant == '3d') {
|
|
1473
|
+
co.restore();
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// Reset the line dash
|
|
1477
|
+
if (typeof co.setLineDash == 'function') {
|
|
1478
|
+
co.setLineDash([1,0]);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Draw the title if one is set
|
|
1482
|
+
if ( typeof(prop['chart.title']) == 'string') {
|
|
1483
|
+
|
|
1484
|
+
if (obj.type == 'gantt') {
|
|
1485
|
+
gutterTop -= 10;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
RG.drawTitle(
|
|
1489
|
+
// Because of caching the obj variablee cannot be used here
|
|
1490
|
+
{context: co, canvas: ca, properties: prop},
|
|
1491
|
+
prop['chart.title'],
|
|
1492
|
+
gutterTop,
|
|
1493
|
+
null,
|
|
1494
|
+
prop['chart.title.size'] ? prop['chart.title.size'] : prop['chart.text.size'] + 2,
|
|
1495
|
+
{canvas: ca, context: co}
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
co.stroke();
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// Now a cached draw in newer browsers
|
|
1503
|
+
RG.ISOLD ? func(obj, obj.canvas, obj.context) : RG.cachedDraw(obj, obj.uid + '_background', func);
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Formats a number with thousand seperators so it's easier to read
|
|
1511
|
+
*
|
|
1512
|
+
* @param integer obj The chart object
|
|
1513
|
+
* @param integer num The number to format
|
|
1514
|
+
* @param string The (optional) string to prepend to the string
|
|
1515
|
+
* @param string The (optional) string to append to the string
|
|
1516
|
+
* @return string The formatted number
|
|
1517
|
+
*/
|
|
1518
|
+
RG.numberFormat =
|
|
1519
|
+
RG.number_format = function (obj, num)
|
|
1520
|
+
{
|
|
1521
|
+
var ca = obj.canvas;
|
|
1522
|
+
var co = obj.context;
|
|
1523
|
+
var prop = obj.properties;
|
|
1524
|
+
|
|
1525
|
+
var i;
|
|
1526
|
+
var prepend = arguments[2] ? String(arguments[2]) : '';
|
|
1527
|
+
var append = arguments[3] ? String(arguments[3]) : '';
|
|
1528
|
+
var output = '';
|
|
1529
|
+
var decimal = '';
|
|
1530
|
+
var decimal_seperator = typeof prop['chart.scale.point'] == 'string' ? prop['chart.scale.point'] : '.';
|
|
1531
|
+
var thousand_seperator = typeof prop['chart.scale.thousand'] == 'string' ? prop['chart.scale.thousand'] : ',';
|
|
1532
|
+
RegExp.$1 = '';
|
|
1533
|
+
var i,j;
|
|
1534
|
+
|
|
1535
|
+
if (typeof prop['chart.scale.formatter'] === 'function') {
|
|
1536
|
+
return prop['chart.scale.formatter'](obj, num);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// Ignore the preformatted version of "1e-2"
|
|
1540
|
+
if (String(num).indexOf('e') > 0) {
|
|
1541
|
+
return String(prepend + String(num) + append);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// We need then number as a string
|
|
1545
|
+
num = String(num);
|
|
1546
|
+
|
|
1547
|
+
// Take off the decimal part - we re-append it later
|
|
1548
|
+
if (num.indexOf('.') > 0) {
|
|
1549
|
+
var tmp = num;
|
|
1550
|
+
num = num.replace(/\.(.*)/, ''); // The front part of the number
|
|
1551
|
+
decimal = tmp.replace(/(.*)\.(.*)/, '$2'); // The decimal part of the number
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// Thousand seperator
|
|
1555
|
+
//var seperator = arguments[1] ? String(arguments[1]) : ',';
|
|
1556
|
+
var seperator = thousand_seperator;
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* Work backwards adding the thousand seperators
|
|
1560
|
+
*/
|
|
1561
|
+
var foundPoint;
|
|
1562
|
+
for (i=(num.length - 1),j=0; i>=0; j++,i--) {
|
|
1563
|
+
var character = num.charAt(i);
|
|
1564
|
+
|
|
1565
|
+
if ( j % 3 == 0 && j != 0) {
|
|
1566
|
+
output += seperator;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* Build the output
|
|
1571
|
+
*/
|
|
1572
|
+
output += character;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* Now need to reverse the string
|
|
1577
|
+
*/
|
|
1578
|
+
var rev = output;
|
|
1579
|
+
output = '';
|
|
1580
|
+
for (i=(rev.length - 1); i>=0; i--) {
|
|
1581
|
+
output += rev.charAt(i);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Tidy up
|
|
1585
|
+
//output = output.replace(/^-,/, '-');
|
|
1586
|
+
if (output.indexOf('-' + prop['chart.scale.thousand']) == 0) {
|
|
1587
|
+
output = '-' + output.substr(('-' + prop['chart.scale.thousand']).length);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// Reappend the decimal
|
|
1591
|
+
if (decimal.length) {
|
|
1592
|
+
output = output + decimal_seperator + decimal;
|
|
1593
|
+
decimal = '';
|
|
1594
|
+
RegExp.$1 = '';
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// Minor bugette
|
|
1598
|
+
if (output.charAt(0) == '-') {
|
|
1599
|
+
output = output.replace(/-/, '');
|
|
1600
|
+
prepend = '-' + prepend;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
return prepend + output + append;
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
|
|
1608
|
+
|
|
1609
|
+
/**
|
|
1610
|
+
* Draws horizontal coloured bars on something like the bar, line or scatter
|
|
1611
|
+
*/
|
|
1612
|
+
RG.drawBars =
|
|
1613
|
+
RG.DrawBars = function (obj)
|
|
1614
|
+
{
|
|
1615
|
+
var prop = obj.properties;
|
|
1616
|
+
var co = obj.context;
|
|
1617
|
+
var ca = obj.canvas;
|
|
1618
|
+
var hbars = prop['chart.background.hbars'];
|
|
1619
|
+
|
|
1620
|
+
if (hbars === null) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* Draws a horizontal bar
|
|
1626
|
+
*/
|
|
1627
|
+
co.beginPath();
|
|
1628
|
+
|
|
1629
|
+
for (i=0,len=hbars.length; i<len; ++i) {
|
|
1630
|
+
|
|
1631
|
+
var start = hbars[i][0];
|
|
1632
|
+
var length = hbars[i][1];
|
|
1633
|
+
var color = hbars[i][2];
|
|
1634
|
+
|
|
1635
|
+
|
|
1636
|
+
// Perform some bounds checking
|
|
1637
|
+
if(RG.is_null(start))start = obj.scale2.max
|
|
1638
|
+
if (start > obj.scale2.max) start = obj.scale2.max;
|
|
1639
|
+
if (RG.is_null(length)) length = obj.scale2.max - start;
|
|
1640
|
+
if (start + length > obj.scale2.max) length = obj.scale2.max - start;
|
|
1641
|
+
if (start + length < (-1 * obj.scale2.max) ) length = (-1 * obj.scale2.max) - start;
|
|
1642
|
+
|
|
1643
|
+
if (prop['chart.xaxispos'] == 'center' && start == obj.scale2.max && length < (obj.scale2.max * -2)) {
|
|
1644
|
+
length = obj.scale2.max * -2;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Draw the bar
|
|
1650
|
+
*/
|
|
1651
|
+
var x = prop['chart.gutter.left'];
|
|
1652
|
+
var y = obj.getYCoord(start);
|
|
1653
|
+
var w = ca.width - prop['chart.gutter.left'] - prop['chart.gutter.right'];
|
|
1654
|
+
var h = obj.getYCoord(start + length) - y;
|
|
1655
|
+
|
|
1656
|
+
// Accommodate Opera :-/
|
|
1657
|
+
if (RG.ISOPERA != -1 && prop['chart.xaxispos'] == 'center' && h < 0) {
|
|
1658
|
+
h *= -1;
|
|
1659
|
+
y = y - h;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* Account for X axis at the top
|
|
1664
|
+
*/
|
|
1665
|
+
if (prop['chart.xaxispos'] == 'top') {
|
|
1666
|
+
y = ca.height - y;
|
|
1667
|
+
h *= -1;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
co.fillStyle = color;
|
|
1671
|
+
co.fillRect(x, y, w, h);
|
|
1672
|
+
}
|
|
1673
|
+
/*
|
|
1674
|
+
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
// If the X axis is at the bottom, and a negative max is given, warn the user
|
|
1680
|
+
if (obj.Get('chart.xaxispos') == 'bottom' && (hbars[i][0] < 0 || (hbars[i][1] + hbars[i][1] < 0)) ) {
|
|
1681
|
+
alert('[' + obj.type.toUpperCase() + ' (ID: ' + obj.id + ') BACKGROUND HBARS] You have a negative value in one of your background hbars values, whilst the X axis is in the center');
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
var ystart = (obj.grapharea - (((hbars[i][0] - obj.scale2.min) / (obj.scale2.max - obj.scale2.min)) * obj.grapharea));
|
|
1685
|
+
//var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / (obj.scale2.max - obj.scale2.min)) * obj.grapharea;
|
|
1686
|
+
var height = obj.getYCoord(hbars[i][0]) - obj.getYCoord(hbars[i][1]);
|
|
1687
|
+
|
|
1688
|
+
// Account for the X axis being in the center
|
|
1689
|
+
if (obj.Get('chart.xaxispos') == 'center') {
|
|
1690
|
+
ystart /= 2;
|
|
1691
|
+
//height /= 2;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
ystart += obj.Get('chart.gutter.top')
|
|
1695
|
+
|
|
1696
|
+
var x = obj.Get('chart.gutter.left');
|
|
1697
|
+
var y = ystart - height;
|
|
1698
|
+
var w = obj.canvas.width - obj.Get('chart.gutter.left') - obj.Get('chart.gutter.right');
|
|
1699
|
+
var h = height;
|
|
1700
|
+
|
|
1701
|
+
// Accommodate Opera :-/
|
|
1702
|
+
if (navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0) {
|
|
1703
|
+
h *= -1;
|
|
1704
|
+
y = y - h;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* Account for X axis at the top
|
|
1709
|
+
*/
|
|
1710
|
+
//if (obj.Get('chart.xaxispos') == 'top') {
|
|
1711
|
+
// y = obj.canvas.height - y;
|
|
1712
|
+
// h *= -1;
|
|
1713
|
+
//}
|
|
1714
|
+
|
|
1715
|
+
//obj.context.fillStyle = hbars[i][2];
|
|
1716
|
+
//obj.context.fillRect(x, y, w, h);
|
|
1717
|
+
//}
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Draws in-graph labels.
|
|
1725
|
+
*
|
|
1726
|
+
* @param object obj The graph object
|
|
1727
|
+
*/
|
|
1728
|
+
RG.drawInGraphLabels =
|
|
1729
|
+
RG.DrawInGraphLabels = function (obj)
|
|
1730
|
+
{
|
|
1731
|
+
var ca = obj.canvas;
|
|
1732
|
+
var co = obj.context;
|
|
1733
|
+
var prop = obj.properties;
|
|
1734
|
+
var labels = prop['chart.labels.ingraph'];
|
|
1735
|
+
var labels_processed = [];
|
|
1736
|
+
|
|
1737
|
+
// Defaults
|
|
1738
|
+
var fgcolor = 'black';
|
|
1739
|
+
var bgcolor = 'white';
|
|
1740
|
+
var direction = 1;
|
|
1741
|
+
|
|
1742
|
+
if (!labels) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
* Preprocess the labels array. Numbers are expanded
|
|
1748
|
+
*/
|
|
1749
|
+
for (var i=0,len=labels.length; i<len; i+=1) {
|
|
1750
|
+
if (typeof labels[i] === 'number') {
|
|
1751
|
+
for (var j=0; j<labels[i]; ++j) {
|
|
1752
|
+
labels_processed.push(null);
|
|
1753
|
+
}
|
|
1754
|
+
} else if (typeof labels[i] === 'string' || typeof labels[i] === 'object') {
|
|
1755
|
+
labels_processed.push(labels[i]);
|
|
1756
|
+
|
|
1757
|
+
} else {
|
|
1758
|
+
labels_processed.push('');
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
/**
|
|
1763
|
+
* Turn off any shadow
|
|
1764
|
+
*/
|
|
1765
|
+
RG.NoShadow(obj);
|
|
1766
|
+
|
|
1767
|
+
if (labels_processed && labels_processed.length > 0) {
|
|
1768
|
+
|
|
1769
|
+
for (var i=0,len=labels_processed.length; i<len; i+=1) {
|
|
1770
|
+
if (labels_processed[i]) {
|
|
1771
|
+
var coords = obj.coords[i];
|
|
1772
|
+
|
|
1773
|
+
if (coords && coords.length > 0) {
|
|
1774
|
+
var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]);
|
|
1775
|
+
var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]);
|
|
1776
|
+
var length = typeof labels_processed[i][4] === 'number' ? labels_processed[i][4] : 25;
|
|
1777
|
+
|
|
1778
|
+
co.beginPath();
|
|
1779
|
+
co.fillStyle = 'black';
|
|
1780
|
+
co.strokeStyle = 'black';
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
if (obj.type === 'bar') {
|
|
1784
|
+
|
|
1785
|
+
/**
|
|
1786
|
+
* X axis at the top
|
|
1787
|
+
*/
|
|
1788
|
+
if (obj.Get('chart.xaxispos') == 'top') {
|
|
1789
|
+
length *= -1;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (prop['chart.variant'] == 'dot') {
|
|
1793
|
+
co.moveTo(ma.round(x), obj.coords[i][1] - 5);
|
|
1794
|
+
co.lineTo(ma.round(x), obj.coords[i][1] - 5 - length);
|
|
1795
|
+
|
|
1796
|
+
var text_x = ma.round(x);
|
|
1797
|
+
var text_y = obj.coords[i][1] - 5 - length;
|
|
1798
|
+
|
|
1799
|
+
} else if (prop['chart.variant'] == 'arrow') {
|
|
1800
|
+
co.moveTo(ma.round(x), obj.coords[i][1] - 5);
|
|
1801
|
+
co.lineTo(ma.round(x), obj.coords[i][1] - 5 - length);
|
|
1802
|
+
|
|
1803
|
+
var text_x = ma.round(x);
|
|
1804
|
+
var text_y = obj.coords[i][1] - 5 - length;
|
|
1805
|
+
|
|
1806
|
+
} else {
|
|
1807
|
+
|
|
1808
|
+
co.arc(ma.round(x), y, 2.5, 0, 6.28, 0);
|
|
1809
|
+
co.moveTo(ma.round(x), y);
|
|
1810
|
+
co.lineTo(ma.round(x), y - length);
|
|
1811
|
+
|
|
1812
|
+
var text_x = ma.round(x);
|
|
1813
|
+
var text_y = y - length;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
co.stroke();
|
|
1817
|
+
co.fill();
|
|
1818
|
+
|
|
1819
|
+
|
|
1820
|
+
} else if (obj.type == 'line') {
|
|
1821
|
+
|
|
1822
|
+
if (
|
|
1823
|
+
typeof labels_processed[i] == 'object' &&
|
|
1824
|
+
typeof labels_processed[i][3] == 'number' &&
|
|
1825
|
+
labels_processed[i][3] == -1
|
|
1826
|
+
) {
|
|
1827
|
+
|
|
1828
|
+
co.moveTo(ma.round(x), y + 5);
|
|
1829
|
+
co.lineTo(ma.round(x), y + 5 + length);
|
|
1830
|
+
|
|
1831
|
+
co.stroke();
|
|
1832
|
+
co.beginPath();
|
|
1833
|
+
|
|
1834
|
+
// This draws the arrow
|
|
1835
|
+
co.moveTo(ma.round(x), y + 5);
|
|
1836
|
+
co.lineTo(ma.round(x) - 3, y + 10);
|
|
1837
|
+
co.lineTo(ma.round(x) + 3, y + 10);
|
|
1838
|
+
co.closePath();
|
|
1839
|
+
|
|
1840
|
+
var text_x = x;
|
|
1841
|
+
var text_y = y + 5 + length;
|
|
1842
|
+
|
|
1843
|
+
} else {
|
|
1844
|
+
|
|
1845
|
+
var text_x = x;
|
|
1846
|
+
var text_y = y - 5 - length;
|
|
1847
|
+
|
|
1848
|
+
co.moveTo(ma.round(x), y - 5);
|
|
1849
|
+
co.lineTo(ma.round(x), y - 5 - length);
|
|
1850
|
+
|
|
1851
|
+
co.stroke();
|
|
1852
|
+
co.beginPath();
|
|
1853
|
+
|
|
1854
|
+
// This draws the arrow
|
|
1855
|
+
co.moveTo(ma.round(x), y - 5);
|
|
1856
|
+
co.lineTo(ma.round(x) - 3, y - 10);
|
|
1857
|
+
co.lineTo(ma.round(x) + 3, y - 10);
|
|
1858
|
+
co.closePath();
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
co.fill();
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// Taken out on the 10th Nov 2010 - unnecessary
|
|
1865
|
+
//var width = context.measureText(labels[i]).width;
|
|
1866
|
+
|
|
1867
|
+
co.beginPath();
|
|
1868
|
+
|
|
1869
|
+
// Fore ground color
|
|
1870
|
+
co.fillStyle = (typeof labels_processed[i] === 'object' && typeof labels_processed[i][1] === 'string') ? labels_processed[i][1] : 'black';
|
|
1871
|
+
|
|
1872
|
+
RG.Text2(obj,{'font':prop['chart.text.font'],
|
|
1873
|
+
'size':prop['chart.text.size'],
|
|
1874
|
+
'x':text_x,
|
|
1875
|
+
'y':text_y,
|
|
1876
|
+
'text': (typeof labels_processed[i] === 'object' && typeof labels_processed[i][0] === 'string') ? labels_processed[i][0] : labels_processed[i],
|
|
1877
|
+
'valign': 'bottom',
|
|
1878
|
+
'halign':'center',
|
|
1879
|
+
'bounding':true,
|
|
1880
|
+
'bounding.fill': (typeof labels_processed[i] === 'object' && typeof labels_processed[i][2] === 'string') ? labels_processed[i][2] : 'white',
|
|
1881
|
+
'tag':'labels ingraph'
|
|
1882
|
+
});
|
|
1883
|
+
co.fill();
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* This function "fills in" key missing properties that various implementations lack
|
|
1895
|
+
*
|
|
1896
|
+
* @param object e The event object
|
|
1897
|
+
*/
|
|
1898
|
+
RG.fixEventObject =
|
|
1899
|
+
RG.FixEventObject = function (e)
|
|
1900
|
+
{
|
|
1901
|
+
if (RG.ISOLD) {
|
|
1902
|
+
var e = event;
|
|
1903
|
+
|
|
1904
|
+
e.pageX = (event.clientX + doc.body.scrollLeft);
|
|
1905
|
+
e.pageY = (event.clientY + doc.body.scrollTop);
|
|
1906
|
+
e.target = event.srcElement;
|
|
1907
|
+
|
|
1908
|
+
if (!doc.body.scrollTop && doc.documentElement.scrollTop) {
|
|
1909
|
+
e.pageX += parseInt(doc.documentElement.scrollLeft);
|
|
1910
|
+
e.pageY += parseInt(doc.documentElement.scrollTop);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
|
|
1915
|
+
// Any browser that doesn't implement stopPropagation() (MSIE)
|
|
1916
|
+
if (!e.stopPropagation) {
|
|
1917
|
+
e.stopPropagation = function () {window.event.cancelBubble = true;}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
return e;
|
|
1921
|
+
};
|
|
1922
|
+
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
/**
|
|
1927
|
+
* Thisz function hides the crosshairs coordinates
|
|
1928
|
+
*/
|
|
1929
|
+
RG.hideCrosshairCoords =
|
|
1930
|
+
RG.HideCrosshairCoords = function ()
|
|
1931
|
+
{
|
|
1932
|
+
var div = RG.Registry.Get('chart.coordinates.coords.div');
|
|
1933
|
+
|
|
1934
|
+
if ( div
|
|
1935
|
+
&& div.style.opacity == 1
|
|
1936
|
+
&& div.__object__.Get('chart.crosshairs.coords.fadeout')
|
|
1937
|
+
) {
|
|
1938
|
+
|
|
1939
|
+
var style = RG.Registry.Get('chart.coordinates.coords.div').style;
|
|
1940
|
+
|
|
1941
|
+
setTimeout(function() {style.opacity = 0.9;}, 25);
|
|
1942
|
+
setTimeout(function() {style.opacity = 0.8;}, 50);
|
|
1943
|
+
setTimeout(function() {style.opacity = 0.7;}, 75);
|
|
1944
|
+
setTimeout(function() {style.opacity = 0.6;}, 100);
|
|
1945
|
+
setTimeout(function() {style.opacity = 0.5;}, 125);
|
|
1946
|
+
setTimeout(function() {style.opacity = 0.4;}, 150);
|
|
1947
|
+
setTimeout(function() {style.opacity = 0.3;}, 175);
|
|
1948
|
+
setTimeout(function() {style.opacity = 0.2;}, 200);
|
|
1949
|
+
setTimeout(function() {style.opacity = 0.1;}, 225);
|
|
1950
|
+
setTimeout(function() {style.opacity = 0;}, 250);
|
|
1951
|
+
setTimeout(function() {style.display = 'none';}, 275);
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* Draws the3D axes/background
|
|
1960
|
+
*
|
|
1961
|
+
* @param object obj The chart object
|
|
1962
|
+
*/
|
|
1963
|
+
RG.draw3DAxes =
|
|
1964
|
+
RG.Draw3DAxes = function (obj)
|
|
1965
|
+
{
|
|
1966
|
+
var prop = obj.properties;
|
|
1967
|
+
var co = obj.context;
|
|
1968
|
+
var ca = obj.canvas;
|
|
1969
|
+
|
|
1970
|
+
var gutterLeft = obj.gutterLeft,
|
|
1971
|
+
gutterRight = obj.gutterRight,
|
|
1972
|
+
gutterTop = obj.gutterTop,
|
|
1973
|
+
gutterBottom = obj.gutterBottom,
|
|
1974
|
+
xaxispos = prop['chart.xaxispos'],
|
|
1975
|
+
graphArea = ca.height - gutterTop - gutterBottom,
|
|
1976
|
+
halfGraphArea = graphArea / 2,
|
|
1977
|
+
offsetx = prop['chart.variant.threed.offsetx'],
|
|
1978
|
+
offsety = prop['chart.variant.threed.offsety']
|
|
1979
|
+
|
|
1980
|
+
if (!prop['chart.noaxes'] && !prop['chart.noyaxis']) {
|
|
1981
|
+
RG.path(co, [
|
|
1982
|
+
'b',
|
|
1983
|
+
// Y axis
|
|
1984
|
+
'm', gutterLeft,gutterTop,
|
|
1985
|
+
'l',gutterLeft + offsetx,gutterTop - offsety,
|
|
1986
|
+
'l',gutterLeft + offsetx,ca.height - gutterBottom - offsety,
|
|
1987
|
+
'l',gutterLeft,ca.height - gutterBottom
|
|
1988
|
+
]);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// X axis
|
|
1992
|
+
if (xaxispos === 'center') {
|
|
1993
|
+
RG.path(co, [
|
|
1994
|
+
'm',gutterLeft,gutterTop + halfGraphArea,
|
|
1995
|
+
'l',gutterLeft + offsetx,gutterTop + halfGraphArea - offsety,
|
|
1996
|
+
|
|
1997
|
+
'l',ca.width - gutterRight + offsetx,gutterTop + halfGraphArea - offsety,
|
|
1998
|
+
'l',ca.width - gutterRight,gutterTop + halfGraphArea,
|
|
1999
|
+
'c','s','#aaa','f','#ddd'
|
|
2000
|
+
]);
|
|
2001
|
+
} else {
|
|
2002
|
+
RG.path(co, [
|
|
2003
|
+
'm',gutterLeft,ca.height - gutterBottom,
|
|
2004
|
+
'l',gutterLeft + offsetx,ca.height - gutterBottom - offsety,
|
|
2005
|
+
'l',ca.width - gutterRight + offsetx,ca.height - gutterBottom - offsety,
|
|
2006
|
+
'l',ca.width - gutterRight,ca.height - gutterBottom,
|
|
2007
|
+
'c','s','#aaa','f','#ddd'
|
|
2008
|
+
]);
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
|
|
2014
|
+
|
|
2015
|
+
/**
|
|
2016
|
+
* Draws a rectangle with curvy corners
|
|
2017
|
+
*
|
|
2018
|
+
* @param co object The context
|
|
2019
|
+
* @param x number The X coordinate (top left of the square)
|
|
2020
|
+
* @param y number The Y coordinate (top left of the square)
|
|
2021
|
+
* @param w number The width of the rectangle
|
|
2022
|
+
* @param h number The height of the rectangle
|
|
2023
|
+
* @param number The radius of the curved corners
|
|
2024
|
+
* @param boolean Whether the top left corner is curvy
|
|
2025
|
+
* @param boolean Whether the top right corner is curvy
|
|
2026
|
+
* @param boolean Whether the bottom right corner is curvy
|
|
2027
|
+
* @param boolean Whether the bottom left corner is curvy
|
|
2028
|
+
*/
|
|
2029
|
+
RG.strokedCurvyRect = function (co, x, y, w, h)
|
|
2030
|
+
{
|
|
2031
|
+
// The corner radius
|
|
2032
|
+
var r = arguments[5] ? arguments[5] : 3;
|
|
2033
|
+
|
|
2034
|
+
// The corners
|
|
2035
|
+
var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
|
|
2036
|
+
var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
|
|
2037
|
+
var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
|
|
2038
|
+
var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
|
|
2039
|
+
|
|
2040
|
+
co.beginPath();
|
|
2041
|
+
|
|
2042
|
+
// Top left side
|
|
2043
|
+
co.moveTo(x + (corner_tl ? r : 0), y);
|
|
2044
|
+
co.lineTo(x + w - (corner_tr ? r : 0), y);
|
|
2045
|
+
|
|
2046
|
+
// Top right corner
|
|
2047
|
+
if (corner_tr) {
|
|
2048
|
+
co.arc(x + w - r, y + r, r, RG.PI + RG.HALFPI, RG.TWOPI, false);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// Top right side
|
|
2052
|
+
co.lineTo(x + w, y + h - (corner_br ? r : 0) );
|
|
2053
|
+
|
|
2054
|
+
// Bottom right corner
|
|
2055
|
+
if (corner_br) {
|
|
2056
|
+
co.arc(x + w - r, y - r + h, r, RG.TWOPI, RG.HALFPI, false);
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
// Bottom right side
|
|
2060
|
+
co.lineTo(x + (corner_bl ? r : 0), y + h);
|
|
2061
|
+
|
|
2062
|
+
// Bottom left corner
|
|
2063
|
+
if (corner_bl) {
|
|
2064
|
+
co.arc(x + r, y - r + h, r, RG.HALFPI, RG.PI, false);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// Bottom left side
|
|
2068
|
+
co.lineTo(x, y + (corner_tl ? r : 0) );
|
|
2069
|
+
|
|
2070
|
+
// Top left corner
|
|
2071
|
+
if (corner_tl) {
|
|
2072
|
+
co.arc(x + r, y + r, r, RG.PI, RG.PI + RG.HALFPI, false);
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
co.stroke();
|
|
2076
|
+
};
|
|
2077
|
+
|
|
2078
|
+
|
|
2079
|
+
|
|
2080
|
+
|
|
2081
|
+
/**
|
|
2082
|
+
* Draws a filled rectangle with curvy corners
|
|
2083
|
+
*
|
|
2084
|
+
* @param context object The context
|
|
2085
|
+
* @param x number The X coordinate (top left of the square)
|
|
2086
|
+
* @param y number The Y coordinate (top left of the square)
|
|
2087
|
+
* @param w number The width of the rectangle
|
|
2088
|
+
* @param h number The height of the rectangle
|
|
2089
|
+
* @param number The radius of the curved corners
|
|
2090
|
+
* @param boolean Whether the top left corner is curvy
|
|
2091
|
+
* @param boolean Whether the top right corner is curvy
|
|
2092
|
+
* @param boolean Whether the bottom right corner is curvy
|
|
2093
|
+
* @param boolean Whether the bottom left corner is curvy
|
|
2094
|
+
*/
|
|
2095
|
+
RG.filledCurvyRect = function (co, x, y, w, h)
|
|
2096
|
+
{
|
|
2097
|
+
// The corner radius
|
|
2098
|
+
var r = arguments[5] ? arguments[5] : 3;
|
|
2099
|
+
|
|
2100
|
+
// The corners
|
|
2101
|
+
var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;
|
|
2102
|
+
var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;
|
|
2103
|
+
var corner_br = (arguments[8] || arguments[8] == null) ? true : false;
|
|
2104
|
+
var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;
|
|
2105
|
+
|
|
2106
|
+
co.beginPath();
|
|
2107
|
+
|
|
2108
|
+
// First draw the corners
|
|
2109
|
+
|
|
2110
|
+
// Top left corner
|
|
2111
|
+
if (corner_tl) {
|
|
2112
|
+
co.moveTo(x + r, y + r);
|
|
2113
|
+
co.arc(x + r, y + r, r, RG.PI, RG.PI + RG.HALFPI, false);
|
|
2114
|
+
} else {
|
|
2115
|
+
co.fillRect(x, y, r, r);
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// Top right corner
|
|
2119
|
+
if (corner_tr) {
|
|
2120
|
+
co.moveTo(x + w - r, y + r);
|
|
2121
|
+
co.arc(x + w - r, y + r, r, RG.PI + RG.HALFPI, 0, false);
|
|
2122
|
+
} else {
|
|
2123
|
+
co.moveTo(x + w - r, y);
|
|
2124
|
+
co.fillRect(x + w - r, y, r, r);
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
|
|
2128
|
+
// Bottom right corner
|
|
2129
|
+
if (corner_br) {
|
|
2130
|
+
co.moveTo(x + w - r, y + h - r);
|
|
2131
|
+
co.arc(x + w - r, y - r + h, r, 0, RG.HALFPI, false);
|
|
2132
|
+
} else {
|
|
2133
|
+
co.moveTo(x + w - r, y + h - r);
|
|
2134
|
+
co.fillRect(x + w - r, y + h - r, r, r);
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// Bottom left corner
|
|
2138
|
+
if (corner_bl) {
|
|
2139
|
+
co.moveTo(x + r, y + h - r);
|
|
2140
|
+
co.arc(x + r, y - r + h, r, RG.HALFPI, RG.PI, false);
|
|
2141
|
+
} else {
|
|
2142
|
+
co.moveTo(x, y + h - r);
|
|
2143
|
+
co.fillRect(x, y + h - r, r, r);
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// Now fill it in
|
|
2147
|
+
co.fillRect(x + r, y, w - r - r, h);
|
|
2148
|
+
co.fillRect(x, y + r, r + 1, h - r - r);
|
|
2149
|
+
co.fillRect(x + w - r - 1, y + r, r + 1, h - r - r);
|
|
2150
|
+
|
|
2151
|
+
co.fill();
|
|
2152
|
+
};
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* Hides the zoomed canvas
|
|
2159
|
+
*/
|
|
2160
|
+
RG.hideZoomedCanvas =
|
|
2161
|
+
RG.HideZoomedCanvas = function ()
|
|
2162
|
+
{
|
|
2163
|
+
var interval = 10;
|
|
2164
|
+
var frames = 15;
|
|
2165
|
+
|
|
2166
|
+
if (typeof RG.zoom_image === 'object') {
|
|
2167
|
+
var obj = RG.zoom_image.obj;
|
|
2168
|
+
var prop = obj.properties;
|
|
2169
|
+
} else {
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
if (prop['chart.zoom.fade.out']) {
|
|
2174
|
+
for (var i=frames,j=1; i>=0; --i, ++j) {
|
|
2175
|
+
if (typeof RG.zoom_image === 'object') {
|
|
2176
|
+
setTimeout("RGraph.zoom_image.style.opacity = " + String(i / 10), j * interval);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
if (typeof RG.zoom_background === 'object') {
|
|
2181
|
+
setTimeout("RGraph.zoom_background.style.opacity = " + String(i / frames), j * interval);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
if (typeof RG.zoom_image === 'object') {
|
|
2186
|
+
setTimeout("RGraph.zoom_image.style.display = 'none'", prop['chart.zoom.fade.out'] ? (frames * interval) + 10 : 0);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
if (typeof RG.zoom_background === 'object') {
|
|
2190
|
+
setTimeout("RGraph.zoom_background.style.display = 'none'", prop['chart.zoom.fade.out'] ? (frames * interval) + 10 : 0);
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
/**
|
|
2198
|
+
* Adds an event handler
|
|
2199
|
+
*
|
|
2200
|
+
* @param object obj The graph object
|
|
2201
|
+
* @param string event The name of the event, eg ontooltip
|
|
2202
|
+
* @param object func The callback function
|
|
2203
|
+
*/
|
|
2204
|
+
RG.addCustomEventListener =
|
|
2205
|
+
RG.AddCustomEventListener = function (obj, name, func)
|
|
2206
|
+
{
|
|
2207
|
+
var RG = RGraph;
|
|
2208
|
+
|
|
2209
|
+
if (typeof RG.events[obj.uid] === 'undefined') {
|
|
2210
|
+
RG.events[obj.uid] = [];
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
RG.events[obj.uid].push([obj, name, func]);
|
|
2214
|
+
|
|
2215
|
+
return RG.events[obj.uid].length - 1;
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2218
|
+
|
|
2219
|
+
|
|
2220
|
+
|
|
2221
|
+
/**
|
|
2222
|
+
* Used to fire one of the RGraph custom events
|
|
2223
|
+
*
|
|
2224
|
+
* @param object obj The graph object that fires the event
|
|
2225
|
+
* @param string event The name of the event to fire
|
|
2226
|
+
*/
|
|
2227
|
+
RG.fireCustomEvent =
|
|
2228
|
+
RG.FireCustomEvent = function (obj, name)
|
|
2229
|
+
{
|
|
2230
|
+
if (obj && obj.isRGraph) {
|
|
2231
|
+
|
|
2232
|
+
// New style of adding custom events
|
|
2233
|
+
if (obj[name]) {
|
|
2234
|
+
(obj[name])(obj);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
var uid = obj.uid;
|
|
2238
|
+
|
|
2239
|
+
if ( typeof uid === 'string'
|
|
2240
|
+
&& typeof RG.events === 'object'
|
|
2241
|
+
&& typeof RG.events[uid] === 'object'
|
|
2242
|
+
&& RG.events[uid].length > 0) {
|
|
2243
|
+
|
|
2244
|
+
for(var j=0; j<RG.events[uid].length; ++j) {
|
|
2245
|
+
if (RG.events[uid][j] && RG.events[uid][j][1] == name) {
|
|
2246
|
+
RG.events[uid][j][2](obj);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
/**
|
|
2257
|
+
* Clears all the custom event listeners that have been registered
|
|
2258
|
+
*
|
|
2259
|
+
* @param string Limits the clearing to this object ID
|
|
2260
|
+
*/
|
|
2261
|
+
RGraph.removeAllCustomEventListeners =
|
|
2262
|
+
RGraph.RemoveAllCustomEventListeners = function ()
|
|
2263
|
+
{
|
|
2264
|
+
var id = arguments[0];
|
|
2265
|
+
|
|
2266
|
+
if (id && RG.events[id]) {
|
|
2267
|
+
RG.events[id] = [];
|
|
2268
|
+
} else {
|
|
2269
|
+
RG.events = [];
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
|
|
2274
|
+
|
|
2275
|
+
|
|
2276
|
+
/**
|
|
2277
|
+
* Clears a particular custom event listener
|
|
2278
|
+
*
|
|
2279
|
+
* @param object obj The graph object
|
|
2280
|
+
* @param number i This is the index that is return by .AddCustomEventListener()
|
|
2281
|
+
*/
|
|
2282
|
+
RG.removeCustomEventListener =
|
|
2283
|
+
RG.RemoveCustomEventListener = function (obj, i)
|
|
2284
|
+
{
|
|
2285
|
+
if ( typeof RG.events === 'object'
|
|
2286
|
+
&& typeof RG.events[obj.id] === 'object'
|
|
2287
|
+
&& typeof RG.events[obj.id][i] === 'object') {
|
|
2288
|
+
|
|
2289
|
+
RG.events[obj.id][i] = null;
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
|
|
2293
|
+
|
|
2294
|
+
|
|
2295
|
+
|
|
2296
|
+
/**
|
|
2297
|
+
* This draws the background
|
|
2298
|
+
*
|
|
2299
|
+
* @param object obj The graph object
|
|
2300
|
+
*/
|
|
2301
|
+
RG.drawBackgroundImage =
|
|
2302
|
+
RG.DrawBackgroundImage = function (obj)
|
|
2303
|
+
{
|
|
2304
|
+
var prop = obj.properties;
|
|
2305
|
+
var ca = obj.canvas;
|
|
2306
|
+
var co = obj.context;
|
|
2307
|
+
|
|
2308
|
+
if (typeof prop['chart.background.image'] === 'string') {
|
|
2309
|
+
if (typeof ca.__rgraph_background_image__ === 'undefined') {
|
|
2310
|
+
var img = new Image();
|
|
2311
|
+
img.__object__ = obj;
|
|
2312
|
+
img.__canvas__ = ca;
|
|
2313
|
+
img.__context__ = co;
|
|
2314
|
+
img.src = obj.Get('chart.background.image');
|
|
2315
|
+
|
|
2316
|
+
ca.__rgraph_background_image__ = img;
|
|
2317
|
+
} else {
|
|
2318
|
+
img = ca.__rgraph_background_image__;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// When the image has loaded - redraw the canvas
|
|
2322
|
+
img.onload = function ()
|
|
2323
|
+
{
|
|
2324
|
+
obj.__rgraph_background_image_loaded__ = true;
|
|
2325
|
+
RG.clear(ca);
|
|
2326
|
+
RG.redrawCanvas(ca);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
var gutterLeft = obj.gutterLeft;
|
|
2330
|
+
var gutterRight = obj.gutterRight;
|
|
2331
|
+
var gutterTop = obj.gutterTop;
|
|
2332
|
+
var gutterBottom = obj.gutterBottom;
|
|
2333
|
+
var stretch = prop['chart.background.image.stretch'];
|
|
2334
|
+
var align = prop['chart.background.image.align'];
|
|
2335
|
+
|
|
2336
|
+
// Handle chart.background.image.align
|
|
2337
|
+
if (typeof align === 'string') {
|
|
2338
|
+
if (align.indexOf('right') != -1) {
|
|
2339
|
+
var x = ca.width - (prop['chart.background.image.w'] || img.width) - gutterRight;
|
|
2340
|
+
} else {
|
|
2341
|
+
var x = gutterLeft;
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
if (align.indexOf('bottom') != -1) {
|
|
2345
|
+
var y = ca.height - (prop['chart.background.image.h'] || img.height) - gutterBottom;
|
|
2346
|
+
} else {
|
|
2347
|
+
var y = gutterTop;
|
|
2348
|
+
}
|
|
2349
|
+
} else {
|
|
2350
|
+
var x = gutterLeft || 25;
|
|
2351
|
+
var y = gutterTop || 25;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// X/Y coords take precedence over the align
|
|
2355
|
+
var x = typeof prop['chart.background.image.x'] === 'number' ? prop['chart.background.image.x'] : x;
|
|
2356
|
+
var y = typeof prop['chart.background.image.y'] === 'number' ? prop['chart.background.image.y'] : y;
|
|
2357
|
+
var w = stretch ? ca.width - gutterLeft - gutterRight : img.width;
|
|
2358
|
+
var h = stretch ? ca.height - gutterTop - gutterBottom : img.height;
|
|
2359
|
+
|
|
2360
|
+
/**
|
|
2361
|
+
* You can now specify the width and height of the image
|
|
2362
|
+
*/
|
|
2363
|
+
if (typeof prop['chart.background.image.w'] === 'number') w = prop['chart.background.image.w'];
|
|
2364
|
+
if (typeof prop['chart.background.image.h'] === 'number') h = prop['chart.background.image.h'];
|
|
2365
|
+
|
|
2366
|
+
var oldAlpha = co.globalAlpha;
|
|
2367
|
+
co.globalAlpha = prop['chart.background.image.alpha'];
|
|
2368
|
+
co.drawImage(img,x,y,w, h);
|
|
2369
|
+
co.globalAlpha = oldAlpha;
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
|
|
2373
|
+
|
|
2374
|
+
|
|
2375
|
+
|
|
2376
|
+
/**
|
|
2377
|
+
* This function determines wshether an object has tooltips or not
|
|
2378
|
+
*
|
|
2379
|
+
* @param object obj The chart object
|
|
2380
|
+
*/
|
|
2381
|
+
RG.hasTooltips = function (obj)
|
|
2382
|
+
{
|
|
2383
|
+
var prop = obj.properties;
|
|
2384
|
+
|
|
2385
|
+
if (typeof prop['chart.tooltips'] == 'object' && prop['chart.tooltips']) {
|
|
2386
|
+
for (var i=0,len=prop['chart.tooltips'].length; i<len; ++i) {
|
|
2387
|
+
if (!RG.is_null(obj.Get('chart.tooltips')[i])) {
|
|
2388
|
+
return true;
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
} else if (typeof prop['chart.tooltips'] === 'function') {
|
|
2392
|
+
return true;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
return false;
|
|
2396
|
+
};
|
|
2397
|
+
|
|
2398
|
+
|
|
2399
|
+
|
|
2400
|
+
|
|
2401
|
+
/**
|
|
2402
|
+
* This function creates a (G)UID which can be used to identify objects.
|
|
2403
|
+
*
|
|
2404
|
+
* @return string (g)uid The (G)UID
|
|
2405
|
+
*/
|
|
2406
|
+
RG.createUID =
|
|
2407
|
+
RG.CreateUID = function ()
|
|
2408
|
+
{
|
|
2409
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
|
|
2410
|
+
{
|
|
2411
|
+
var r = ma.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
|
2412
|
+
return v.toString(16);
|
|
2413
|
+
});
|
|
2414
|
+
};
|
|
2415
|
+
|
|
2416
|
+
|
|
2417
|
+
|
|
2418
|
+
/**
|
|
2419
|
+
* This is the new object registry, used to facilitate multiple objects per canvas.
|
|
2420
|
+
*
|
|
2421
|
+
* @param object obj The object to register
|
|
2422
|
+
*/
|
|
2423
|
+
RG.ObjectRegistry.add =
|
|
2424
|
+
RG.ObjectRegistry.Add = function (obj)
|
|
2425
|
+
{
|
|
2426
|
+
var uid = obj.uid;
|
|
2427
|
+
var id = obj.canvas.id;
|
|
2428
|
+
|
|
2429
|
+
/**
|
|
2430
|
+
* Index the objects by UID
|
|
2431
|
+
*/
|
|
2432
|
+
RG.ObjectRegistry.objects.byUID.push([uid, obj]);
|
|
2433
|
+
|
|
2434
|
+
/**
|
|
2435
|
+
* Index the objects by the canvas that they're drawn on
|
|
2436
|
+
*/
|
|
2437
|
+
RG.ObjectRegistry.objects.byCanvasID.push([id, obj]);
|
|
2438
|
+
};
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
|
|
2442
|
+
|
|
2443
|
+
/**
|
|
2444
|
+
* Remove an object from the object registry
|
|
2445
|
+
*
|
|
2446
|
+
* @param object obj The object to remove.
|
|
2447
|
+
*/
|
|
2448
|
+
RG.ObjectRegistry.remove =
|
|
2449
|
+
RG.ObjectRegistry.Remove = function (obj)
|
|
2450
|
+
{
|
|
2451
|
+
var id = obj.id;
|
|
2452
|
+
var uid = obj.uid;
|
|
2453
|
+
|
|
2454
|
+
for (var i=0; i<RG.ObjectRegistry.objects.byUID.length; ++i) {
|
|
2455
|
+
if (RG.ObjectRegistry.objects.byUID[i] && RG.ObjectRegistry.objects.byUID[i][1].uid == uid) {
|
|
2456
|
+
RG.ObjectRegistry.objects.byUID[i] = null;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
for (var i=0; i<RG.ObjectRegistry.objects.byCanvasID.length; ++i) {
|
|
2462
|
+
if ( RG.ObjectRegistry.objects.byCanvasID[i]
|
|
2463
|
+
&& RG.ObjectRegistry.objects.byCanvasID[i][1]
|
|
2464
|
+
&& RG.ObjectRegistry.objects.byCanvasID[i][1].uid == uid) {
|
|
2465
|
+
|
|
2466
|
+
RG.ObjectRegistry.objects.byCanvasID[i] = null;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
|
|
2473
|
+
|
|
2474
|
+
/**
|
|
2475
|
+
* Removes all objects from the ObjectRegistry. If either the ID of a canvas is supplied,
|
|
2476
|
+
* or the canvas itself, then only objects pertaining to that canvas are cleared.
|
|
2477
|
+
*
|
|
2478
|
+
* @param mixed Either a canvas object (as returned by document.getElementById()
|
|
2479
|
+
* or the ID of a canvas (ie a string)
|
|
2480
|
+
*/
|
|
2481
|
+
RG.ObjectRegistry.clear =
|
|
2482
|
+
RG.ObjectRegistry.Clear = function ()
|
|
2483
|
+
{
|
|
2484
|
+
// If an ID is supplied restrict the learing to that
|
|
2485
|
+
if (arguments[0]) {
|
|
2486
|
+
var id = (typeof arguments[0] === 'object' ? arguments[0].id : arguments[0]);
|
|
2487
|
+
var objects = RG.ObjectRegistry.getObjectsByCanvasID(id);
|
|
2488
|
+
|
|
2489
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2490
|
+
RG.ObjectRegistry.remove(objects[i]);
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
} else {
|
|
2494
|
+
|
|
2495
|
+
RG.ObjectRegistry.objects = {};
|
|
2496
|
+
RG.ObjectRegistry.objects.byUID = [];
|
|
2497
|
+
RG.ObjectRegistry.objects.byCanvasID = [];
|
|
2498
|
+
}
|
|
2499
|
+
};
|
|
2500
|
+
|
|
2501
|
+
|
|
2502
|
+
|
|
2503
|
+
|
|
2504
|
+
/**
|
|
2505
|
+
* Lists all objects in the ObjectRegistry
|
|
2506
|
+
*
|
|
2507
|
+
* @param boolean ret Whether to return the list or alert() it
|
|
2508
|
+
*/
|
|
2509
|
+
RGraph.ObjectRegistry.list =
|
|
2510
|
+
RGraph.ObjectRegistry.List = function ()
|
|
2511
|
+
{
|
|
2512
|
+
var list = [];
|
|
2513
|
+
|
|
2514
|
+
for (var i=0,len=RG.ObjectRegistry.objects.byUID.length; i<len; ++i) {
|
|
2515
|
+
if (RG.ObjectRegistry.objects.byUID[i]) {
|
|
2516
|
+
list.push(RG.ObjectRegistry.objects.byUID[i][1].type);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
if (arguments[0]) {
|
|
2521
|
+
return list;
|
|
2522
|
+
} else {
|
|
2523
|
+
p(list);
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
|
|
2530
|
+
/**
|
|
2531
|
+
* Clears the ObjectRegistry of objects that are of a certain given type
|
|
2532
|
+
*
|
|
2533
|
+
* @param type string The type to clear
|
|
2534
|
+
*/
|
|
2535
|
+
RG.ObjectRegistry.clearByType =
|
|
2536
|
+
RG.ObjectRegistry.ClearByType = function (type)
|
|
2537
|
+
{
|
|
2538
|
+
var objects = RG.ObjectRegistry.objects.byUID;
|
|
2539
|
+
|
|
2540
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2541
|
+
if (objects[i]) {
|
|
2542
|
+
var uid = objects[i][0];
|
|
2543
|
+
var obj = objects[i][1];
|
|
2544
|
+
|
|
2545
|
+
if (obj && obj.type == type) {
|
|
2546
|
+
RG.ObjectRegistry.remove(obj);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
};
|
|
2551
|
+
|
|
2552
|
+
|
|
2553
|
+
|
|
2554
|
+
|
|
2555
|
+
/**
|
|
2556
|
+
* This function provides an easy way to go through all of the objects that are held in the
|
|
2557
|
+
* Registry
|
|
2558
|
+
*
|
|
2559
|
+
* @param func function This function is run for every object. Its passed the object as an argument
|
|
2560
|
+
* @param string type Optionally, you can pass a type of object to look for
|
|
2561
|
+
*/
|
|
2562
|
+
RG.ObjectRegistry.iterate =
|
|
2563
|
+
RG.ObjectRegistry.Iterate = function (func)
|
|
2564
|
+
{
|
|
2565
|
+
var objects = RGraph.ObjectRegistry.objects.byUID;
|
|
2566
|
+
|
|
2567
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2568
|
+
|
|
2569
|
+
if (typeof arguments[1] === 'string') {
|
|
2570
|
+
|
|
2571
|
+
var types = arguments[1].split(/,/);
|
|
2572
|
+
|
|
2573
|
+
for (var j=0,len2=types.length; j<len2; ++j) {
|
|
2574
|
+
if (types[j] == objects[i][1].type) {
|
|
2575
|
+
func(objects[i][1]);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
} else {
|
|
2579
|
+
func(objects[i][1]);
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
|
|
2587
|
+
/**
|
|
2588
|
+
* Retrieves all objects for a given canvas id
|
|
2589
|
+
*
|
|
2590
|
+
* @patarm id string The canvas ID to get objects for.
|
|
2591
|
+
*/
|
|
2592
|
+
RG.ObjectRegistry.getObjectsByCanvasID = function (id)
|
|
2593
|
+
{
|
|
2594
|
+
var store = RG.ObjectRegistry.objects.byCanvasID;
|
|
2595
|
+
var ret = [];
|
|
2596
|
+
|
|
2597
|
+
// Loop through all of the objects and return the appropriate ones
|
|
2598
|
+
for (var i=0,len=store.length; i<len; ++i) {
|
|
2599
|
+
if (store[i] && store[i][0] == id ) {
|
|
2600
|
+
ret.push(store[i][1]);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
return ret;
|
|
2605
|
+
};
|
|
2606
|
+
|
|
2607
|
+
|
|
2608
|
+
|
|
2609
|
+
|
|
2610
|
+
/**
|
|
2611
|
+
* Retrieves the relevant object based on the X/Y position.
|
|
2612
|
+
*
|
|
2613
|
+
* @param object e The event object
|
|
2614
|
+
* @return object The applicable (if any) object
|
|
2615
|
+
*/
|
|
2616
|
+
RG.ObjectRegistry.getFirstObjectByXY =
|
|
2617
|
+
RG.ObjectRegistry.getObjectByXY = function (e)
|
|
2618
|
+
{
|
|
2619
|
+
var canvas = e.target;
|
|
2620
|
+
var ret = null;
|
|
2621
|
+
var objects = RG.ObjectRegistry.getObjectsByCanvasID(canvas.id);
|
|
2622
|
+
|
|
2623
|
+
for (var i=(objects.length - 1); i>=0; --i) {
|
|
2624
|
+
|
|
2625
|
+
var obj = objects[i].getObjectByXY(e);
|
|
2626
|
+
|
|
2627
|
+
if (obj) {
|
|
2628
|
+
return obj;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
|
|
2633
|
+
|
|
2634
|
+
|
|
2635
|
+
|
|
2636
|
+
/**
|
|
2637
|
+
* Retrieves the relevant objects based on the X/Y position.
|
|
2638
|
+
* NOTE This function returns an array of objects
|
|
2639
|
+
*
|
|
2640
|
+
* @param object e The event object
|
|
2641
|
+
* @return An array of pertinent objects. Note the there may be only one object
|
|
2642
|
+
*/
|
|
2643
|
+
RG.ObjectRegistry.getObjectsByXY = function (e)
|
|
2644
|
+
{
|
|
2645
|
+
var canvas = e.target;
|
|
2646
|
+
var ret = [];
|
|
2647
|
+
var objects = RG.ObjectRegistry.getObjectsByCanvasID(canvas.id);
|
|
2648
|
+
|
|
2649
|
+
// Retrieve objects "front to back"
|
|
2650
|
+
for (var i=(objects.length - 1); i>=0; --i) {
|
|
2651
|
+
|
|
2652
|
+
var obj = objects[i].getObjectByXY(e);
|
|
2653
|
+
|
|
2654
|
+
if (obj) {
|
|
2655
|
+
ret.push(obj);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
return ret;
|
|
2660
|
+
};
|
|
2661
|
+
|
|
2662
|
+
|
|
2663
|
+
|
|
2664
|
+
|
|
2665
|
+
/**
|
|
2666
|
+
* Retrieves the object with the corresponding UID
|
|
2667
|
+
*
|
|
2668
|
+
* @param string uid The UID to get the relevant object for
|
|
2669
|
+
*/
|
|
2670
|
+
RG.ObjectRegistry.getObjectByUID = function (uid)
|
|
2671
|
+
{
|
|
2672
|
+
var objects = RG.ObjectRegistry.objects.byUID;
|
|
2673
|
+
|
|
2674
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2675
|
+
if (objects[i] && objects[i][1].uid == uid) {
|
|
2676
|
+
return objects[i][1];
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
};
|
|
2680
|
+
|
|
2681
|
+
|
|
2682
|
+
|
|
2683
|
+
|
|
2684
|
+
/**
|
|
2685
|
+
* Brings a chart to the front of the ObjectRegistry by
|
|
2686
|
+
* removing it and then readding it at the end and then
|
|
2687
|
+
* redrawing the canvas
|
|
2688
|
+
*
|
|
2689
|
+
* @param object obj The object to bring to the front
|
|
2690
|
+
* @param boolean redraw Whether to redraw the canvas after the
|
|
2691
|
+
* object has been moved
|
|
2692
|
+
*/
|
|
2693
|
+
RG.ObjectRegistry.bringToFront = function (obj)
|
|
2694
|
+
{
|
|
2695
|
+
var redraw = typeof arguments[1] === 'undefined' ? true : arguments[1];
|
|
2696
|
+
|
|
2697
|
+
RG.ObjectRegistry.remove(obj);
|
|
2698
|
+
RG.ObjectRegistry.add(obj);
|
|
2699
|
+
|
|
2700
|
+
if (redraw) {
|
|
2701
|
+
RG.redrawCanvas(obj.canvas);
|
|
2702
|
+
}
|
|
2703
|
+
};
|
|
2704
|
+
|
|
2705
|
+
|
|
2706
|
+
|
|
2707
|
+
|
|
2708
|
+
/**
|
|
2709
|
+
* Retrieves the objects that are the given type
|
|
2710
|
+
*
|
|
2711
|
+
* @param mixed canvas The canvas to check. It can either be the canvas object itself or just the ID
|
|
2712
|
+
* @param string type The type to look for
|
|
2713
|
+
* @return array An array of one or more objects
|
|
2714
|
+
*/
|
|
2715
|
+
RG.ObjectRegistry.getObjectsByType = function (type)
|
|
2716
|
+
{
|
|
2717
|
+
var objects = RG.ObjectRegistry.objects.byUID;
|
|
2718
|
+
var ret = [];
|
|
2719
|
+
|
|
2720
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2721
|
+
|
|
2722
|
+
if (objects[i] && objects[i][1] && objects[i][1].type && objects[i][1].type && objects[i][1].type == type) {
|
|
2723
|
+
ret.push(objects[i][1]);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
return ret;
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2730
|
+
|
|
2731
|
+
|
|
2732
|
+
|
|
2733
|
+
/**
|
|
2734
|
+
* Retrieves the FIRST object that matches the given type
|
|
2735
|
+
*
|
|
2736
|
+
* @param string type The type of object to look for
|
|
2737
|
+
* @return object The FIRST object that matches the given type
|
|
2738
|
+
*/
|
|
2739
|
+
RG.ObjectRegistry.getFirstObjectByType = function (type)
|
|
2740
|
+
{
|
|
2741
|
+
var objects = RG.ObjectRegistry.objects.byUID;
|
|
2742
|
+
|
|
2743
|
+
for (var i=0,len=objects.length; i<len; ++i) {
|
|
2744
|
+
if (objects[i] && objects[i][1] && objects[i][1].type == type) {
|
|
2745
|
+
return objects[i][1];
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
return null;
|
|
2750
|
+
};
|
|
2751
|
+
|
|
2752
|
+
|
|
2753
|
+
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* This takes centerx, centery, x and y coordinates and returns the
|
|
2757
|
+
* appropriate angle relative to the canvas angle system. Remember
|
|
2758
|
+
* that the canvas angle system starts at the EAST axis
|
|
2759
|
+
*
|
|
2760
|
+
* @param number cx The centerx coordinate
|
|
2761
|
+
* @param number cy The centery coordinate
|
|
2762
|
+
* @param number x The X coordinate (eg the mouseX if coming from a click)
|
|
2763
|
+
* @param number y The Y coordinate (eg the mouseY if coming from a click)
|
|
2764
|
+
* @return number The relevant angle (measured in in RADIANS)
|
|
2765
|
+
*/
|
|
2766
|
+
RG.getAngleByXY = function (cx, cy, x, y)
|
|
2767
|
+
{
|
|
2768
|
+
var angle = ma.atan((y - cy) / (x - cx));
|
|
2769
|
+
angle = ma.abs(angle)
|
|
2770
|
+
|
|
2771
|
+
if (x >= cx && y >= cy) {
|
|
2772
|
+
angle += RG.TWOPI;
|
|
2773
|
+
|
|
2774
|
+
} else if (x >= cx && y < cy) {
|
|
2775
|
+
angle = (RG.HALFPI - angle) + (RG.PI + RG.HALFPI);
|
|
2776
|
+
|
|
2777
|
+
} else if (x < cx && y < cy) {
|
|
2778
|
+
angle += RG.PI;
|
|
2779
|
+
|
|
2780
|
+
} else {
|
|
2781
|
+
angle = RG.PI - angle;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
/**
|
|
2785
|
+
* Upper and lower limit checking
|
|
2786
|
+
*/
|
|
2787
|
+
if (angle > RG.TWOPI) {
|
|
2788
|
+
angle -= RG.TWOPI;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
return angle;
|
|
2792
|
+
};
|
|
2793
|
+
|
|
2794
|
+
|
|
2795
|
+
|
|
2796
|
+
|
|
2797
|
+
/**
|
|
2798
|
+
* This function returns the distance between two points. In effect the
|
|
2799
|
+
* radius of an imaginary circle that is centered on x1 and y1. The name
|
|
2800
|
+
* of this function is derived from the word "Hypoteneuse", which in
|
|
2801
|
+
* trigonmetry is the longest side of a triangle
|
|
2802
|
+
*
|
|
2803
|
+
* @param number x1 The original X coordinate
|
|
2804
|
+
* @param number y1 The original Y coordinate
|
|
2805
|
+
* @param number x2 The target X coordinate
|
|
2806
|
+
* @param number y2 The target Y coordinate
|
|
2807
|
+
*/
|
|
2808
|
+
RG.getHypLength = function (x1, y1, x2, y2)
|
|
2809
|
+
{
|
|
2810
|
+
var ret = ma.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
|
|
2811
|
+
|
|
2812
|
+
return ret;
|
|
2813
|
+
};
|
|
2814
|
+
|
|
2815
|
+
|
|
2816
|
+
|
|
2817
|
+
|
|
2818
|
+
/**
|
|
2819
|
+
* This function gets the end point (X/Y coordinates) of a given radius.
|
|
2820
|
+
* You pass it the center X/Y and the radius and this function will return
|
|
2821
|
+
* the endpoint X/Y coordinates.
|
|
2822
|
+
*
|
|
2823
|
+
* @param number cx The center X coord
|
|
2824
|
+
* @param number cy The center Y coord
|
|
2825
|
+
* @param number r The lrngth of the radius
|
|
2826
|
+
*/
|
|
2827
|
+
RG.getRadiusEndPoint = function (cx, cy, angle, radius)
|
|
2828
|
+
{
|
|
2829
|
+
var x = cx + (ma.cos(angle) * radius);
|
|
2830
|
+
var y = cy + (ma.sin(angle) * radius);
|
|
2831
|
+
|
|
2832
|
+
return [x, y];
|
|
2833
|
+
};
|
|
2834
|
+
|
|
2835
|
+
|
|
2836
|
+
|
|
2837
|
+
|
|
2838
|
+
/**
|
|
2839
|
+
* This installs all of the event listeners
|
|
2840
|
+
*
|
|
2841
|
+
* @param object obj The chart object
|
|
2842
|
+
*/
|
|
2843
|
+
RG.installEventListeners =
|
|
2844
|
+
RG.InstallEventListeners = function (obj)
|
|
2845
|
+
{
|
|
2846
|
+
var prop = obj.properties;
|
|
2847
|
+
|
|
2848
|
+
/**
|
|
2849
|
+
* Don't attempt to install event listeners for older versions of MSIE
|
|
2850
|
+
*/
|
|
2851
|
+
if (RG.ISOLD) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
/**
|
|
2856
|
+
* If this function exists, then the dynamic file has been included.
|
|
2857
|
+
*/
|
|
2858
|
+
if (RG.installCanvasClickListener) {
|
|
2859
|
+
|
|
2860
|
+
RG.installWindowMousedownListener(obj);
|
|
2861
|
+
RG.installWindowMouseupListener(obj);
|
|
2862
|
+
RG.installCanvasMousemoveListener(obj);
|
|
2863
|
+
RG.installCanvasMouseupListener(obj);
|
|
2864
|
+
RG.installCanvasMousedownListener(obj);
|
|
2865
|
+
RG.installCanvasClickListener(obj);
|
|
2866
|
+
|
|
2867
|
+
} else if ( RG.hasTooltips(obj)
|
|
2868
|
+
|| prop['chart.adjustable']
|
|
2869
|
+
|| prop['chart.annotatable']
|
|
2870
|
+
|| prop['chart.contextmenu']
|
|
2871
|
+
|| prop['chart.resizable']
|
|
2872
|
+
|| prop['chart.key.interactive']
|
|
2873
|
+
|| prop['chart.events.click']
|
|
2874
|
+
|| prop['chart.events.mousemove']
|
|
2875
|
+
|| typeof obj.onclick === 'function'
|
|
2876
|
+
|| typeof obj.onmousemove === 'function'
|
|
2877
|
+
) {
|
|
2878
|
+
|
|
2879
|
+
alert('[RGRAPH] You appear to have used dynamic features but not included the file: RGraph.common.dynamic.js');
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
|
|
2883
|
+
|
|
2884
|
+
|
|
2885
|
+
|
|
2886
|
+
/**
|
|
2887
|
+
* Loosly mimicks the PHP function print_r();
|
|
2888
|
+
*/
|
|
2889
|
+
RG.pr = function (obj)
|
|
2890
|
+
{
|
|
2891
|
+
var indent = (arguments[2] ? arguments[2] : ' ');
|
|
2892
|
+
var str = '';
|
|
2893
|
+
|
|
2894
|
+
var counter = typeof arguments[3] == 'number' ? arguments[3] : 0;
|
|
2895
|
+
|
|
2896
|
+
if (counter >= 5) {
|
|
2897
|
+
return '';
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
switch (typeof obj) {
|
|
2901
|
+
|
|
2902
|
+
case 'string': str += obj + ' (' + (typeof obj) + ', ' + obj.length + ')'; break;
|
|
2903
|
+
case 'number': str += obj + ' (' + (typeof obj) + ')'; break;
|
|
2904
|
+
case 'boolean': str += obj + ' (' + (typeof obj) + ')'; break;
|
|
2905
|
+
case 'function': str += 'function () {}'; break;
|
|
2906
|
+
case 'undefined': str += 'undefined'; break;
|
|
2907
|
+
case 'null': str += 'null'; break;
|
|
2908
|
+
|
|
2909
|
+
case 'object':
|
|
2910
|
+
// In case of null
|
|
2911
|
+
if (RGraph.is_null(obj)) {
|
|
2912
|
+
str += indent + 'null\n';
|
|
2913
|
+
} else {
|
|
2914
|
+
str += indent + 'Object {' + '\n'
|
|
2915
|
+
for (j in obj) {
|
|
2916
|
+
str += indent + ' ' + j + ' => ' + RGraph.pr(obj[j], true, indent + ' ', counter + 1) + '\n';
|
|
2917
|
+
}
|
|
2918
|
+
str += indent + '}';
|
|
2919
|
+
}
|
|
2920
|
+
break;
|
|
2921
|
+
|
|
2922
|
+
|
|
2923
|
+
default:
|
|
2924
|
+
str += 'Unknown type: ' + typeof obj + '';
|
|
2925
|
+
break;
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
|
|
2929
|
+
/**
|
|
2930
|
+
* Finished, now either return if we're in a recursed call, or alert()
|
|
2931
|
+
* if we're not.
|
|
2932
|
+
*/
|
|
2933
|
+
if (!arguments[1]) {
|
|
2934
|
+
alert(str);
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
return str;
|
|
2938
|
+
};
|
|
2939
|
+
|
|
2940
|
+
|
|
2941
|
+
|
|
2942
|
+
|
|
2943
|
+
/**
|
|
2944
|
+
* Produces a dashed line
|
|
2945
|
+
*
|
|
2946
|
+
* @param object co The 2D context
|
|
2947
|
+
* @param number x1 The start X coordinate
|
|
2948
|
+
* @param number y1 The start Y coordinate
|
|
2949
|
+
* @param number x2 The end X coordinate
|
|
2950
|
+
* @param number y2 The end Y coordinate
|
|
2951
|
+
*/
|
|
2952
|
+
RG.dashedLine =
|
|
2953
|
+
RG.DashedLine = function(co, x1, y1, x2, y2)
|
|
2954
|
+
{
|
|
2955
|
+
/**
|
|
2956
|
+
* This is the size of the dashes
|
|
2957
|
+
*/
|
|
2958
|
+
var size = 5;
|
|
2959
|
+
|
|
2960
|
+
/**
|
|
2961
|
+
* The optional fifth argument can be the size of the dashes
|
|
2962
|
+
*/
|
|
2963
|
+
if (typeof arguments[5] === 'number') {
|
|
2964
|
+
size = arguments[5];
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
var dx = x2 - x1;
|
|
2968
|
+
var dy = y2 - y1;
|
|
2969
|
+
var num = ma.floor(ma.sqrt((dx * dx) + (dy * dy)) / size);
|
|
2970
|
+
|
|
2971
|
+
var xLen = dx / num;
|
|
2972
|
+
var yLen = dy / num;
|
|
2973
|
+
|
|
2974
|
+
var count = 0;
|
|
2975
|
+
|
|
2976
|
+
do {
|
|
2977
|
+
(count % 2 == 0 && count > 0) ? co.lineTo(x1, y1) : co.moveTo(x1, y1);
|
|
2978
|
+
|
|
2979
|
+
x1 += xLen;
|
|
2980
|
+
y1 += yLen;
|
|
2981
|
+
} while(count++ <= num);
|
|
2982
|
+
};
|
|
2983
|
+
|
|
2984
|
+
|
|
2985
|
+
|
|
2986
|
+
|
|
2987
|
+
/**
|
|
2988
|
+
* Makes an AJAX call. It calls the given callback (a function) when ready
|
|
2989
|
+
*
|
|
2990
|
+
* @param string url The URL to retrieve
|
|
2991
|
+
* @param function callback A function that is called when the response is ready, there's an example below
|
|
2992
|
+
* called "myCallback".
|
|
2993
|
+
*/
|
|
2994
|
+
RG.AJAX = function (url, callback)
|
|
2995
|
+
{
|
|
2996
|
+
// Mozilla, Safari, ...
|
|
2997
|
+
if (window.XMLHttpRequest) {
|
|
2998
|
+
var httpRequest = new XMLHttpRequest();
|
|
2999
|
+
|
|
3000
|
+
// MSIE
|
|
3001
|
+
} else if (window.ActiveXObject) {
|
|
3002
|
+
var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
httpRequest.onreadystatechange = function ()
|
|
3006
|
+
{
|
|
3007
|
+
if (this.readyState == 4 && this.status == 200) {
|
|
3008
|
+
this.__user_callback__ = callback;
|
|
3009
|
+
this.__user_callback__(this.responseText);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
httpRequest.open('GET', url, true);
|
|
3014
|
+
httpRequest.send();
|
|
3015
|
+
};
|
|
3016
|
+
|
|
3017
|
+
|
|
3018
|
+
|
|
3019
|
+
|
|
3020
|
+
/**
|
|
3021
|
+
* Makes an AJAX POST request. It calls the given callback (a function) when ready
|
|
3022
|
+
*
|
|
3023
|
+
* @param string url The URL to retrieve
|
|
3024
|
+
* @param object data The POST data
|
|
3025
|
+
* @param function callback A function that is called when the response is ready, there's an example below
|
|
3026
|
+
* called "myCallback".
|
|
3027
|
+
*/
|
|
3028
|
+
RG.AJAX.POST = function (url, data, callback)
|
|
3029
|
+
{
|
|
3030
|
+
// Used when building the POST string
|
|
3031
|
+
var crumbs = [];
|
|
3032
|
+
|
|
3033
|
+
|
|
3034
|
+
|
|
3035
|
+
|
|
3036
|
+
|
|
3037
|
+
|
|
3038
|
+
// Mozilla, Safari, ...
|
|
3039
|
+
if (window.XMLHttpRequest) {
|
|
3040
|
+
var httpRequest = new XMLHttpRequest();
|
|
3041
|
+
|
|
3042
|
+
// MSIE
|
|
3043
|
+
} else if (window.ActiveXObject) {
|
|
3044
|
+
var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
|
|
3048
|
+
|
|
3049
|
+
|
|
3050
|
+
|
|
3051
|
+
httpRequest.onreadystatechange = function ()
|
|
3052
|
+
{
|
|
3053
|
+
if (this.readyState == 4 && this.status == 200) {
|
|
3054
|
+
this.__user_callback__ = callback;
|
|
3055
|
+
this.__user_callback__(this.responseText);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
httpRequest.open('POST', url, true);
|
|
3060
|
+
httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
|
3061
|
+
|
|
3062
|
+
for (i in data) {
|
|
3063
|
+
if (typeof i == 'string') {
|
|
3064
|
+
crumbs.push(i + '=' + encodeURIComponent(data[i]));
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
httpRequest.send(crumbs.join('&'));
|
|
3069
|
+
};
|
|
3070
|
+
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
|
|
3074
|
+
/**
|
|
3075
|
+
* Uses the above function but calls the call back passing a number as its argument
|
|
3076
|
+
*
|
|
3077
|
+
* @param url string The URL to fetch
|
|
3078
|
+
* @param callback function Your callback function (which is passed the number as an argument)
|
|
3079
|
+
*/
|
|
3080
|
+
RG.AJAX.getNumber = function (url, callback)
|
|
3081
|
+
{
|
|
3082
|
+
RG.AJAX(url, function ()
|
|
3083
|
+
{
|
|
3084
|
+
var num = parseFloat(this.responseText);
|
|
3085
|
+
|
|
3086
|
+
callback(num);
|
|
3087
|
+
});
|
|
3088
|
+
};
|
|
3089
|
+
|
|
3090
|
+
|
|
3091
|
+
|
|
3092
|
+
|
|
3093
|
+
/**
|
|
3094
|
+
* Uses the above function but calls the call back passing a string as its argument
|
|
3095
|
+
*
|
|
3096
|
+
* @param url string The URL to fetch
|
|
3097
|
+
* @param callback function Your callback function (which is passed the string as an argument)
|
|
3098
|
+
*/
|
|
3099
|
+
RG.AJAX.getString = function (url, callback)
|
|
3100
|
+
{
|
|
3101
|
+
RG.AJAX(url, function ()
|
|
3102
|
+
{
|
|
3103
|
+
var str = String(this.responseText);
|
|
3104
|
+
|
|
3105
|
+
callback(str);
|
|
3106
|
+
});
|
|
3107
|
+
};
|
|
3108
|
+
|
|
3109
|
+
|
|
3110
|
+
|
|
3111
|
+
|
|
3112
|
+
/**
|
|
3113
|
+
* Uses the above function but calls the call back passing JSON (ie a JavaScript object ) as its argument
|
|
3114
|
+
*
|
|
3115
|
+
* @param url string The URL to fetch
|
|
3116
|
+
* @param callback function Your callback function (which is passed the JSON object as an argument)
|
|
3117
|
+
*/
|
|
3118
|
+
RG.AJAX.getJSON = function (url, callback)
|
|
3119
|
+
{
|
|
3120
|
+
RG.AJAX(url, function ()
|
|
3121
|
+
{
|
|
3122
|
+
var json = eval('(' + this.responseText + ')');
|
|
3123
|
+
|
|
3124
|
+
callback(json);
|
|
3125
|
+
});
|
|
3126
|
+
};
|
|
3127
|
+
|
|
3128
|
+
|
|
3129
|
+
|
|
3130
|
+
|
|
3131
|
+
/**
|
|
3132
|
+
* Uses the above RGraph.AJAX function but calls the call back passing an array as its argument.
|
|
3133
|
+
* Useful if you're retrieving CSV data
|
|
3134
|
+
*
|
|
3135
|
+
* @param url string The URL to fetch
|
|
3136
|
+
* @param callback function Your callback function (which is passed the CSV/array as an argument)
|
|
3137
|
+
*/
|
|
3138
|
+
RG.AJAX.getCSV = function (url, callback)
|
|
3139
|
+
{
|
|
3140
|
+
var seperator = arguments[2] ? arguments[2] : ',';
|
|
3141
|
+
|
|
3142
|
+
RG.AJAX(url, function ()
|
|
3143
|
+
{
|
|
3144
|
+
var regexp = new RegExp(seperator);
|
|
3145
|
+
var arr = this.responseText.split(regexp);
|
|
3146
|
+
|
|
3147
|
+
// Convert the strings to numbers
|
|
3148
|
+
for (var i=0,len=arr.length;i<len;++i) {
|
|
3149
|
+
arr[i] = parseFloat(arr[i]);
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
callback(arr);
|
|
3153
|
+
});
|
|
3154
|
+
};
|
|
3155
|
+
|
|
3156
|
+
|
|
3157
|
+
|
|
3158
|
+
|
|
3159
|
+
/**
|
|
3160
|
+
* Rotates the canvas
|
|
3161
|
+
*
|
|
3162
|
+
* @param object canvas The canvas to rotate
|
|
3163
|
+
* @param int x The X coordinate about which to rotate the canvas
|
|
3164
|
+
* @param int y The Y coordinate about which to rotate the canvas
|
|
3165
|
+
* @param int angle The angle(in RADIANS) to rotate the canvas by
|
|
3166
|
+
*/
|
|
3167
|
+
RG.rotateCanvas =
|
|
3168
|
+
RG.RotateCanvas = function (ca, x, y, angle)
|
|
3169
|
+
{
|
|
3170
|
+
var co = ca.getContext('2d');
|
|
3171
|
+
|
|
3172
|
+
co.translate(x, y);
|
|
3173
|
+
co.rotate(angle);
|
|
3174
|
+
co.translate(0 - x, 0 - y);
|
|
3175
|
+
};
|
|
3176
|
+
|
|
3177
|
+
|
|
3178
|
+
|
|
3179
|
+
|
|
3180
|
+
/**
|
|
3181
|
+
* Measures text by creating a DIV in the document and adding the relevant text to it.
|
|
3182
|
+
* Then checking the .offsetWidth and .offsetHeight.
|
|
3183
|
+
*
|
|
3184
|
+
* @param string text The text to measure
|
|
3185
|
+
* @param bool bold Whether the text is bold or not
|
|
3186
|
+
* @param string font The font to use
|
|
3187
|
+
* @param size number The size of the text (in pts)
|
|
3188
|
+
* @return array A two element array of the width and height of the text
|
|
3189
|
+
*/
|
|
3190
|
+
RG.measureText =
|
|
3191
|
+
RG.MeasureText = function (text, bold, font, size)
|
|
3192
|
+
{
|
|
3193
|
+
// Add the sizes to the cache as adding DOM elements is costly and causes slow downs
|
|
3194
|
+
if (typeof RGraph.measuretext_cache === 'undefined') {
|
|
3195
|
+
RGraph.measuretext_cache = [];
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
var str = text + ':' + bold + ':' + font + ':' + size;
|
|
3199
|
+
if (typeof RGraph.measuretext_cache == 'object' && RGraph.measuretext_cache[str]) {
|
|
3200
|
+
return RGraph.measuretext_cache[str];
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
if (!RGraph.measuretext_cache['text-div']) {
|
|
3204
|
+
var div = document.createElement('DIV');
|
|
3205
|
+
div.style.position = 'absolute';
|
|
3206
|
+
div.style.top = '-100px';
|
|
3207
|
+
div.style.left = '-100px';
|
|
3208
|
+
document.body.appendChild(div);
|
|
3209
|
+
|
|
3210
|
+
// Now store the newly created DIV
|
|
3211
|
+
RGraph.measuretext_cache['text-div'] = div;
|
|
3212
|
+
|
|
3213
|
+
} else if (RGraph.measuretext_cache['text-div']) {
|
|
3214
|
+
var div = RGraph.measuretext_cache['text-div'];
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
div.innerHTML = text.replace(/\r\n/g, '<br />');
|
|
3218
|
+
div.style.fontFamily = font;
|
|
3219
|
+
div.style.fontWeight = bold ? 'bold' : 'normal';
|
|
3220
|
+
div.style.fontSize = (size || 12) + 'pt';
|
|
3221
|
+
|
|
3222
|
+
var size = [div.offsetWidth, div.offsetHeight];
|
|
3223
|
+
|
|
3224
|
+
//document.body.removeChild(div);
|
|
3225
|
+
RGraph.measuretext_cache[str] = size;
|
|
3226
|
+
|
|
3227
|
+
return size;
|
|
3228
|
+
};
|
|
3229
|
+
|
|
3230
|
+
|
|
3231
|
+
|
|
3232
|
+
|
|
3233
|
+
/* New text function. Accepts two arguments:
|
|
3234
|
+
* o obj - The chart object
|
|
3235
|
+
* o opt - An object/hash/map of properties. This can consist of:
|
|
3236
|
+
* x The X coordinate (REQUIRED)
|
|
3237
|
+
* y The Y coordinate (REQUIRED)
|
|
3238
|
+
* text The text to show (REQUIRED)
|
|
3239
|
+
* font The font to use
|
|
3240
|
+
* size The size of the text (in pt)
|
|
3241
|
+
* italic Whether the text should be italic or not
|
|
3242
|
+
* bold Whether the text shouldd be bold or not
|
|
3243
|
+
* marker Whether to show a marker that indicates the X/Y coordinates
|
|
3244
|
+
* valign The vertical alignment
|
|
3245
|
+
* halign The horizontal alignment
|
|
3246
|
+
* bounding Whether to draw a bounding box for the text
|
|
3247
|
+
* boundingStroke The strokeStyle of the bounding box
|
|
3248
|
+
* boundingFill The fillStyle of the bounding box
|
|
3249
|
+
*/
|
|
3250
|
+
RG.text2 =
|
|
3251
|
+
RG.Text2 = function (obj, opt)
|
|
3252
|
+
{
|
|
3253
|
+
/**
|
|
3254
|
+
* An RGraph object can be given, or a string or the 2D rendering context
|
|
3255
|
+
* The coords are placed on the obj.coordsText variable ONLY if it's an RGraph object. The function
|
|
3256
|
+
* still returns the cooords though in all cases.
|
|
3257
|
+
*/
|
|
3258
|
+
if (obj && obj.isRGraph) {
|
|
3259
|
+
var obj = obj;
|
|
3260
|
+
var co = obj.context;
|
|
3261
|
+
var ca = obj.canvas;
|
|
3262
|
+
} else if (typeof obj == 'string') {
|
|
3263
|
+
var ca = document.getElementById(obj);
|
|
3264
|
+
var co = ca.getContext('2d');
|
|
3265
|
+
var obj = ca.__object__;
|
|
3266
|
+
} else if (typeof obj.getContext === 'function') {
|
|
3267
|
+
var ca = obj;
|
|
3268
|
+
var co = ca.getContext('2d');
|
|
3269
|
+
var obj = ca.__object__;
|
|
3270
|
+
} else if (obj.toString().indexOf('CanvasRenderingContext2D') != -1 || RGraph.ISIE8 && obj.moveTo) {
|
|
3271
|
+
var co = obj;
|
|
3272
|
+
var ca = obj.canvas;
|
|
3273
|
+
var obj = ca.__object__;
|
|
3274
|
+
|
|
3275
|
+
// IE7/8
|
|
3276
|
+
} else if (RG.ISOLD && obj.fillText) {
|
|
3277
|
+
var co = obj;
|
|
3278
|
+
var ca = obj.canvas;
|
|
3279
|
+
var obj = ca.__object__;
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
var x = opt.x;
|
|
3283
|
+
var y = opt.y;
|
|
3284
|
+
var originalX = x;
|
|
3285
|
+
var originalY = y;
|
|
3286
|
+
var text = opt.text;
|
|
3287
|
+
var text_multiline = typeof text === 'string' ? text.split(/\r?\n/g) : '';
|
|
3288
|
+
var numlines = text_multiline.length;
|
|
3289
|
+
var font = opt.font ? opt.font : 'Arial';
|
|
3290
|
+
var size = opt.size ? opt.size : 10;
|
|
3291
|
+
var size_pixels = size * 1.5;
|
|
3292
|
+
var bold = opt.bold;
|
|
3293
|
+
var italic = opt.italic;
|
|
3294
|
+
var halign = opt.halign ? opt.halign : 'left';
|
|
3295
|
+
var valign = opt.valign ? opt.valign : 'bottom';
|
|
3296
|
+
var tag = typeof opt.tag == 'string' && opt.tag.length > 0 ? opt.tag : '';
|
|
3297
|
+
var marker = opt.marker;
|
|
3298
|
+
var angle = opt.angle || 0;
|
|
3299
|
+
|
|
3300
|
+
|
|
3301
|
+
|
|
3302
|
+
|
|
3303
|
+
|
|
3304
|
+
|
|
3305
|
+
|
|
3306
|
+
|
|
3307
|
+
|
|
3308
|
+
|
|
3309
|
+
|
|
3310
|
+
|
|
3311
|
+
|
|
3312
|
+
|
|
3313
|
+
|
|
3314
|
+
|
|
3315
|
+
|
|
3316
|
+
|
|
3317
|
+
|
|
3318
|
+
|
|
3319
|
+
|
|
3320
|
+
|
|
3321
|
+
|
|
3322
|
+
|
|
3323
|
+
/**
|
|
3324
|
+
* Changed the name of boundingFill/boundingStroke - this allows you to still use those names
|
|
3325
|
+
*/
|
|
3326
|
+
if (typeof opt.boundingFill === 'string') opt['bounding.fill'] = opt.boundingFill;
|
|
3327
|
+
if (typeof opt.boundingStroke === 'string') opt['bounding.stroke'] = opt.boundingStroke;
|
|
3328
|
+
|
|
3329
|
+
var bounding = opt.bounding;
|
|
3330
|
+
var bounding_stroke = opt['bounding.stroke'] ? opt['bounding.stroke'] : 'black';
|
|
3331
|
+
var bounding_fill = opt['bounding.fill'] ? opt['bounding.fill'] : 'rgba(255,255,255,0.7)';
|
|
3332
|
+
var bounding_shadow = opt['bounding.shadow'];
|
|
3333
|
+
var bounding_shadow_color = opt['bounding.shadow.color'] || '#ccc';
|
|
3334
|
+
var bounding_shadow_blur = opt['bounding.shadow.blur'] || 3;
|
|
3335
|
+
var bounding_shadow_offsetx = opt['bounding.shadow.offsetx'] || 3;
|
|
3336
|
+
var bounding_shadow_offsety = opt['bounding.shadow.offsety'] || 3;
|
|
3337
|
+
var bounding_linewidth = opt['bounding.linewidth'] || 1;
|
|
3338
|
+
|
|
3339
|
+
|
|
3340
|
+
|
|
3341
|
+
/**
|
|
3342
|
+
* Initialize the return value to an empty object
|
|
3343
|
+
*/
|
|
3344
|
+
var ret = {};
|
|
3345
|
+
|
|
3346
|
+
//
|
|
3347
|
+
// Color
|
|
3348
|
+
//
|
|
3349
|
+
if (typeof opt.color === 'string') {
|
|
3350
|
+
var orig_fillstyle = co.fillStyle;
|
|
3351
|
+
co.fillStyle = opt.color;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
|
|
3355
|
+
|
|
3356
|
+
/**
|
|
3357
|
+
* The text arg must be a string or a number
|
|
3358
|
+
*/
|
|
3359
|
+
if (typeof text == 'number') {
|
|
3360
|
+
text = String(text);
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
if (typeof text !== 'string') {
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
|
|
3368
|
+
|
|
3369
|
+
/**
|
|
3370
|
+
* This facilitates vertical text
|
|
3371
|
+
*/
|
|
3372
|
+
if (angle != 0) {
|
|
3373
|
+
co.save();
|
|
3374
|
+
co.translate(x, y);
|
|
3375
|
+
co.rotate((ma.PI / 180) * angle)
|
|
3376
|
+
x = 0;
|
|
3377
|
+
y = 0;
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
|
|
3381
|
+
|
|
3382
|
+
/**
|
|
3383
|
+
* Set the font
|
|
3384
|
+
*/
|
|
3385
|
+
co.font = (opt.italic ? 'italic ' : '') + (opt.bold ? 'bold ' : '') + size + 'pt ' + font;
|
|
3386
|
+
|
|
3387
|
+
|
|
3388
|
+
|
|
3389
|
+
/**
|
|
3390
|
+
* Measure the width/height. This must be done AFTER the font has been set
|
|
3391
|
+
*/
|
|
3392
|
+
var width=0;
|
|
3393
|
+
for (var i=0; i<numlines; ++i) {
|
|
3394
|
+
width = ma.max(width, co.measureText(text_multiline[i]).width);
|
|
3395
|
+
}
|
|
3396
|
+
var height = size_pixels * numlines;
|
|
3397
|
+
|
|
3398
|
+
|
|
3399
|
+
|
|
3400
|
+
|
|
3401
|
+
/**
|
|
3402
|
+
* Accommodate old MSIE 7/8
|
|
3403
|
+
*/
|
|
3404
|
+
//if (document.all && RGraph.ISOLD) {
|
|
3405
|
+
//y += 2;
|
|
3406
|
+
//}
|
|
3407
|
+
|
|
3408
|
+
|
|
3409
|
+
|
|
3410
|
+
/**
|
|
3411
|
+
* If marker is specified draw a marker at the X/Y coordinates
|
|
3412
|
+
*/
|
|
3413
|
+
if (opt.marker) {
|
|
3414
|
+
var marker_size = 10;
|
|
3415
|
+
var strokestyle = co.strokeStyle;
|
|
3416
|
+
co.beginPath();
|
|
3417
|
+
co.strokeStyle = 'red';
|
|
3418
|
+
co.moveTo(x, y - marker_size);
|
|
3419
|
+
co.lineTo(x, y + marker_size);
|
|
3420
|
+
co.moveTo(x - marker_size, y);
|
|
3421
|
+
co.lineTo(x + marker_size, y);
|
|
3422
|
+
co.stroke();
|
|
3423
|
+
co.strokeStyle = strokestyle;
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
|
|
3427
|
+
|
|
3428
|
+
/**
|
|
3429
|
+
* Set the horizontal alignment
|
|
3430
|
+
*/
|
|
3431
|
+
if (halign == 'center') {
|
|
3432
|
+
co.textAlign = 'center';
|
|
3433
|
+
var boundingX = x - 2 - (width / 2);
|
|
3434
|
+
} else if (halign == 'right') {
|
|
3435
|
+
co.textAlign = 'right';
|
|
3436
|
+
var boundingX = x - 2 - width;
|
|
3437
|
+
} else {
|
|
3438
|
+
co.textAlign = 'left';
|
|
3439
|
+
var boundingX = x - 2;
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
|
|
3443
|
+
/**
|
|
3444
|
+
* Set the vertical alignment
|
|
3445
|
+
*/
|
|
3446
|
+
if (valign == 'center') {
|
|
3447
|
+
|
|
3448
|
+
co.textBaseline = 'middle';
|
|
3449
|
+
// Move the text slightly
|
|
3450
|
+
y -= 1;
|
|
3451
|
+
|
|
3452
|
+
y -= ((numlines - 1) / 2) * size_pixels;
|
|
3453
|
+
var boundingY = y - (size_pixels / 2) - 2;
|
|
3454
|
+
|
|
3455
|
+
} else if (valign == 'top') {
|
|
3456
|
+
co.textBaseline = 'top';
|
|
3457
|
+
|
|
3458
|
+
var boundingY = y - 2;
|
|
3459
|
+
|
|
3460
|
+
} else {
|
|
3461
|
+
|
|
3462
|
+
co.textBaseline = 'bottom';
|
|
3463
|
+
|
|
3464
|
+
// Move the Y coord if multiline text
|
|
3465
|
+
if (numlines > 1) {
|
|
3466
|
+
y -= ((numlines - 1) * size_pixels);
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3469
|
+
var boundingY = y - size_pixels - 2;
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
var boundingW = width + 4;
|
|
3473
|
+
var boundingH = height + 4;
|
|
3474
|
+
|
|
3475
|
+
|
|
3476
|
+
|
|
3477
|
+
/**
|
|
3478
|
+
* Draw a bounding box if required
|
|
3479
|
+
*/
|
|
3480
|
+
if (bounding) {
|
|
3481
|
+
|
|
3482
|
+
var pre_bounding_linewidth = co.lineWidth;
|
|
3483
|
+
var pre_bounding_strokestyle = co.strokeStyle;
|
|
3484
|
+
var pre_bounding_fillstyle = co.fillStyle;
|
|
3485
|
+
var pre_bounding_shadowcolor = co.shadowColor;
|
|
3486
|
+
var pre_bounding_shadowblur = co.shadowBlur;
|
|
3487
|
+
var pre_bounding_shadowoffsetx = co.shadowOffsetX;
|
|
3488
|
+
var pre_bounding_shadowoffsety = co.shadowOffsetY;
|
|
3489
|
+
|
|
3490
|
+
co.lineWidth = bounding_linewidth;
|
|
3491
|
+
co.strokeStyle = bounding_stroke;
|
|
3492
|
+
co.fillStyle = bounding_fill;
|
|
3493
|
+
|
|
3494
|
+
if (bounding_shadow) {
|
|
3495
|
+
co.shadowColor = bounding_shadow_color;
|
|
3496
|
+
co.shadowBlur = bounding_shadow_blur;
|
|
3497
|
+
co.shadowOffsetX = bounding_shadow_offsetx;
|
|
3498
|
+
co.shadowOffsetY = bounding_shadow_offsety;
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
//obj.context.strokeRect(boundingX, boundingY, width + 6, (size_pixels * numlines) + 4);
|
|
3502
|
+
//obj.context.fillRect(boundingX, boundingY, width + 6, (size_pixels * numlines) + 4);
|
|
3503
|
+
co.strokeRect(boundingX, boundingY, boundingW, boundingH);
|
|
3504
|
+
co.fillRect(boundingX, boundingY, boundingW, boundingH);
|
|
3505
|
+
|
|
3506
|
+
// Reset the linewidth,colors and shadow to it's original setting
|
|
3507
|
+
co.lineWidth = pre_bounding_linewidth;
|
|
3508
|
+
co.strokeStyle = pre_bounding_strokestyle;
|
|
3509
|
+
co.fillStyle = pre_bounding_fillstyle;
|
|
3510
|
+
co.shadowColor = pre_bounding_shadowcolor
|
|
3511
|
+
co.shadowBlur = pre_bounding_shadowblur
|
|
3512
|
+
co.shadowOffsetX = pre_bounding_shadowoffsetx
|
|
3513
|
+
co.shadowOffsetY = pre_bounding_shadowoffsety
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
|
|
3517
|
+
|
|
3518
|
+
/**
|
|
3519
|
+
* Draw the text
|
|
3520
|
+
*/
|
|
3521
|
+
if (numlines > 1) {
|
|
3522
|
+
for (var i=0; i<numlines; ++i) {
|
|
3523
|
+
co.fillText(text_multiline[i], x, y + (size_pixels * i));
|
|
3524
|
+
}
|
|
3525
|
+
} else {
|
|
3526
|
+
co.fillText(text, x + 0.5, y + 0.5);
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
|
|
3530
|
+
|
|
3531
|
+
/**
|
|
3532
|
+
* If the text is at 90 degrees restore() the canvas - getting rid of the rotation
|
|
3533
|
+
* and the translate that we did
|
|
3534
|
+
*/
|
|
3535
|
+
if (angle != 0) {
|
|
3536
|
+
if (angle == 90) {
|
|
3537
|
+
if (halign == 'left') {
|
|
3538
|
+
if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3539
|
+
if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3540
|
+
if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3541
|
+
|
|
3542
|
+
} else if (halign == 'center') {
|
|
3543
|
+
if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3544
|
+
if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3545
|
+
if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - (width / 2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3546
|
+
|
|
3547
|
+
} else if (halign == 'right') {
|
|
3548
|
+
if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3549
|
+
if (valign == 'center') {boundingX = originalX - (height / 2) - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3550
|
+
if (valign == 'top') {boundingX = originalX - height - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
} else if (angle == 180) {
|
|
3554
|
+
|
|
3555
|
+
if (halign == 'left') {
|
|
3556
|
+
if (valign == 'bottom') {boundingX = originalX - width - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3557
|
+
if (valign == 'center') {boundingX = originalX - width - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3558
|
+
if (valign == 'top') {boundingX = originalX - width - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3559
|
+
|
|
3560
|
+
} else if (halign == 'center') {
|
|
3561
|
+
if (valign == 'bottom') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3562
|
+
if (valign == 'center') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3563
|
+
if (valign == 'top') {boundingX = originalX - (width / 2) - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3564
|
+
|
|
3565
|
+
} else if (halign == 'right') {
|
|
3566
|
+
if (valign == 'bottom') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3567
|
+
if (valign == 'center') {boundingX = originalX - 2; boundingY = originalY - (height / 2) - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3568
|
+
if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - height - 2; boundingW = width + 4; boundingH = height + 4;}
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3571
|
+
} else if (angle == 270) {
|
|
3572
|
+
|
|
3573
|
+
if (halign == 'left') {
|
|
3574
|
+
if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3575
|
+
if (valign == 'center') {boundingX = originalX - (height / 2) - 4; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3576
|
+
if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - width - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3577
|
+
|
|
3578
|
+
} else if (halign == 'center') {
|
|
3579
|
+
if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3580
|
+
if (valign == 'center') {boundingX = originalX - (height/2) - 4; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3581
|
+
if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - (width/2) - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3582
|
+
|
|
3583
|
+
} else if (halign == 'right') {
|
|
3584
|
+
if (valign == 'bottom') {boundingX = originalX - height - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3585
|
+
if (valign == 'center') {boundingX = originalX - (height/2) - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3586
|
+
if (valign == 'top') {boundingX = originalX - 2; boundingY = originalY - 2; boundingW = height + 4; boundingH = width + 4;}
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
co.restore();
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
|
|
3594
|
+
|
|
3595
|
+
|
|
3596
|
+
/**
|
|
3597
|
+
* Reset the text alignment so that text rendered after this text function is not affected
|
|
3598
|
+
*/
|
|
3599
|
+
co.textBaseline = 'alphabetic';
|
|
3600
|
+
co.textAlign = 'left';
|
|
3601
|
+
|
|
3602
|
+
|
|
3603
|
+
|
|
3604
|
+
|
|
3605
|
+
|
|
3606
|
+
/**
|
|
3607
|
+
* Fill the ret variable with details of the text
|
|
3608
|
+
*/
|
|
3609
|
+
ret.x = boundingX;
|
|
3610
|
+
ret.y = boundingY;
|
|
3611
|
+
ret.width = boundingW;
|
|
3612
|
+
ret.height = boundingH
|
|
3613
|
+
ret.object = obj;
|
|
3614
|
+
ret.text = text;
|
|
3615
|
+
ret.tag = tag;
|
|
3616
|
+
|
|
3617
|
+
|
|
3618
|
+
|
|
3619
|
+
/**
|
|
3620
|
+
* Save and then return the details of the text (but oly
|
|
3621
|
+
* if it's an RGraph object that was given)
|
|
3622
|
+
*/
|
|
3623
|
+
if (obj && obj.isRGraph && obj.coordsText) {
|
|
3624
|
+
obj.coordsText.push(ret);
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
//
|
|
3628
|
+
// Restore the original fillstyle
|
|
3629
|
+
//
|
|
3630
|
+
if (typeof orig_fillstyle === 'string') {
|
|
3631
|
+
co.fillStyle = orig_fillstyle;
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
return ret;
|
|
3635
|
+
};
|
|
3636
|
+
|
|
3637
|
+
|
|
3638
|
+
|
|
3639
|
+
|
|
3640
|
+
/**
|
|
3641
|
+
* Takes a sequential index abd returns the group/index variation of it. Eg if you have a
|
|
3642
|
+
* sequential index from a grouped bar chart this function can be used to convert that into
|
|
3643
|
+
* an appropriate group/index combination
|
|
3644
|
+
*
|
|
3645
|
+
* @param nindex number The sequential index
|
|
3646
|
+
* @param data array The original data (which is grouped)
|
|
3647
|
+
* @return The group/index information
|
|
3648
|
+
*/
|
|
3649
|
+
RG.sequentialIndexToGrouped = function (index, data)
|
|
3650
|
+
{
|
|
3651
|
+
var group = 0;
|
|
3652
|
+
var grouped_index = 0;
|
|
3653
|
+
|
|
3654
|
+
while (--index >= 0) {
|
|
3655
|
+
|
|
3656
|
+
if (RG.is_null(data[group])) {
|
|
3657
|
+
group++;
|
|
3658
|
+
grouped_index = 0;
|
|
3659
|
+
continue;
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
// Allow for numbers as well as arrays in the dataset
|
|
3663
|
+
if (typeof data[group] == 'number') {
|
|
3664
|
+
group++
|
|
3665
|
+
grouped_index = 0;
|
|
3666
|
+
continue;
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
|
|
3670
|
+
grouped_index++;
|
|
3671
|
+
|
|
3672
|
+
if (grouped_index >= data[group].length) {
|
|
3673
|
+
group++;
|
|
3674
|
+
grouped_index = 0;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
return [group, grouped_index];
|
|
3679
|
+
};
|
|
3680
|
+
|
|
3681
|
+
|
|
3682
|
+
|
|
3683
|
+
|
|
3684
|
+
/**
|
|
3685
|
+
* This function highlights a rectangle
|
|
3686
|
+
*
|
|
3687
|
+
* @param object obj The chart object
|
|
3688
|
+
* @param number shape The coordinates of the rect to highlight
|
|
3689
|
+
*/
|
|
3690
|
+
RG.Highlight.rect =
|
|
3691
|
+
RG.Highlight.Rect = function (obj, shape)
|
|
3692
|
+
{
|
|
3693
|
+
var ca = obj.canvas;
|
|
3694
|
+
var co = obj.context;
|
|
3695
|
+
var prop = obj.properties;
|
|
3696
|
+
|
|
3697
|
+
if (prop['chart.tooltips.highlight']) {
|
|
3698
|
+
|
|
3699
|
+
|
|
3700
|
+
// Safari seems to need this
|
|
3701
|
+
co.lineWidth = 1;
|
|
3702
|
+
|
|
3703
|
+
/**
|
|
3704
|
+
* Draw a rectangle on the canvas to highlight the appropriate area
|
|
3705
|
+
*/
|
|
3706
|
+
co.beginPath();
|
|
3707
|
+
|
|
3708
|
+
co.strokeStyle = prop['chart.highlight.stroke'];
|
|
3709
|
+
co.fillStyle = prop['chart.highlight.fill'];
|
|
3710
|
+
|
|
3711
|
+
co.rect(shape['x'],shape['y'],shape['width'],shape['height']);
|
|
3712
|
+
//co.fillRect(shape['x'],shape['y'],shape['width'],shape['height']);
|
|
3713
|
+
co.stroke();
|
|
3714
|
+
co.fill();
|
|
3715
|
+
}
|
|
3716
|
+
};
|
|
3717
|
+
|
|
3718
|
+
|
|
3719
|
+
|
|
3720
|
+
|
|
3721
|
+
/**
|
|
3722
|
+
* This function highlights a point
|
|
3723
|
+
*
|
|
3724
|
+
* @param object obj The chart object
|
|
3725
|
+
* @param number shape The coordinates of the rect to highlight
|
|
3726
|
+
*/
|
|
3727
|
+
RG.Highlight.point =
|
|
3728
|
+
RG.Highlight.Point = function (obj, shape)
|
|
3729
|
+
{
|
|
3730
|
+
var prop = obj.properties;
|
|
3731
|
+
var ca = obj.canvas;
|
|
3732
|
+
var co = obj.context;
|
|
3733
|
+
|
|
3734
|
+
if (prop['chart.tooltips.highlight']) {
|
|
3735
|
+
|
|
3736
|
+
/**
|
|
3737
|
+
* Draw a rectangle on the canvas to highlight the appropriate area
|
|
3738
|
+
*/
|
|
3739
|
+
co.beginPath();
|
|
3740
|
+
co.strokeStyle = prop['chart.highlight.stroke'];
|
|
3741
|
+
co.fillStyle = prop['chart.highlight.fill'];
|
|
3742
|
+
var radius = prop['chart.highlight.point.radius'] || 2;
|
|
3743
|
+
co.arc(shape['x'],shape['y'],radius, 0, RG.TWOPI, 0);
|
|
3744
|
+
co.stroke();
|
|
3745
|
+
co.fill();
|
|
3746
|
+
}
|
|
3747
|
+
};
|
|
3748
|
+
|
|
3749
|
+
|
|
3750
|
+
|
|
3751
|
+
|
|
3752
|
+
/**
|
|
3753
|
+
* This is the same as Date.parse - though a little more flexible.
|
|
3754
|
+
*
|
|
3755
|
+
* @param string str The date string to parse
|
|
3756
|
+
* @return Returns the same thing as Date.parse
|
|
3757
|
+
*/
|
|
3758
|
+
RG.parseDate = function (str)
|
|
3759
|
+
{
|
|
3760
|
+
str = RG.trim(str);
|
|
3761
|
+
|
|
3762
|
+
// Allow for: now (just the word "now")
|
|
3763
|
+
if (str === 'now') {
|
|
3764
|
+
str = (new Date()).toString();
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
// Allow for: 2013-11-22 12:12:12 or 2013/11/22 12:12:12
|
|
3768
|
+
if (str.match(/^(\d\d\d\d)(-|\/)(\d\d)(-|\/)(\d\d)( |T)(\d\d):(\d\d):(\d\d)$/)) {
|
|
3769
|
+
str = RegExp.$1 + '-' + RegExp.$3 + '-' + RegExp.$5 + 'T' + RegExp.$7 + ':' + RegExp.$8 + ':' + RegExp.$9;
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
// Allow for: 2013-11-22
|
|
3773
|
+
if (str.match(/^\d\d\d\d-\d\d-\d\d$/)) {
|
|
3774
|
+
str = str.replace(/-/g, '/');
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
// Allow for: 12:09:44 (time only using todays date)
|
|
3778
|
+
if (str.match(/^\d\d:\d\d:\d\d$/)) {
|
|
3779
|
+
|
|
3780
|
+
var dateObj = new Date();
|
|
3781
|
+
var date = dateObj.getDate();
|
|
3782
|
+
var month = dateObj.getMonth() + 1;
|
|
3783
|
+
var year = dateObj.getFullYear();
|
|
3784
|
+
|
|
3785
|
+
// Pad the date/month with a zero if it's not two characters
|
|
3786
|
+
if (String(month).length === 1) month = '0' + month;
|
|
3787
|
+
if (String(date).length === 1) date = '0' + date;
|
|
3788
|
+
|
|
3789
|
+
str = (year + '/' + month + '/' + date) + ' ' + str;
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
return Date.parse(str);
|
|
3793
|
+
};
|
|
3794
|
+
|
|
3795
|
+
|
|
3796
|
+
|
|
3797
|
+
|
|
3798
|
+
/**
|
|
3799
|
+
* Reset all of the color values to their original values
|
|
3800
|
+
*
|
|
3801
|
+
* @param object
|
|
3802
|
+
*/
|
|
3803
|
+
RG.resetColorsToOriginalValues = function (obj)
|
|
3804
|
+
{
|
|
3805
|
+
if (obj.original_colors) {
|
|
3806
|
+
// Reset the colors to their original values
|
|
3807
|
+
for (var j in obj.original_colors) {
|
|
3808
|
+
if (typeof j === 'string' && j.substr(0,6) === 'chart.') {
|
|
3809
|
+
obj.properties[j] = RG.arrayClone(obj.original_colors[j]);
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3814
|
+
|
|
3815
|
+
|
|
3816
|
+
/**
|
|
3817
|
+
* If the function is present on the object to reset specific colors - use that
|
|
3818
|
+
*/
|
|
3819
|
+
if (typeof obj.resetColorsToOriginalValues === 'function') {
|
|
3820
|
+
obj.resetColorsToOriginalValues();
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
|
|
3824
|
+
|
|
3825
|
+
// Reset the colorsParsed flag so that they're parsed for gradients again
|
|
3826
|
+
obj.colorsParsed = false;
|
|
3827
|
+
};
|
|
3828
|
+
|
|
3829
|
+
|
|
3830
|
+
|
|
3831
|
+
|
|
3832
|
+
/**
|
|
3833
|
+
* This function is a short-cut for the canvas path syntax (which can be rather verbose)
|
|
3834
|
+
*
|
|
3835
|
+
* @param mixed obj This can either be the 2D context or an RGraph object
|
|
3836
|
+
* @param array path The path details
|
|
3837
|
+
*/
|
|
3838
|
+
RG.path =
|
|
3839
|
+
RG.Path = function (obj, path)
|
|
3840
|
+
{
|
|
3841
|
+
/**
|
|
3842
|
+
* Allow either the RGraph object or the context to be used as the first argument
|
|
3843
|
+
*/
|
|
3844
|
+
if (obj.isRGraph && typeof obj.type === 'string') {
|
|
3845
|
+
var co = obj.context;
|
|
3846
|
+
} else {
|
|
3847
|
+
var co = obj;
|
|
3848
|
+
obj = obj.canvas.__object__;
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
/**
|
|
3852
|
+
* If the Path information has been passed as a string - split it up
|
|
3853
|
+
*/
|
|
3854
|
+
if (typeof path == 'string') {
|
|
3855
|
+
path = path.split(/ +/);
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3858
|
+
/**
|
|
3859
|
+
* Go through the path information
|
|
3860
|
+
*/
|
|
3861
|
+
for (var i=0,len=path.length; i<len; i+=1) {
|
|
3862
|
+
|
|
3863
|
+
var op = path[i];
|
|
3864
|
+
|
|
3865
|
+
// 100,100,50,0,Math.PI * 1.5, false
|
|
3866
|
+
switch (op) {
|
|
3867
|
+
case 'b':co.beginPath();break;
|
|
3868
|
+
case 'c':co.closePath();break;
|
|
3869
|
+
case 'm':co.moveTo(parseFloat(path[i+1]),parseFloat(path[i+2]));i+=2;break;
|
|
3870
|
+
case 'l':co.lineTo(parseFloat(path[i+1]),parseFloat(path[i+2]));i+=2;break;
|
|
3871
|
+
case 's':if(path[i+1])co.strokeStyle=obj.parseSingleColorForGradient(path[i+1]);co.stroke();i++;break;
|
|
3872
|
+
case 'f':if(path[i+1]){co.fillStyle = obj.parseSingleColorForGradient(path[i+1]);}co.fill();i++;break;
|
|
3873
|
+
case 'qc':co.quadraticCurveTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
|
|
3874
|
+
case 'bc':co.bezierCurveTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]),parseFloat(path[i+6]));i+=6;break;
|
|
3875
|
+
case 'r':co.rect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
|
|
3876
|
+
case 'a':co.arc(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]),path[i+6]==='true'||path[i+6]===true?true:false);i+=6;break;
|
|
3877
|
+
case 'at':co.arcTo(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]),parseFloat(path[i+5]));i+=5;break;
|
|
3878
|
+
case 'lw':co.lineWidth=parseFloat(path[i+1]);i++;break;
|
|
3879
|
+
case 'lj':co.lineJoin=path[i+1];i++;break;
|
|
3880
|
+
case 'lc':co.lineCap=path[i+1];i++;break;
|
|
3881
|
+
case 'sc':co.shadowColor=path[i+1];i++;break;
|
|
3882
|
+
case 'sb':co.shadowBlur=parseFloat(path[i+1]);i++;break;
|
|
3883
|
+
case 'sx':co.shadowOffsetX=parseFloat(path[i+1]);i++;break;
|
|
3884
|
+
case 'sy':co.shadowOffsetY=parseFloat(path[i+1]);i++;break;
|
|
3885
|
+
case 'fu':(path[i+1])(obj);i++;break;
|
|
3886
|
+
case 'fs':co.fillStyle=obj.parseSingleColorForGradient(path[i+1]);i++;break;
|
|
3887
|
+
case 'ss':co.strokeStyle=obj.parseSingleColorForGradient(path[i+1]);i++;break;
|
|
3888
|
+
case 'fr':co.fillRect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
|
|
3889
|
+
case 'sr':co.strokeRect(parseFloat(path[i+1]),parseFloat(path[i+2]),parseFloat(path[i+3]),parseFloat(path[i+4]));i+=4;break;
|
|
3890
|
+
case 'cl':co.clip();break;
|
|
3891
|
+
case 'ct':co.save();co.beginPath();RG.path(co, path[i+1]);co.clip();i++;break;
|
|
3892
|
+
case 'sa':co.save();break;
|
|
3893
|
+
case 'rs':co.restore();break;
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
};
|
|
3897
|
+
|
|
3898
|
+
|
|
3899
|
+
|
|
3900
|
+
/**
|
|
3901
|
+
* Creates a Linear gradient
|
|
3902
|
+
*
|
|
3903
|
+
* @param object obj The chart object
|
|
3904
|
+
* @param number x1 The start X coordinate
|
|
3905
|
+
* @param number x2 The end X coordinate
|
|
3906
|
+
* @param number y1 The start Y coordinate
|
|
3907
|
+
* @param number y2 The end Y coordinate
|
|
3908
|
+
* @param string color1 The start color
|
|
3909
|
+
* @param string color2 The end color
|
|
3910
|
+
*/
|
|
3911
|
+
RG.linearGradient =
|
|
3912
|
+
RG.LinearGradient = function (obj, x1, y1, x2, y2, color1, color2)
|
|
3913
|
+
{
|
|
3914
|
+
var gradient = obj.context.createLinearGradient(x1, y1, x2, y2);
|
|
3915
|
+
var numColors=arguments.length-5;
|
|
3916
|
+
|
|
3917
|
+
for (var i=5; i<arguments.length; ++i) {
|
|
3918
|
+
|
|
3919
|
+
var color = arguments[i];
|
|
3920
|
+
var stop = (i - 5) / (numColors - 1);
|
|
3921
|
+
|
|
3922
|
+
gradient.addColorStop(stop, color);
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
return gradient;
|
|
3926
|
+
};
|
|
3927
|
+
|
|
3928
|
+
|
|
3929
|
+
|
|
3930
|
+
|
|
3931
|
+
/**
|
|
3932
|
+
* Creates a Radial gradient
|
|
3933
|
+
*
|
|
3934
|
+
* @param object obj The chart object
|
|
3935
|
+
* @param number x1 The start X coordinate
|
|
3936
|
+
* @param number x2 The end X coordinate
|
|
3937
|
+
* @param number y1 The start Y coordinate
|
|
3938
|
+
* @param number y2 The end Y coordinate
|
|
3939
|
+
* @param string color1 The start color
|
|
3940
|
+
* @param string color2 The end color
|
|
3941
|
+
*/
|
|
3942
|
+
RG.radialGradient =
|
|
3943
|
+
RG.RadialGradient = function(obj, x1, y1, r1, x2, y2, r2, color1, color2)
|
|
3944
|
+
{
|
|
3945
|
+
var gradient = obj.context.createRadialGradient(x1, y1, r1, x2, y2, r2);
|
|
3946
|
+
var numColors = arguments.length-7;
|
|
3947
|
+
|
|
3948
|
+
for(var i=7; i<arguments.length; ++i) {
|
|
3949
|
+
|
|
3950
|
+
var color = arguments[i];
|
|
3951
|
+
var stop = (i-7) / (numColors-1);
|
|
3952
|
+
|
|
3953
|
+
gradient.addColorStop(stop, color);
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
return gradient;
|
|
3957
|
+
};
|
|
3958
|
+
|
|
3959
|
+
|
|
3960
|
+
|
|
3961
|
+
|
|
3962
|
+
/**
|
|
3963
|
+
* Adds an event listener to RGraphs internal array so that RGraph can track them.
|
|
3964
|
+
* This DOESN'T add the event listener to the canvas/window.
|
|
3965
|
+
*
|
|
3966
|
+
* 5/1/14 TODO Used in the tooltips file, but is it necessary any more?
|
|
3967
|
+
*/
|
|
3968
|
+
RG.addEventListener =
|
|
3969
|
+
RG.AddEventListener = function (id, e, func)
|
|
3970
|
+
{
|
|
3971
|
+
var type = arguments[3] ? arguments[3] : 'unknown';
|
|
3972
|
+
|
|
3973
|
+
RG.Registry.get('chart.event.handlers').push([id,e,func,type]);
|
|
3974
|
+
};
|
|
3975
|
+
|
|
3976
|
+
|
|
3977
|
+
|
|
3978
|
+
|
|
3979
|
+
/**
|
|
3980
|
+
* Clears event listeners that have been installed by RGraph
|
|
3981
|
+
*
|
|
3982
|
+
* @param string id The ID of the canvas to clear event listeners for - or 'window' to clear
|
|
3983
|
+
* the event listeners attached to the window
|
|
3984
|
+
*/
|
|
3985
|
+
RG.clearEventListeners =
|
|
3986
|
+
RG.ClearEventListeners = function(id)
|
|
3987
|
+
{
|
|
3988
|
+
if (id && id == 'window') {
|
|
3989
|
+
|
|
3990
|
+
window.removeEventListener('mousedown', window.__rgraph_mousedown_event_listener_installed__, false);
|
|
3991
|
+
window.removeEventListener('mouseup', window.__rgraph_mouseup_event_listener_installed__, false);
|
|
3992
|
+
|
|
3993
|
+
} else {
|
|
3994
|
+
|
|
3995
|
+
var canvas = document.getElementById(id);
|
|
3996
|
+
|
|
3997
|
+
canvas.removeEventListener('mouseup', canvas.__rgraph_mouseup_event_listener_installed__, false);
|
|
3998
|
+
canvas.removeEventListener('mousemove', canvas.__rgraph_mousemove_event_listener_installed__, false);
|
|
3999
|
+
canvas.removeEventListener('mousedown', canvas.__rgraph_mousedown_event_listener_installed__, false);
|
|
4000
|
+
canvas.removeEventListener('click', canvas.__rgraph_click_event_listener_installed__, false);
|
|
4001
|
+
}
|
|
4002
|
+
};
|
|
4003
|
+
|
|
4004
|
+
|
|
4005
|
+
|
|
4006
|
+
|
|
4007
|
+
/**
|
|
4008
|
+
* Hides the annotating palette. It's here because it can be called
|
|
4009
|
+
* from code other than the annotating code.
|
|
4010
|
+
*/
|
|
4011
|
+
RG.hidePalette =
|
|
4012
|
+
RG.HidePalette = function ()
|
|
4013
|
+
{
|
|
4014
|
+
var div = RG.Registry.get('palette');
|
|
4015
|
+
|
|
4016
|
+
if(typeof div == 'object' && div) {
|
|
4017
|
+
|
|
4018
|
+
div.style.visibility = 'hidden';
|
|
4019
|
+
div.style.display = 'none';
|
|
4020
|
+
|
|
4021
|
+
RG.Registry.set('palette', null);
|
|
4022
|
+
}
|
|
4023
|
+
};
|
|
4024
|
+
|
|
4025
|
+
|
|
4026
|
+
|
|
4027
|
+
|
|
4028
|
+
/**
|
|
4029
|
+
* Generates a random number between the minimum and maximum
|
|
4030
|
+
*
|
|
4031
|
+
* @param number min The minimum value
|
|
4032
|
+
* @param number max The maximum value
|
|
4033
|
+
* @param number OPTIONAL Number of decimal places
|
|
4034
|
+
*/
|
|
4035
|
+
RG.random = function (min, max)
|
|
4036
|
+
{
|
|
4037
|
+
var dp = arguments[2] ? arguments[2] : 0;
|
|
4038
|
+
var r = ma.random();
|
|
4039
|
+
|
|
4040
|
+
return Number((((max - min) * r) + min).toFixed(dp));
|
|
4041
|
+
};
|
|
4042
|
+
|
|
4043
|
+
|
|
4044
|
+
|
|
4045
|
+
|
|
4046
|
+
/**
|
|
4047
|
+
*
|
|
4048
|
+
*/
|
|
4049
|
+
RG.random.array = function (num, min, max)
|
|
4050
|
+
{
|
|
4051
|
+
var arr = [];
|
|
4052
|
+
|
|
4053
|
+
for(var i=0; i<num; i+=1) {
|
|
4054
|
+
arr.push(RG.random(min,max));
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
return arr;
|
|
4058
|
+
};
|
|
4059
|
+
|
|
4060
|
+
|
|
4061
|
+
|
|
4062
|
+
|
|
4063
|
+
/**
|
|
4064
|
+
* Turns off shadow by setting blur to zero, the offsets to zero and the color to transparent black.
|
|
4065
|
+
*
|
|
4066
|
+
* @param object obj The chart object
|
|
4067
|
+
*/
|
|
4068
|
+
RG.noShadow =
|
|
4069
|
+
RG.NoShadow = function (obj)
|
|
4070
|
+
{
|
|
4071
|
+
var co = obj.context;
|
|
4072
|
+
|
|
4073
|
+
co.shadowColor = 'rgba(0,0,0,0)';
|
|
4074
|
+
co.shadowBlur = 0;
|
|
4075
|
+
co.shadowOffsetX = 0;
|
|
4076
|
+
co.shadowOffsetY = 0;
|
|
4077
|
+
};
|
|
4078
|
+
|
|
4079
|
+
|
|
4080
|
+
|
|
4081
|
+
|
|
4082
|
+
/**
|
|
4083
|
+
* Sets the various shadow properties
|
|
4084
|
+
*
|
|
4085
|
+
* @param object obj The chart object
|
|
4086
|
+
* @param string color The color of the shadow
|
|
4087
|
+
* @param number offsetx The offsetX value for the shadow
|
|
4088
|
+
* @param number offsety The offsetY value for the shadow
|
|
4089
|
+
* @param number blur The blurring value for the shadow
|
|
4090
|
+
*/
|
|
4091
|
+
RG.setShadow =
|
|
4092
|
+
RG.SetShadow = function (obj, color, offsetx, offsety, blur)
|
|
4093
|
+
{
|
|
4094
|
+
var co = obj.context;
|
|
4095
|
+
|
|
4096
|
+
co.shadowColor = color;
|
|
4097
|
+
co.shadowOffsetX = offsetx;
|
|
4098
|
+
co.shadowOffsetY = offsety;
|
|
4099
|
+
co.shadowBlur = blur;
|
|
4100
|
+
|
|
4101
|
+
};
|
|
4102
|
+
|
|
4103
|
+
|
|
4104
|
+
|
|
4105
|
+
|
|
4106
|
+
/**
|
|
4107
|
+
* Sets an object in the RGraph registry
|
|
4108
|
+
*
|
|
4109
|
+
* @param string name The name of the value to set
|
|
4110
|
+
*/
|
|
4111
|
+
RG.Registry.set =
|
|
4112
|
+
RG.Registry.Set = function (name, value)
|
|
4113
|
+
{
|
|
4114
|
+
// Convert uppercase letters to dot+lower case letter
|
|
4115
|
+
name = name.replace(/([A-Z])/g, function (str)
|
|
4116
|
+
{
|
|
4117
|
+
return '.' + String(RegExp.$1).toLowerCase();
|
|
4118
|
+
});
|
|
4119
|
+
|
|
4120
|
+
// Ensure there is the chart. prefix
|
|
4121
|
+
if (name.substr(0,6) !== 'chart.') {
|
|
4122
|
+
name = 'chart.' + name;
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
RG.Registry.store[name] = value;
|
|
4126
|
+
|
|
4127
|
+
return value;
|
|
4128
|
+
};
|
|
4129
|
+
|
|
4130
|
+
|
|
4131
|
+
|
|
4132
|
+
|
|
4133
|
+
/**
|
|
4134
|
+
* Gets an object from the RGraph registry
|
|
4135
|
+
*
|
|
4136
|
+
* @param string name The name of the value to fetch
|
|
4137
|
+
*/
|
|
4138
|
+
RG.Registry.get =
|
|
4139
|
+
RG.Registry.Get = function (name)
|
|
4140
|
+
{
|
|
4141
|
+
// Convert uppercase letters to dot+lower case letter
|
|
4142
|
+
name = name.replace(/([A-Z])/g, function (str)
|
|
4143
|
+
{
|
|
4144
|
+
return '.' + String(RegExp.$1).toLowerCase();
|
|
4145
|
+
});
|
|
4146
|
+
|
|
4147
|
+
// Ensure there is the chart. prefix
|
|
4148
|
+
if (name.substr(0,6) !== 'chart.') {
|
|
4149
|
+
name = 'chart.' + name;
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
|
|
4153
|
+
return RG.Registry.store[name];
|
|
4154
|
+
};
|
|
4155
|
+
|
|
4156
|
+
|
|
4157
|
+
|
|
4158
|
+
|
|
4159
|
+
/**
|
|
4160
|
+
* Converts the given number of degrees to radians. Angles in canvas are measured in radians
|
|
4161
|
+
*
|
|
4162
|
+
* @param number deg The value to convert
|
|
4163
|
+
*/
|
|
4164
|
+
RG.degrees2Radians = function (deg)
|
|
4165
|
+
{
|
|
4166
|
+
return deg * (RG.PI / 180);
|
|
4167
|
+
};
|
|
4168
|
+
|
|
4169
|
+
|
|
4170
|
+
|
|
4171
|
+
|
|
4172
|
+
/**
|
|
4173
|
+
* Generates logs for... ...log charts
|
|
4174
|
+
*
|
|
4175
|
+
* @param number n The number to generate the log for
|
|
4176
|
+
* @param number base The base to use
|
|
4177
|
+
*/
|
|
4178
|
+
RG.log = function (n,base)
|
|
4179
|
+
{
|
|
4180
|
+
return ma.log(n) / (base ? ma.log(base) : 1);
|
|
4181
|
+
};
|
|
4182
|
+
|
|
4183
|
+
|
|
4184
|
+
|
|
4185
|
+
|
|
4186
|
+
/**
|
|
4187
|
+
* Determines if the given object is an array or not
|
|
4188
|
+
*
|
|
4189
|
+
* @param mixed obj The variable to test
|
|
4190
|
+
*/
|
|
4191
|
+
RG.isArray =
|
|
4192
|
+
RG.is_array = function (obj)
|
|
4193
|
+
{
|
|
4194
|
+
if (obj && obj.constructor) {
|
|
4195
|
+
var pos = obj.constructor.toString().indexOf('Array');
|
|
4196
|
+
} else {
|
|
4197
|
+
return false;
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
return obj != null &&
|
|
4201
|
+
typeof pos === 'number' &&
|
|
4202
|
+
pos > 0 &&
|
|
4203
|
+
pos < 20;
|
|
4204
|
+
};
|
|
4205
|
+
|
|
4206
|
+
|
|
4207
|
+
|
|
4208
|
+
|
|
4209
|
+
/**
|
|
4210
|
+
* Removes white-space from the start aqnd end of a string
|
|
4211
|
+
*
|
|
4212
|
+
* @param string str The string to trim
|
|
4213
|
+
*/
|
|
4214
|
+
RG.trim = function (str)
|
|
4215
|
+
{
|
|
4216
|
+
return RG.ltrim(RG.rtrim(str));
|
|
4217
|
+
};
|
|
4218
|
+
|
|
4219
|
+
|
|
4220
|
+
|
|
4221
|
+
|
|
4222
|
+
/**
|
|
4223
|
+
* Trims the white-space from the start of a string
|
|
4224
|
+
*
|
|
4225
|
+
* @param string str The string to trim
|
|
4226
|
+
*/
|
|
4227
|
+
RG.ltrim = function (str)
|
|
4228
|
+
{
|
|
4229
|
+
return str.replace(/^(\s|\0)+/, '');
|
|
4230
|
+
};
|
|
4231
|
+
|
|
4232
|
+
|
|
4233
|
+
|
|
4234
|
+
|
|
4235
|
+
/**
|
|
4236
|
+
* Trims the white-space off of the end of a string
|
|
4237
|
+
*
|
|
4238
|
+
* @param string str The string to trim
|
|
4239
|
+
*/
|
|
4240
|
+
RG.rtrim = function (str)
|
|
4241
|
+
{
|
|
4242
|
+
return str.replace(/(\s|\0)+$/, '');
|
|
4243
|
+
};
|
|
4244
|
+
|
|
4245
|
+
|
|
4246
|
+
|
|
4247
|
+
/**
|
|
4248
|
+
* Returns true/false as to whether the given variable is null or not
|
|
4249
|
+
*
|
|
4250
|
+
* @param mixed arg The argument to check
|
|
4251
|
+
*/
|
|
4252
|
+
RG.isNull =
|
|
4253
|
+
RG.is_null = function (arg)
|
|
4254
|
+
{
|
|
4255
|
+
// must BE DOUBLE EQUALS - NOT TRIPLE
|
|
4256
|
+
if (arg == null || typeof arg === 'object' && !arg) {
|
|
4257
|
+
return true;
|
|
4258
|
+
}
|
|
4259
|
+
|
|
4260
|
+
return false;
|
|
4261
|
+
};
|
|
4262
|
+
|
|
4263
|
+
|
|
4264
|
+
|
|
4265
|
+
|
|
4266
|
+
/**
|
|
4267
|
+
* This function facilitates a very limited way of making your charts
|
|
4268
|
+
* whilst letting the rest of page continue - using the setTimeout function
|
|
4269
|
+
*
|
|
4270
|
+
* @param function func The function to run that creates the chart
|
|
4271
|
+
*/
|
|
4272
|
+
RG.async =
|
|
4273
|
+
RG.Async = function (func)
|
|
4274
|
+
{
|
|
4275
|
+
return setTimeout(func, arguments[1] ? arguments[1] : 1);
|
|
4276
|
+
};
|
|
4277
|
+
|
|
4278
|
+
|
|
4279
|
+
|
|
4280
|
+
|
|
4281
|
+
/**
|
|
4282
|
+
* Resets (more than just clears) the canvas and clears any pertinent objects
|
|
4283
|
+
* from the ObjectRegistry
|
|
4284
|
+
*
|
|
4285
|
+
* @param object ca The canvas object (as returned by document.getElementById() ).
|
|
4286
|
+
*/
|
|
4287
|
+
RG.reset =
|
|
4288
|
+
RG.Reset = function (ca)
|
|
4289
|
+
{
|
|
4290
|
+
ca.width = ca.width;
|
|
4291
|
+
|
|
4292
|
+
RG.ObjectRegistry.clear(ca);
|
|
4293
|
+
|
|
4294
|
+
ca.__rgraph_aa_translated__ = false;
|
|
4295
|
+
|
|
4296
|
+
|
|
4297
|
+
|
|
4298
|
+
|
|
4299
|
+
|
|
4300
|
+
//
|
|
4301
|
+
// Clear any text objects that are in the cache
|
|
4302
|
+
//
|
|
4303
|
+
//var len = (ca.id + '-text-').length;
|
|
4304
|
+
|
|
4305
|
+
//for (i in RG.cache) {
|
|
4306
|
+
//
|
|
4307
|
+
// var value = RG.cache[i];
|
|
4308
|
+
|
|
4309
|
+
// if (i.substr(0, len) === (ca.id + '-text-') && typeof value === 'object' && value) {
|
|
4310
|
+
// RG.cache[i].parentNode.removeChild(RG.cache[i]);
|
|
4311
|
+
// RG.cache[i] = null;
|
|
4312
|
+
// }
|
|
4313
|
+
//}
|
|
4314
|
+
};
|
|
4315
|
+
|
|
4316
|
+
|
|
4317
|
+
|
|
4318
|
+
|
|
4319
|
+
/**
|
|
4320
|
+
* NOT USED ANY MORE
|
|
4321
|
+
*/
|
|
4322
|
+
RG.att = function (ca)
|
|
4323
|
+
{
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
|
|
4327
|
+
|
|
4328
|
+
/**
|
|
4329
|
+
* This function is due to be removed.
|
|
4330
|
+
*
|
|
4331
|
+
* @param string id The ID of what can be either the canvas tag or a DIV tag
|
|
4332
|
+
*/
|
|
4333
|
+
RG.getCanvasTag = function (id)
|
|
4334
|
+
{
|
|
4335
|
+
id = typeof id === 'object' ? id.id : id;
|
|
4336
|
+
var canvas = doc.getElementById(id);
|
|
4337
|
+
|
|
4338
|
+
return [id, canvas];
|
|
4339
|
+
};
|
|
4340
|
+
|
|
4341
|
+
|
|
4342
|
+
|
|
4343
|
+
|
|
4344
|
+
/**
|
|
4345
|
+
* A wrapper function that encapsulate requestAnimationFrame
|
|
4346
|
+
*
|
|
4347
|
+
* @param function func The animation function
|
|
4348
|
+
*/
|
|
4349
|
+
RG.Effects.updateCanvas =
|
|
4350
|
+
RG.Effects.UpdateCanvas = function (func)
|
|
4351
|
+
{
|
|
4352
|
+
win.requestAnimationFrame = win.requestAnimationFrame
|
|
4353
|
+
|| win.webkitRequestAnimationFrame
|
|
4354
|
+
|| win.msRequestAnimationFrame
|
|
4355
|
+
|| win.mozRequestAnimationFrame
|
|
4356
|
+
|| (function (func){setTimeout(func, 16.666);});
|
|
4357
|
+
|
|
4358
|
+
win.requestAnimationFrame(func);
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4361
|
+
|
|
4362
|
+
|
|
4363
|
+
|
|
4364
|
+
/**
|
|
4365
|
+
* This function returns an easing multiplier for effects so they eas out towards the
|
|
4366
|
+
* end of the effect.
|
|
4367
|
+
*
|
|
4368
|
+
* @param number frames The total number of frames
|
|
4369
|
+
* @param number frame The frame number
|
|
4370
|
+
*/
|
|
4371
|
+
RG.Effects.getEasingMultiplier = function (frames, frame)
|
|
4372
|
+
{
|
|
4373
|
+
return ma.pow(ma.sin((frame / frames) * RG.HALFPI), 3);
|
|
4374
|
+
};
|
|
4375
|
+
|
|
4376
|
+
|
|
4377
|
+
|
|
4378
|
+
|
|
4379
|
+
/**
|
|
4380
|
+
* This function converts an array of strings to an array of numbers. Its used by the meter/gauge
|
|
4381
|
+
* style charts so that if you want you can pass in a string. It supports various formats:
|
|
4382
|
+
*
|
|
4383
|
+
* '45.2'
|
|
4384
|
+
* '-45.2'
|
|
4385
|
+
* ['45.2']
|
|
4386
|
+
* ['-45.2']
|
|
4387
|
+
* '45.2,45.2,45.2' // A CSV style string
|
|
4388
|
+
*
|
|
4389
|
+
* @param number frames The string or array to parse
|
|
4390
|
+
*/
|
|
4391
|
+
RG.stringsToNumbers = function (str)
|
|
4392
|
+
{
|
|
4393
|
+
// An optional seperator to use intead of a comma
|
|
4394
|
+
var sep = arguments[1] || ',';
|
|
4395
|
+
|
|
4396
|
+
|
|
4397
|
+
// If it's already a number just return it
|
|
4398
|
+
if (typeof str === 'number') {
|
|
4399
|
+
return str;
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
|
|
4403
|
+
|
|
4404
|
+
|
|
4405
|
+
|
|
4406
|
+
if (typeof str === 'string') {
|
|
4407
|
+
if (str.indexOf(sep) != -1) {
|
|
4408
|
+
str = str.split(sep);
|
|
4409
|
+
} else {
|
|
4410
|
+
str = parseFloat(str);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
|
|
4415
|
+
|
|
4416
|
+
|
|
4417
|
+
|
|
4418
|
+
if (typeof str === 'object') {
|
|
4419
|
+
for (var i=0,len=str.length; i<len; i+=1) {
|
|
4420
|
+
str[i] = parseFloat(str[i]);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
return str;
|
|
4425
|
+
};
|
|
4426
|
+
|
|
4427
|
+
|
|
4428
|
+
|
|
4429
|
+
|
|
4430
|
+
/**
|
|
4431
|
+
* Drawing cache function. This function creates an off-screen canvas and draws [wwhatever] to it
|
|
4432
|
+
* and then subsequent calls use that instead of repeatedly drawing the same thing.
|
|
4433
|
+
*
|
|
4434
|
+
* @param object obj The graph object
|
|
4435
|
+
* @param string id An ID string used to identify the relevant entry in the cache
|
|
4436
|
+
* @param function func The drawing function. This will be called to do the draw.
|
|
4437
|
+
*/
|
|
4438
|
+
RG.cachedDraw = function (obj, id, func)
|
|
4439
|
+
{
|
|
4440
|
+
//If the cache entry xists - just copy it across to the main canvas
|
|
4441
|
+
if (!RG.cache[id]) {
|
|
4442
|
+
|
|
4443
|
+
RG.cache[id] = {};
|
|
4444
|
+
RG.cache[id].object = obj;
|
|
4445
|
+
RG.cache[id].canvas = document.createElement('canvas');
|
|
4446
|
+
RG.cache[id].canvas.setAttribute('width', obj.canvas.width);
|
|
4447
|
+
RG.cache[id].canvas.setAttribute('height', obj.canvas.height);
|
|
4448
|
+
RG.cache[id].canvas.setAttribute('id', 'background_cached_canvas' + obj.canvas.id);
|
|
4449
|
+
|
|
4450
|
+
//Add MSIE support
|
|
4451
|
+
if (typeof G_vmlCanvasManager === 'object' && G_vmlCanvasManager.initElement) {
|
|
4452
|
+
G_vmlCanvasManager.initElement(RG.cache[id].canvas);
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
RG.cache[id].context = RG.cache[id].canvas.getContext('2d');
|
|
4456
|
+
|
|
4457
|
+
// Antialiasing on the cache canvas
|
|
4458
|
+
RG.cache[id].context.translate(0.5,0.5);
|
|
4459
|
+
|
|
4460
|
+
// Call the function
|
|
4461
|
+
func(obj, RG.cache[id].canvas, RG.cache[id].context);
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
// Now copy the contents of the cached canvas over to the main one.
|
|
4465
|
+
// The coordinates are -0.5 because of the anti-aliasing effect in
|
|
4466
|
+
// use on the main canvas
|
|
4467
|
+
obj.context.drawImage(RG.cache[id].canvas,-0.5,-0.5);
|
|
4468
|
+
};
|
|
4469
|
+
|
|
4470
|
+
|
|
4471
|
+
|
|
4472
|
+
|
|
4473
|
+
/**
|
|
4474
|
+
* The function that runs through the supplied configuration and
|
|
4475
|
+
* converts it to the RGraph stylee.
|
|
4476
|
+
*
|
|
4477
|
+
* @param object conf The config
|
|
4478
|
+
* @param object The settings for the object
|
|
4479
|
+
*/
|
|
4480
|
+
RG.parseObjectStyleConfig = function (obj, config)
|
|
4481
|
+
{
|
|
4482
|
+
/**
|
|
4483
|
+
* The recursion function
|
|
4484
|
+
*/
|
|
4485
|
+
var recurse = function (obj, config, name, settings)
|
|
4486
|
+
{
|
|
4487
|
+
var i;
|
|
4488
|
+
|
|
4489
|
+
for (key in config) {
|
|
4490
|
+
|
|
4491
|
+
// Allow for functions in the configuration. Run them immediately
|
|
4492
|
+
if (key.match(/^exec[0-9]*$/)) {
|
|
4493
|
+
config[key](obj, settings);
|
|
4494
|
+
continue;
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4497
|
+
var isObject = false; // Default value
|
|
4498
|
+
var isArray = false; // Default value
|
|
4499
|
+
var value = config[key];
|
|
4500
|
+
|
|
4501
|
+
// Change caps to dots. Eg textSize => text.size
|
|
4502
|
+
while(key.match(/([A-Z])/)) {
|
|
4503
|
+
key = key.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase());
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4506
|
+
if (!RG.isNull(value) && value.constructor) {
|
|
4507
|
+
isObject = value.constructor.toString().indexOf('Object') > 0;
|
|
4508
|
+
isArray = value.constructor.toString().indexOf('Array') > 0;
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4511
|
+
if (isObject && !isArray) {
|
|
4512
|
+
recurse(obj, config[key], name + '.' + key, settings);
|
|
4513
|
+
|
|
4514
|
+
} else if (key === 'self') {
|
|
4515
|
+
settings[name] = value;
|
|
4516
|
+
|
|
4517
|
+
} else {
|
|
4518
|
+
settings[name + '.' + key] = value;
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
return settings;
|
|
4523
|
+
};
|
|
4524
|
+
|
|
4525
|
+
|
|
4526
|
+
|
|
4527
|
+
|
|
4528
|
+
/**
|
|
4529
|
+
* Go through the settings that we've been given
|
|
4530
|
+
*/
|
|
4531
|
+
var settings = recurse(obj, config, 'chart', {});
|
|
4532
|
+
|
|
4533
|
+
/**
|
|
4534
|
+
* Go through the settings and set them on the object
|
|
4535
|
+
*/
|
|
4536
|
+
for (key in settings) {
|
|
4537
|
+
if (typeof key === 'string') {
|
|
4538
|
+
obj.set(key, settings[key]);
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
};
|
|
4542
|
+
|
|
4543
|
+
|
|
4544
|
+
|
|
4545
|
+
|
|
4546
|
+
/**
|
|
4547
|
+
* This function is a short-cut for the canvas path syntax (which can be rather
|
|
4548
|
+
* verbose). You can read a description of it (which details all of the
|
|
4549
|
+
* various options) on the RGraph blog (www.rgraph.net/blog). The function is
|
|
4550
|
+
* added to the CanvasRenderingContext2D object so it becomes a context function.
|
|
4551
|
+
*
|
|
4552
|
+
* So you can use it like these examples show:
|
|
4553
|
+
*
|
|
4554
|
+
* 1. RG.pa2(context, 'b r 0 0 50 50 f red');
|
|
4555
|
+
* 2. RG.pa2(context, 'b a 50 50 50 0 3.14 false f red');
|
|
4556
|
+
* 3. RG.pa2(context, 'b m 5 100 bc 5 0 100 0 100 100 s red');
|
|
4557
|
+
* 4. RG.pa2(context, 'b m 5 100 at 50 0 95 100 50 s red');
|
|
4558
|
+
* 5. RG.pa2(context, 'sa b r 0 0 50 50 c b r 5 5 590 240 f red rs');
|
|
4559
|
+
* 6. RG.pa2(context, 'ld [2,6] ldo 4 b r 5 5 590 240 f red');
|
|
4560
|
+
* 7. RG.pa2(context, 'ga 0.25 b r 5 5 590 240 f red');
|
|
4561
|
+
*
|
|
4562
|
+
* @param array p The path details
|
|
4563
|
+
*/
|
|
4564
|
+
RG.path2 = function (co, p)
|
|
4565
|
+
{
|
|
4566
|
+
if (typeof p === 'string') {
|
|
4567
|
+
|
|
4568
|
+
// Clear leading and trailing whitespace
|
|
4569
|
+
p = p.trim();
|
|
4570
|
+
|
|
4571
|
+
|
|
4572
|
+
|
|
4573
|
+
|
|
4574
|
+
// Allow for % placeholder substitution
|
|
4575
|
+
if (p.indexOf('%') !== -1) {
|
|
4576
|
+
|
|
4577
|
+
p = p.split(/%/);
|
|
4578
|
+
|
|
4579
|
+
for (var i=1; i<p.length; i+=1) {
|
|
4580
|
+
p[i] = arguments[i+1].toString() + ' ' + p[i];
|
|
4581
|
+
}
|
|
4582
|
+
|
|
4583
|
+
p = p.join(' ');
|
|
4584
|
+
}
|
|
4585
|
+
|
|
4586
|
+
|
|
4587
|
+
|
|
4588
|
+
|
|
4589
|
+
// Split up the path
|
|
4590
|
+
p = p.split(/ +/);
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
// Collapse args that are in single or double quotes
|
|
4594
|
+
p = collapseQuoted(p);
|
|
4595
|
+
|
|
4596
|
+
|
|
4597
|
+
// Go through the path information
|
|
4598
|
+
for (var i=0,len=p.length; i<len; i+=1) {
|
|
4599
|
+
|
|
4600
|
+
switch (p[i]) {
|
|
4601
|
+
case 'b':co.beginPath();break;
|
|
4602
|
+
case 'c':co.closePath();break;
|
|
4603
|
+
case 'm':co.moveTo(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
|
|
4604
|
+
case 'l':co.lineTo(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
|
|
4605
|
+
case 's':if(p[i+1])co.strokeStyle=p[i+1];co.stroke();i++;break;
|
|
4606
|
+
case 'f':if(p[i+1]){co.fillStyle=p[i+1];}co.fill();i++;break;
|
|
4607
|
+
case 'qc':co.quadraticCurveTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
|
|
4608
|
+
case 'bc':co.bezierCurveTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
|
|
4609
|
+
case 'r':co.rect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
|
|
4610
|
+
case 'a':co.arc(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),p[i+6]==='true'||p[i+6]===true||p[i+6]===1||p[i+6]==='1'?true:false);i+=6;break;
|
|
4611
|
+
case 'at':co.arcTo(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]));i+=5;break;
|
|
4612
|
+
case 'lw':co.lineWidth=parseFloat(p[i+1]);i++;break;
|
|
4613
|
+
case 'e':co.ellipse(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]),parseFloat(p[i+7]),p[i+8] === 'true' ? true : false);i+=8;break;
|
|
4614
|
+
case 'lj':co.lineJoin=p[i+1];i++;break;
|
|
4615
|
+
case 'lc':co.lineCap=p[i+1];i++;break;
|
|
4616
|
+
case 'sc':co.shadowColor=p[i+1];i++;break;
|
|
4617
|
+
case 'sb':co.shadowBlur=parseFloat(p[i+1]);i++;break;
|
|
4618
|
+
case 'sx':co.shadowOffsetX=parseFloat(p[i+1]);i++;break;
|
|
4619
|
+
case 'sy':co.shadowOffsetY=parseFloat(p[i+1]);i++;break;
|
|
4620
|
+
case 'fs':co.fillStyle=p[i+1];i++;break;
|
|
4621
|
+
case 'ss':co.strokeStyle=p[i+1];i++;break;
|
|
4622
|
+
case 'fr':co.fillRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
|
|
4623
|
+
case 'sr':co.strokeRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
|
|
4624
|
+
case 'cl':co.clip();break;
|
|
4625
|
+
case 'sa':co.save();break;
|
|
4626
|
+
case 'rs':co.restore();break;
|
|
4627
|
+
case 'tr':co.translate(parseFloat(p[i+1]),parseFloat(p[i+2]));i+=2;break;
|
|
4628
|
+
case 'sl':co.scale(parseFloat(p[i+1]), parseFloat(p[i+2]));i+=2;break;
|
|
4629
|
+
case 'ro':co.rotate(parseFloat(p[i+1]));i++;break;
|
|
4630
|
+
case 'tf':co.transform(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
|
|
4631
|
+
case 'stf':co.setTransform(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]),parseFloat(p[i+5]),parseFloat(p[i+6]));i+=6;break;
|
|
4632
|
+
case 'cr':co.clearRect(parseFloat(p[i+1]),parseFloat(p[i+2]),parseFloat(p[i+3]),parseFloat(p[i+4]));i+=4;break;
|
|
4633
|
+
case 'ld':var parts = p[i+1].split(/,/);for (var j=0;j<parts.length; j++){parts[j] = parts[j].replace(/^\[/, '');parts[j] = parts[j].replace(/\]$/, '');}co.setLineDash(parts);i+=1;break;
|
|
4634
|
+
case 'ldo':co.lineDashOffset=p[i+1];i++;break;
|
|
4635
|
+
case 'fo':co.font=p[i+1];i++;break;
|
|
4636
|
+
case 'ft':co.fillText(p[i+1], parseFloat(p[i+2]), parseFloat(p[i+3]));i+=3;break;
|
|
4637
|
+
case 'st':co.strokeText(p[i+1], parseFloat(p[i+2]), parseFloat(p[i+3]));i+=3;break;
|
|
4638
|
+
case 'ta':co.textAlign=p[i+1];i++;break;
|
|
4639
|
+
case 'tbl':co.textBaseline=p[i+1];i++;break;
|
|
4640
|
+
case 'ga':co.globalAlpha=parseFloat(p[i+1]);i++;break;
|
|
4641
|
+
case 'gco':co.globalCompositeOperation=p[i+1];i++;break;
|
|
4642
|
+
|
|
4643
|
+
// Empty option - ignore it
|
|
4644
|
+
case '':break;
|
|
4645
|
+
|
|
4646
|
+
// Unknown option
|
|
4647
|
+
default: alert('[ERROR] Unknown option: ' + p[i]);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
//
|
|
4652
|
+
// This function looks for quoted args and collapses them
|
|
4653
|
+
//
|
|
4654
|
+
function collapseQuoted (arr)
|
|
4655
|
+
{
|
|
4656
|
+
var buffer = '', quote, index,out = [],quotes = ['"', "'"];
|
|
4657
|
+
|
|
4658
|
+
for (var j=0; j<quotes.length; j+=1) {
|
|
4659
|
+
for (var i=0; i<arr.length; i+=1) {
|
|
4660
|
+
|
|
4661
|
+
// Start and close quotes in the same part
|
|
4662
|
+
if (arr[i].match('/^' + quotes[j] + '/') && arr[i].match('/' + quotes[j] + '$/')) {
|
|
4663
|
+
arr[i] = arr[i].substr(1, arr[i].length - 2);
|
|
4664
|
+
}
|
|
4665
|
+
|
|
4666
|
+
// Start quoted part
|
|
4667
|
+
if (buffer.length === 0 && arr[i].indexOf(quotes[j]) !== -1) {
|
|
4668
|
+
buffer = arr[i].substr(arr[i].indexOf(quotes[j]) + 1, arr[i].length);
|
|
4669
|
+
quote = quotes[j];
|
|
4670
|
+
index = i;
|
|
4671
|
+
|
|
4672
|
+
// End quoted part
|
|
4673
|
+
} else if (quote && arr[i].indexOf(quote) !== -1) {
|
|
4674
|
+
|
|
4675
|
+
buffer = buffer + ' ' + arr[i].substr(0, arr[i].length - 1);
|
|
4676
|
+
arr[index] = buffer;
|
|
4677
|
+
arr[i] = '';
|
|
4678
|
+
quote = '';
|
|
4679
|
+
buffer = '';
|
|
4680
|
+
|
|
4681
|
+
// Add to quoted part
|
|
4682
|
+
} else if (quote) {
|
|
4683
|
+
buffer += ' ' + arr[i];
|
|
4684
|
+
arr[i] = '';
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
// Trim the array down
|
|
4690
|
+
for (i=0; i<arr.length; i+=1) {
|
|
4691
|
+
if (arr[i].length) {
|
|
4692
|
+
out.push(arr[i]);
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4696
|
+
return out;
|
|
4697
|
+
}
|
|
4698
|
+
};
|
|
4699
|
+
|
|
4700
|
+
|
|
4701
|
+
|
|
4702
|
+
|
|
4703
|
+
//
|
|
4704
|
+
// Wraps the canvas in a DIV to allow DOM text to be used
|
|
4705
|
+
//
|
|
4706
|
+
// NOT USED ANY MORE
|
|
4707
|
+
//
|
|
4708
|
+
RG.wrap = function () {};
|
|
4709
|
+
|
|
4710
|
+
|
|
4711
|
+
|
|
4712
|
+
|
|
4713
|
+
// End module pattern
|
|
4714
|
+
})(window, document);
|
|
4715
|
+
|
|
4716
|
+
|
|
4717
|
+
|
|
4718
|
+
|
|
4719
|
+
/**
|
|
4720
|
+
* Uses the alert() function to show the structure of the given variable
|
|
4721
|
+
*
|
|
4722
|
+
* @param mixed v The variable to print/alert the structure of
|
|
4723
|
+
*/
|
|
4724
|
+
window.$p = function (v)
|
|
4725
|
+
{
|
|
4726
|
+
RGraph.pr(arguments[0], arguments[1], arguments[3]);
|
|
4727
|
+
};
|
|
4728
|
+
|
|
4729
|
+
|
|
4730
|
+
|
|
4731
|
+
|
|
4732
|
+
/**
|
|
4733
|
+
* A shorthand for the default alert() function
|
|
4734
|
+
*/
|
|
4735
|
+
window.$a = function (v)
|
|
4736
|
+
{
|
|
4737
|
+
alert(v);
|
|
4738
|
+
};
|
|
4739
|
+
|
|
4740
|
+
|
|
4741
|
+
|
|
4742
|
+
|
|
4743
|
+
/**
|
|
4744
|
+
* Short-hand for console.log
|
|
4745
|
+
*
|
|
4746
|
+
* @param mixed v The variable to log to the console
|
|
4747
|
+
*/
|
|
4748
|
+
window.$cl = function (v)
|
|
4749
|
+
{
|
|
4750
|
+
return console.log(v);
|
|
4751
|
+
};
|