rgraph-rails 1.0.7 → 1.0.8

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/rgraph-rails/version.rb +1 -1
  4. data/license.txt +4 -16
  5. data/vendor/assets/javascripts/RGraph.bar.js +3734 -241
  6. data/vendor/assets/javascripts/RGraph.bipolar.js +2005 -115
  7. data/vendor/assets/javascripts/RGraph.common.annotate.js +395 -35
  8. data/vendor/assets/javascripts/RGraph.common.context.js +595 -30
  9. data/vendor/assets/javascripts/RGraph.common.core.js +5282 -405
  10. data/vendor/assets/javascripts/RGraph.common.csv.js +276 -19
  11. data/vendor/assets/javascripts/RGraph.common.deprecated.js +450 -35
  12. data/vendor/assets/javascripts/RGraph.common.dynamic.js +1395 -86
  13. data/vendor/assets/javascripts/RGraph.common.effects.js +1545 -90
  14. data/vendor/assets/javascripts/RGraph.common.key.js +753 -54
  15. data/vendor/assets/javascripts/RGraph.common.resizing.js +563 -37
  16. data/vendor/assets/javascripts/RGraph.common.sheets.js +352 -29
  17. data/vendor/assets/javascripts/RGraph.common.tooltips.js +450 -32
  18. data/vendor/assets/javascripts/RGraph.common.zoom.js +219 -14
  19. data/vendor/assets/javascripts/RGraph.drawing.background.js +570 -35
  20. data/vendor/assets/javascripts/RGraph.drawing.circle.js +544 -35
  21. data/vendor/assets/javascripts/RGraph.drawing.image.js +755 -52
  22. data/vendor/assets/javascripts/RGraph.drawing.marker1.js +645 -41
  23. data/vendor/assets/javascripts/RGraph.drawing.marker2.js +633 -37
  24. data/vendor/assets/javascripts/RGraph.drawing.marker3.js +514 -36
  25. data/vendor/assets/javascripts/RGraph.drawing.poly.js +559 -39
  26. data/vendor/assets/javascripts/RGraph.drawing.rect.js +548 -35
  27. data/vendor/assets/javascripts/RGraph.drawing.text.js +664 -36
  28. data/vendor/assets/javascripts/RGraph.drawing.xaxis.js +812 -50
  29. data/vendor/assets/javascripts/RGraph.drawing.yaxis.js +856 -51
  30. data/vendor/assets/javascripts/RGraph.fuel.js +964 -58
  31. data/vendor/assets/javascripts/RGraph.funnel.js +984 -55
  32. data/vendor/assets/javascripts/RGraph.gantt.js +1354 -77
  33. data/vendor/assets/javascripts/RGraph.gauge.js +1421 -87
  34. data/vendor/assets/javascripts/RGraph.hbar.js +2562 -146
  35. data/vendor/assets/javascripts/RGraph.hprogress.js +1401 -80
  36. data/vendor/assets/javascripts/RGraph.line.js +4226 -244
  37. data/vendor/assets/javascripts/RGraph.meter.js +1280 -74
  38. data/vendor/assets/javascripts/RGraph.modaldialog.js +301 -19
  39. data/vendor/assets/javascripts/RGraph.odo.js +1264 -71
  40. data/vendor/assets/javascripts/RGraph.pie.js +2288 -137
  41. data/vendor/assets/javascripts/RGraph.radar.js +1847 -110
  42. data/vendor/assets/javascripts/RGraph.rose.js +1977 -108
  43. data/vendor/assets/javascripts/RGraph.rscatter.js +1432 -80
  44. data/vendor/assets/javascripts/RGraph.scatter.js +3036 -168
  45. data/vendor/assets/javascripts/RGraph.semicircularprogress.js +1120 -60
  46. data/vendor/assets/javascripts/RGraph.svg.bar.js +1067 -0
  47. data/vendor/assets/javascripts/RGraph.svg.common.ajax.js +247 -0
  48. data/vendor/assets/javascripts/RGraph.svg.common.core.js +3363 -0
  49. data/vendor/assets/javascripts/RGraph.svg.common.csv.js +277 -0
  50. data/vendor/assets/javascripts/RGraph.svg.common.fx.js +1304 -0
  51. data/vendor/assets/javascripts/RGraph.svg.common.sheets.js +353 -0
  52. data/vendor/assets/javascripts/RGraph.svg.common.tooltips.js +233 -0
  53. data/vendor/assets/javascripts/RGraph.svg.hbar.js +1141 -0
  54. data/vendor/assets/javascripts/RGraph.svg.line.js +1486 -0
  55. data/vendor/assets/javascripts/RGraph.svg.pie.js +781 -0
  56. data/vendor/assets/javascripts/RGraph.svg.radar.js +1326 -0
  57. data/vendor/assets/javascripts/RGraph.svg.semicircularprogress.js +817 -0
  58. data/vendor/assets/javascripts/RGraph.thermometer.js +1135 -62
  59. data/vendor/assets/javascripts/RGraph.vprogress.js +1470 -83
  60. data/vendor/assets/javascripts/RGraph.waterfall.js +1347 -80
  61. metadata +15 -3
@@ -0,0 +1,247 @@
1
+ // version: 2017-01-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 licensed under the Open Source MIT license. That means that it's |
9
+ * | totally free to use! |
10
+ * o--------------------------------------------------------------------------------o
11
+ */
12
+
13
+ RGraph = window.RGraph || {isRGraph: true,isRGraphSVG: true};
14
+ RGraph.SVG = RGraph.SVG || {};
15
+ RGraph.SVG.AJAX = RGraph.SVG.AJAX || {};
16
+
17
+ // Module pattern
18
+ (function (win, doc, undefined)
19
+ {
20
+ var RG = RGraph,
21
+ ua = navigator.userAgent,
22
+ ma = Math;
23
+
24
+
25
+
26
+ /**
27
+ * Makes an AJAX call. It calls the given callback (a function) when ready
28
+ *
29
+ * @param string url The URL to retrieve
30
+ * @param function callback A function that is called when the response is ready, there's an example below
31
+ * called "myCallback".
32
+ */
33
+ RG.SVG.AJAX = function (url, callback)
34
+ {
35
+ // Mozilla, Safari, ...
36
+ if (window.XMLHttpRequest) {
37
+ var httpRequest = new XMLHttpRequest();
38
+
39
+ // MSIE
40
+ } else if (window.ActiveXObject) {
41
+ var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
42
+ }
43
+
44
+ httpRequest.onreadystatechange = function ()
45
+ {
46
+ if (this.readyState == 4 && this.status == 200) {
47
+ this.__user_callback__ = callback;
48
+ this.__user_callback__(this.responseText);
49
+ }
50
+ }
51
+
52
+ httpRequest.open('GET', url, true);
53
+ httpRequest.send();
54
+ };
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+ /**
66
+ * Makes an AJAX POST request. It calls the given callback (a function) when ready
67
+ *
68
+ * @param string url The URL to retrieve
69
+ * @param object data The POST data
70
+ * @param function callback A function that is called when the response is ready, there's an example below
71
+ * called "myCallback".
72
+ */
73
+ RG.SVG.AJAX.POST = function (url, data, callback)
74
+ {
75
+ // Used when building the POST string
76
+ var crumbs = [];
77
+
78
+
79
+
80
+
81
+
82
+
83
+ // Mozilla, Safari, ...
84
+ if (window.XMLHttpRequest) {
85
+ var httpRequest = new XMLHttpRequest();
86
+
87
+ // MSIE
88
+ } else if (window.ActiveXObject) {
89
+ var httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
90
+ }
91
+
92
+
93
+
94
+
95
+
96
+ httpRequest.onreadystatechange = function ()
97
+ {
98
+ if (this.readyState == 4 && this.status == 200) {
99
+ this.__user_callback__ = callback;
100
+ this.__user_callback__(this.responseText);
101
+ }
102
+ }
103
+
104
+ httpRequest.open('POST', url, true);
105
+ httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
106
+
107
+ for (i in data) {
108
+ if (typeof i == 'string') {
109
+ crumbs.push(i + '=' + encodeURIComponent(data[i]));
110
+ }
111
+ }
112
+
113
+ httpRequest.send(crumbs.join('&'));
114
+ };
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+ /**
126
+ * Uses the above function but calls the call back passing a number as its argument
127
+ *
128
+ * @param url string The URL to fetch
129
+ * @param callback function Your callback function (which is passed the number as an argument)
130
+ */
131
+ RG.SVG.AJAX.getNumber = function (url, callback)
132
+ {
133
+ RG.SVG.AJAX(url, function ()
134
+ {
135
+ var num = parseFloat(this.responseText);
136
+
137
+ callback(num);
138
+ });
139
+ };
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+ /**
151
+ * Uses the above function but calls the call back passing a string as its argument
152
+ *
153
+ * @param url string The URL to fetch
154
+ * @param callback function Your callback function (which is passed the string as an argument)
155
+ */
156
+ RG.SVG.AJAX.getString = function (url, callback)
157
+ {
158
+ RG.SVG.AJAX(url, function ()
159
+ {
160
+ var str = String(this.responseText);
161
+
162
+ callback(str);
163
+ });
164
+ };
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+
175
+ /**
176
+ * Uses the above function but calls the call back passing JSON (ie a JavaScript object ) as its argument
177
+ *
178
+ * @param url string The URL to fetch
179
+ * @param callback function Your callback function (which is passed the JSON object as an argument)
180
+ */
181
+ RG.SVG.AJAX.getJSON = function (url, callback)
182
+ {
183
+ RG.SVG.AJAX(url, function ()
184
+ {
185
+ var json = eval('(' + this.responseText + ')');
186
+
187
+ callback(json);
188
+ });
189
+ };
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+ /**
201
+ * Uses the above RGraph.AJAX function but calls the call back passing an array as its argument.
202
+ * Useful if you're retrieving CSV data
203
+ *
204
+ * @param url string The URL to fetch
205
+ * @param callback function Your callback function (which is passed the CSV/array as an argument)
206
+ */
207
+ RG.SVG.AJAX.getCSV = function (url, callback)
208
+ {
209
+ var seperator = (typeof arguments[2] === 'string' ? arguments[2] : ','),
210
+ lineSep = (typeof arguments[3] === 'string' ? arguments[3] : "\r?\n");
211
+
212
+ RG.SVG.AJAX(url, function ()
213
+ {
214
+ var text = this.responseText,
215
+ regexp = new RegExp(seperator),
216
+ lines = this.responseText.split(lineSep),
217
+ rows = [];
218
+
219
+ for (var i=0; i<lines.length; ++i) {
220
+
221
+ var row = lines[i].split(seperator);
222
+
223
+ for (var j=0,len=row.length;j<len;++j) {
224
+ if (row[j].match(/^[0-9.]+$/)) {
225
+ row[j] = parseFloat(row[j]);
226
+ }
227
+ }
228
+
229
+ rows.push(row);
230
+ }
231
+
232
+
233
+ callback(rows);
234
+ });
235
+ };
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+ // End module pattern
247
+ })(window, document);
@@ -0,0 +1,3363 @@
1
+ // version: 2017-01-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 licensed under the Open Source MIT license. That means that it's |
9
+ * | totally free to use! |
10
+ * o--------------------------------------------------------------------------------o
11
+ */
12
+
13
+ RGraph = window.RGraph || {isRGraph: true,isRGraphSVG: true};
14
+ RGraph.SVG = RGraph.SVG || {};
15
+ RGraph.SVG.FX = RGraph.SVG.FX || {};
16
+
17
+
18
+ // Module pattern
19
+ (function (win, doc, undefined)
20
+ {
21
+ var RG = RGraph,
22
+ ua = navigator.userAgent,
23
+ ma = Math;
24
+
25
+ RG.SVG.REG = {
26
+ store: []
27
+ };
28
+
29
+ // ObjectRegistry
30
+ RG.SVG.OR = {objects: []};
31
+
32
+ // Used to categorise trigonometery functions
33
+ RG.SVG.TRIG = {};
34
+ RG.SVG.TRIG.HALFPI = ma.PI * .4999;
35
+ RG.SVG.TRIG.PI = RG.SVG.TRIG.HALFPI * 2;
36
+ RG.SVG.TRIG.TWOPI = RG.SVG.TRIG.PI * 2;
37
+
38
+ RG.SVG.ISIE = ua.indexOf('rident') > 0;
39
+ RG.SVG.ISFF = ua.indexOf('irefox') > 0;
40
+
41
+ RG.SVG.events = [];
42
+
43
+
44
+ RG.SVG.ISFF = ua.indexOf('Firefox') != -1;
45
+ RG.SVG.ISOPERA = ua.indexOf('Opera') != -1;
46
+ RG.SVG.ISCHROME = ua.indexOf('Chrome') != -1;
47
+ RG.SVG.ISSAFARI = ua.indexOf('Safari') != -1 && !RG.ISCHROME;
48
+ RG.SVG.ISWEBKIT = ua.indexOf('WebKit') != -1;
49
+
50
+ RG.SVG.ISIE = ua.indexOf('Trident') > 0 || navigator.userAgent.indexOf('MSIE') > 0;
51
+ RG.SVG.ISIE6 = ua.indexOf('MSIE 6') > 0;
52
+ RG.SVG.ISIE7 = ua.indexOf('MSIE 7') > 0;
53
+ RG.SVG.ISIE8 = ua.indexOf('MSIE 8') > 0;
54
+ RG.SVG.ISIE9 = ua.indexOf('MSIE 9') > 0;
55
+ RG.SVG.ISIE10 = ua.indexOf('MSIE 10') > 0;
56
+ RG.SVG.ISIE11UP = ua.indexOf('MSIE') == -1 && ua.indexOf('Trident') > 0;
57
+ RG.SVG.ISIE10UP = RG.SVG.ISIE10 || RG.SVG.ISIE11UP;
58
+ RG.SVG.ISIE9UP = RG.SVG.ISIE9 || RG.SVG.ISIE10UP;
59
+
60
+
61
+
62
+
63
+ //
64
+ // Create an SVG tag
65
+ //
66
+ RG.SVG.createSVG = function (opt)
67
+ {
68
+ var container = opt.container;
69
+
70
+ if (container.__svg__) {
71
+ return container.__svg__;
72
+ }
73
+
74
+ var svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
75
+ svg.setAttribute('style', 'top: 0; left: 0; position: absolute');
76
+ svg.setAttribute('width', container.offsetWidth);
77
+ svg.setAttribute('height', container.offsetHeight);
78
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", 'xmlns', 'http://www.w3.org/2000/svg');
79
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
80
+ container.appendChild(svg);
81
+
82
+ container.__svg__ = svg;
83
+ container.style.position = 'relative';
84
+
85
+ return svg;
86
+ };
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+ //
96
+ // Create a defs tag inside the SVG
97
+ //
98
+ RG.SVG.createDefs = function (obj)
99
+ {
100
+ var defs = RG.SVG.create({
101
+ svg: obj.svg,
102
+ type: 'defs'
103
+ });
104
+
105
+ obj.defs = defs;
106
+
107
+ return defs;
108
+ };
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ //
118
+ // Creates a tag depending on the args that you give
119
+ //
120
+ //@param opt object The options for the function
121
+ //
122
+ RG.SVG.create = function (opt)
123
+ {
124
+ var ns = "http://www.w3.org/2000/svg",
125
+ tag = doc.createElementNS(ns, opt.type);
126
+
127
+ // Add the attributes
128
+ for (var o in opt.attr) {
129
+ if (typeof o === 'string') {
130
+
131
+ var name = o;
132
+
133
+ if (o === 'className') {
134
+ name = 'class';
135
+ }
136
+ if (opt.type === 'a' && o === 'xlink:href') {
137
+ tag.setAttributeNS('http://www.w3.org/1999/xlink', o, String(opt.attr[o]));
138
+ } else {
139
+ tag.setAttribute(name, String(opt.attr[o]));
140
+ }
141
+ }
142
+ }
143
+
144
+ // Add the style
145
+ for (var o in opt.style) {
146
+ if (typeof o === 'string') {
147
+ tag.style[o] = String(opt.style[o]);
148
+ }
149
+ }
150
+
151
+ if (opt.parent) {
152
+ opt.parent.appendChild(tag);
153
+ } else {
154
+ opt.svg.appendChild(tag);
155
+ }
156
+
157
+ return tag;
158
+ };
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+ //
168
+ // Draws an X axis
169
+ //
170
+ //@param The chart object
171
+ //
172
+ RG.SVG.drawXAxis = function (obj)
173
+ {
174
+ var prop = obj.properties;
175
+
176
+ // Draw the axis
177
+ if (prop.xaxis) {
178
+
179
+ var y = obj.type === 'hbar' ? obj.height - prop.gutterBottom : obj.getYCoord(0);
180
+
181
+ var axis = RG.SVG.create({
182
+ svg: obj.svg,
183
+ type: 'path',
184
+ attr: {
185
+ d: 'M{1} {2} L{3} {4}'.format(
186
+ prop.gutterLeft,
187
+ y + 0.01,
188
+ obj.width - prop.gutterRight,
189
+ y
190
+ ),
191
+ fill: prop.xaxisColor,
192
+ stroke: prop.xaxisColor,
193
+ 'shape-rendering': "crispEdges"
194
+ }
195
+ });
196
+
197
+
198
+ // HBar X axis
199
+ if (obj.type === 'hbar') {
200
+ var width = obj.graphWidth / obj.data.length,
201
+ x = prop.gutterLeft,
202
+ startY = (obj.height - prop.gutterBottom),
203
+ endY = (obj.height - prop.gutterBottom) + prop.xaxisTickmarksLength;
204
+
205
+ // Line/Bar X axis
206
+ } else {
207
+ var width = obj.graphWidth / obj.data.length,
208
+ x = prop.gutterLeft,
209
+ startY = obj.getYCoord(0) - (prop.yaxisMin < 0 ? prop.xaxisTickmarksLength : 0),
210
+ endY = obj.getYCoord(0) + prop.xaxisTickmarksLength;
211
+ }
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+ // Draw the tickmarks
222
+ if (prop.xaxisTickmarks) {
223
+
224
+ // TH HBar uses a scale
225
+ if (prop.xaxisScale) {
226
+
227
+ for (var i=0; i<(obj.scale.numlabels + (prop.yaxis && prop.xaxisMin === 0 ? 0 : 1)); ++i) {
228
+
229
+ if (obj.type === 'hbar') {
230
+ var dataPoints = obj.data.length;
231
+ }
232
+
233
+ x = prop.gutterLeft + ((i+(prop.yaxis && prop.xaxisMin === 0 ? 1 : 0)) * (obj.graphWidth / obj.scale.numlabels));
234
+
235
+ RG.SVG.create({
236
+ svg: obj.svg,
237
+ type: 'path',
238
+ attr: {
239
+ d: 'M{1} {2} L{3} {4}'.format(
240
+ x + 0.001,
241
+ startY,
242
+ x,
243
+ endY
244
+ ),
245
+ stroke: prop.xaxisColor,
246
+ 'shape-rendering': "crispEdges"
247
+ }
248
+ });
249
+ }
250
+
251
+
252
+
253
+
254
+ } else {
255
+
256
+ // This style is used by Bar and Scatter charts
257
+ if (prop.xaxisLabelsPosition === 'section') {
258
+
259
+ for (var i=0; i<obj.data.length; ++i) {
260
+
261
+ if (obj.type === 'bar') {
262
+ var dataPoints = obj.data.length;
263
+ } else if (obj.type === 'line'){
264
+ var dataPoints = obj.data[0].length;
265
+ }
266
+
267
+
268
+ x = prop.gutterLeft + ((i+1) * (obj.graphWidth / dataPoints));
269
+
270
+ RG.SVG.create({
271
+ svg: obj.svg,
272
+ type: 'path',
273
+ attr: {
274
+ d: 'M{1} {2} L{3} {4}'.format(
275
+ x + 0.001,
276
+ startY,
277
+ x,
278
+ endY
279
+ ),
280
+ stroke: prop.xaxisColor,
281
+ 'shape-rendering': "crispEdges"
282
+ }
283
+ });
284
+ }
285
+
286
+ // This style is used by line charts
287
+ } else if (prop.xaxisLabelsPosition === 'edge') {
288
+
289
+ if (typeof prop.xaxisLabelsPositionEdgeTickmarksCount === 'number') {
290
+ var len = prop.xaxisLabelsPositionEdgeTickmarksCount;
291
+ } else {
292
+ var len = obj.data && obj.data[0] && obj.data[0].length ? obj.data[0].length : 0;
293
+ }
294
+
295
+ for (var i=0; i<len; ++i) {
296
+
297
+ var gap = ( (obj.graphWidth) / (len - 1)),
298
+ x = prop.gutterLeft + ((i+1) * gap);
299
+
300
+ RG.SVG.create({
301
+ svg: obj.svg,
302
+ type: 'path',
303
+ attr: {
304
+ d: 'M{1} {2} L{3} {4}'.format(
305
+ x + 0.001,
306
+ startY,
307
+ x,
308
+ endY
309
+ ),
310
+ stroke: prop.xaxisColor,
311
+ 'shape-rendering': "crispEdges"
312
+ }
313
+ });
314
+ }
315
+ }
316
+ }
317
+
318
+
319
+
320
+
321
+
322
+
323
+ // Draw an extra tick if the Y axis is not being shown
324
+ if (prop.yaxis === false) {
325
+ RG.SVG.create({
326
+ svg: obj.svg,
327
+ type: 'path',
328
+ attr: {
329
+ d: 'M{1} {2} L{3} {4}'.format(
330
+ prop.gutterLeft + 0.001,
331
+ startY,
332
+ prop.gutterLeft,
333
+ endY
334
+ ),
335
+ stroke: obj.properties.xaxisColor,
336
+ 'shape-rendering': "crispEdges"
337
+ }
338
+ });
339
+ }
340
+ }
341
+ }
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+
351
+
352
+
353
+
354
+ //
355
+ // Draw an X axis scale
356
+ //
357
+ if (prop.xaxisScale) {
358
+
359
+ var segment = obj.graphWidth / prop.xaxisLabelsCount;
360
+
361
+ for (var i=0; i<obj.scale.labels.length; ++i) {
362
+
363
+ var x = prop.gutterLeft + (segment * i) + segment + prop.xaxisLabelsOffsetx;
364
+
365
+ RG.SVG.text({
366
+ object: obj,
367
+ text: obj.scale.labels[i],
368
+ x: x,
369
+ y: (obj.height - prop.gutterBottom) + (prop.xaxis ? prop.xaxisTickmarksLength + 6 : 10) + prop.xaxisLabelsOffsety,
370
+ halign: 'center',
371
+ valign: 'top',
372
+ font: prop.xaxisTextFont || prop.textFont,
373
+ size: prop.xaxisTextSize || (typeof prop.textSize === 'number' ? prop.textSize + 'pt' : prop.textSize),
374
+ bold: prop.xaxisTextBold || prop.textBold,
375
+ italic: prop.xaxisTextItalic || prop.textItalic,
376
+ color: prop.xaxisTextColor || prop.textColor
377
+ });
378
+ }
379
+
380
+
381
+
382
+
383
+
384
+ // Add the minimum label
385
+ var y = obj.height - prop.gutterBottom + prop.xaxisLabelsOffsety + (prop.xaxis ? prop.xaxisTickmarksLength + 6 : 10),
386
+ str = RG.SVG.numberFormat({
387
+ object: obj,
388
+ num: prop.xaxisMin.toFixed(prop.xaxisDecimals),
389
+ prepend: prop.xaxisUnitsPre,
390
+ append: prop.xaxisUnitsPost,
391
+ point: prop.xaxisPoint,
392
+ thousand: prop.xaxisThousand,
393
+ formatter: prop.xaxisFormatter
394
+ });
395
+
396
+ var text = RG.SVG.text({
397
+ object: obj,
398
+ text: typeof prop.xaxisFormatter === 'function' ? (prop.xaxisFormatter)(this, prop.xaxisMin) : str,
399
+ x: prop.gutterLeft + prop.xaxisLabelsOffsetx,
400
+ y: y,
401
+ halign: 'center',
402
+ valign: 'top',
403
+ font: prop.xaxisTextFont || prop.textFont,
404
+ size: prop.xaxisTextSize || (typeof prop.textSize === 'number' ? prop.textSize + 'pt' : prop.textSize),
405
+ bold: prop.xaxisTextBold || prop.textBold,
406
+ italic: prop.xaxisTextItalic || prop.textItalic,
407
+ color: prop.xaxisTextColor || prop.textColor
408
+ });
409
+
410
+ //
411
+ // Draw the X axis labels
412
+ //
413
+ } else {
414
+ if (typeof prop.xaxisLabels === 'object' && !RG.SVG.isNull(prop.xaxisLabels) ) {
415
+
416
+ // Loop through the X labels
417
+ if (prop.xaxisLabelsPosition === 'section') {
418
+
419
+ var segment = (obj.width - prop.gutterLeft - prop.gutterRight) / prop.xaxisLabels.length;
420
+
421
+ for (var i=0; i<prop.xaxisLabels.length; ++i) {
422
+
423
+ var x = prop.gutterLeft + (segment / 2) + (i * segment);
424
+
425
+ RG.SVG.text({
426
+ object: obj,
427
+ text: prop.xaxisLabels[i],
428
+ x: x + prop.xaxisLabelsOffsetx,
429
+ y: obj.height - prop.gutterBottom + (RG.SVG.ISFF ? 5 : 10) + prop.xaxisLabelsOffsety,
430
+ valign: 'top',
431
+ halign: 'center',
432
+ size: prop.xaxisTextSize || prop.textSize,
433
+ italic: prop.xaxisTextItalic || prop.textItalic,
434
+ font: prop.xaxisTextFont || prop.textFont,
435
+ bold: prop.xaxisTextBold || prop.textBold,
436
+ color: prop.xaxisTextColor || prop.textColor
437
+ });
438
+ }
439
+ } else if (prop.xaxisLabelsPosition === 'edge') {
440
+
441
+ if (obj.type === 'line') {
442
+ var hmargin = prop.hmargin;
443
+ } else {
444
+ var hmargin = 0;
445
+ }
446
+
447
+
448
+
449
+ var segment = (obj.graphWidth - hmargin - hmargin) / (prop.xaxisLabels.length - 1);
450
+
451
+ for (var i=0; i<prop.xaxisLabels.length; ++i) {
452
+
453
+ var x = prop.gutterLeft + (i * segment) + hmargin;
454
+
455
+ RG.SVG.text({
456
+ object: obj,
457
+ text: prop.xaxisLabels[i],
458
+ x: x + prop.xaxisLabelsOffsetx,
459
+ y: obj.height - prop.gutterBottom + (RG.SVG.ISFF ? 5 : 10) + (prop.xaxisTickmarksLength - 5) + prop.xaxisLabelsOffsety,
460
+ valign: 'top',
461
+ halign: 'center',
462
+ size: prop.xaxisTextSize || prop.textSize,
463
+ italic: prop.xaxisTextItalic || prop.textItalic,
464
+ font: prop.xaxisTextFont || prop.textFont,
465
+ bold: prop.xaxisTextBold || prop.textBold,
466
+ color: prop.xaxisTextColor || prop.textColor
467
+ });
468
+ }
469
+ }
470
+ }
471
+ }
472
+ };
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+ //
482
+ // Draws an Y axis
483
+ //
484
+ //@param The chart object
485
+ //
486
+ RG.SVG.drawYAxis = function (obj)
487
+ {
488
+ var prop = obj.properties;
489
+
490
+ if (prop.yaxis) {
491
+
492
+ // The X coordinate that the Y axis is positioned at
493
+ var x = obj.type === 'hbar' ? obj.getXCoord(0) : prop.gutterLeft;
494
+
495
+ var axis = RG.SVG.create({
496
+ svg: obj.svg,
497
+ type: 'path',
498
+ attr: {
499
+ d: 'M{1} {2} L{3} {4}'.format(
500
+ x,
501
+ prop.gutterTop,
502
+ x + 0.001,
503
+ obj.height - prop.gutterBottom
504
+ ),
505
+ stroke: prop.yaxisColor,
506
+ fill: prop.yaxisColor,
507
+ 'shape-rendering': "crispEdges"
508
+ }
509
+ });
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+ if (obj.type === 'hbar') {
518
+
519
+ var height = obj.graphHeight / prop.yaxisLabels.length,
520
+ y = prop.gutterTop,
521
+ len = prop.yaxisLabels.length,
522
+ startX = obj.getXCoord(0) + (prop.xaxisMin < 0 ? prop.yaxisTickmarksLength : 0),
523
+ endX = obj.getXCoord(0) - prop.yaxisTickmarksLength;
524
+
525
+ //
526
+ // Draw the tickmarks
527
+ //
528
+ if (prop.yaxisTickmarks) {
529
+ for (var i=0; i<len; ++i) {
530
+
531
+ // Draw the axis
532
+ var axis = RG.SVG.create({
533
+ svg: obj.svg,
534
+ type: 'path',
535
+ attr: {
536
+ d: 'M{1} {2} L{3} {4}'.format(
537
+ startX,
538
+ y,
539
+ endX,
540
+ y + 0.001
541
+ ),
542
+ stroke: prop.yaxisColor,
543
+ 'shape-rendering': "crispEdges"
544
+ }
545
+ });
546
+
547
+ y += height;
548
+ }
549
+
550
+
551
+ // Draw an extra tick if the X axis position is not zero or
552
+ //if the xaxis is not being shown
553
+ if (prop.xaxis === false) {
554
+ var axis = RG.SVG.create({
555
+ svg: obj.svg,
556
+ type: 'path',
557
+ attr: {
558
+ d: 'M{1} {2} L{3} {4}'.format(
559
+ obj.getXCoord(0) - prop.yaxisTickmarksLength - 1,
560
+ obj.height - prop.gutterBottom,
561
+ obj.getXCoord(0) + (obj.type === 'hbar' && prop.xaxisMin < 0 ? 3 : 0),
562
+ obj.height - prop.gutterBottom - 0.001
563
+ ),
564
+ stroke: obj.properties.yaxisColor,
565
+ 'shape-rendering': "crispEdges"
566
+ }
567
+ });
568
+ }
569
+ }
570
+
571
+ //
572
+ // Bar, Line etc types of chart
573
+ //
574
+ } else {
575
+
576
+ var height = obj.graphHeight / prop.yaxisLabelsCount,
577
+ y = prop.gutterTop,
578
+ len = prop.yaxisLabelsCount,
579
+ startX = prop.gutterLeft,
580
+ endX = prop.gutterLeft - prop.yaxisTickmarksLength;
581
+
582
+ //
583
+ // Draw the tickmarks
584
+ //
585
+ if (prop.yaxisTickmarks) {
586
+ for (var i=0; i<len; ++i) {
587
+
588
+ // Draw the axis
589
+ var axis = RG.SVG.create({
590
+ svg: obj.svg,
591
+ type: 'path',
592
+ attr: {
593
+ d: 'M{1} {2} L{3} {4}'.format(
594
+ startX,
595
+ y,
596
+ endX,
597
+ y + 0.001
598
+ ),
599
+ stroke: prop.yaxisColor,
600
+ 'shape-rendering': "crispEdges"
601
+ }
602
+ });
603
+
604
+ y += height;
605
+ }
606
+
607
+
608
+ // Draw an extra tick if the X axis position is not zero or
609
+ //if the xaxis is not being shown
610
+ if (obj.type !== 'hbar' && (prop.yaxisMin !== 0 || prop.xaxis === false)) {
611
+ var axis = RG.SVG.create({
612
+ svg: obj.svg,
613
+ type: 'path',
614
+ attr: {
615
+ d: 'M{1} {2} L{3} {4}'.format(
616
+ prop.gutterLeft - prop.yaxisTickmarksLength,
617
+ obj.height - prop.gutterBottom,
618
+ prop.gutterLeft,
619
+ obj.height - prop.gutterBottom - 0.001
620
+ ),
621
+ stroke: prop.yaxisColor,
622
+ 'shape-rendering': "crispEdges"
623
+ }
624
+ });
625
+ }
626
+ }
627
+ }
628
+ }
629
+
630
+
631
+
632
+
633
+
634
+
635
+ //
636
+ // Draw the Y axis labels
637
+ //
638
+ if (prop.yaxisScale) {
639
+
640
+ var segment = (obj.height - prop.gutterTop - prop.gutterBottom) / prop.yaxisLabelsCount;
641
+
642
+ for (var i=0; i<obj.scale.labels.length; ++i) {
643
+
644
+ var y = obj.height - prop.gutterBottom - (segment * i) - segment;
645
+
646
+ RG.SVG.text({
647
+ object: obj,
648
+ text: obj.scale.labels[i],
649
+ x: prop.gutterLeft - 7 - (prop.yaxis ? (prop.yaxisTickmarksLength - 3) : 0) + prop.yaxisLabelsOffsetx,
650
+ y: y + prop.yaxisLabelsOffsety,
651
+ halign: 'right',
652
+ valign: 'center',
653
+ font: prop.yaxisTextFont || prop.textFont,
654
+ size: prop.yaxisTextSize || (typeof prop.textSize === 'number' ? prop.textSize + 'pt' : prop.textSize),
655
+ bold: prop.yaxisTextBold || prop.textBold,
656
+ italic: prop.yaxisTextItalic || prop.textItalic,
657
+ color: prop.yaxisTextColor || prop.textColor
658
+ });
659
+ }
660
+
661
+
662
+
663
+
664
+ //
665
+ // Add the minimum label
666
+ //
667
+ var y = obj.height - prop.gutterBottom,
668
+ str = (prop.yaxisUnitsPre + prop.yaxisMin.toFixed(prop.yaxisDecimals).replace(/\./, prop.yaxisPoint) + prop.yaxisUnitsPost);
669
+
670
+ var text = RG.SVG.text({
671
+ object: obj,
672
+ text: typeof prop.yaxisFormatter === 'function' ? (prop.yaxisFormatter)(this, prop.yaxisMin) : str,
673
+ x: prop.gutterLeft - 7 - (prop.yaxis ? (prop.yaxisTickmarksLength - 3) : 0) + prop.yaxisLabelsOffsetx,
674
+ y: y + prop.yaxisLabelsOffsety,
675
+ halign: 'right',
676
+ valign: 'center',
677
+ font: prop.yaxisTextFont || prop.textFont,
678
+ size: prop.yaxisTextSize || (typeof prop.textSize === 'number' ? prop.textSize + 'pt' : prop.textSize),
679
+ bold: prop.yaxisTextBold || prop.textBold,
680
+ italic: prop.yaxisTextItalic || prop.textItalic,
681
+ color: prop.yaxisTextColor || prop.textColor
682
+ });
683
+
684
+
685
+ //
686
+ // Draw Y axis labels (eg when specific labels are defined or
687
+ //the chart is an HBar
688
+ //
689
+ } else if (prop.yaxisLabels && prop.yaxisLabels.length) {
690
+
691
+ for (var i=0; i<prop.yaxisLabels.length; ++i) {
692
+
693
+ var segment = obj.graphHeight / prop.yaxisLabels.length,
694
+ y = prop.gutterTop + (segment * i) + (segment / 2) + prop.yaxisLabelsOffsety;
695
+
696
+ var text = RG.SVG.text({
697
+ object: obj,
698
+ text: prop.yaxisLabels[i] ? prop.yaxisLabels[i] : '',
699
+ x: prop.gutterLeft - 7 /*- (prop.yaxis ? (prop.yaxisTickmarksLength) : 0)*/ + prop.yaxisLabelsOffsetx,
700
+ y: y,
701
+ halign: 'right',
702
+ valign: 'center',
703
+ font: prop.yaxisTextFont || prop.textFont,
704
+ size: prop.yaxisTextSize || (typeof prop.textSize === 'number' ? prop.textSize + 'pt' : prop.textSize),
705
+ bold: prop.yaxisTextBold || prop.textBold,
706
+ italic: prop.yaxisTextItalic || prop.textItalic,
707
+ color: prop.yaxisTextColor || prop.textColor
708
+ });
709
+ }
710
+ }
711
+ };
712
+
713
+
714
+
715
+
716
+
717
+
718
+
719
+
720
+ //
721
+ // Draws the background
722
+ //
723
+ //@param The chart object
724
+ //
725
+ RG.SVG.drawBackground = function (obj)
726
+ {
727
+ var prop = obj.properties;
728
+
729
+ if (prop .backgroundGrid) {
730
+
731
+ var parts = [];
732
+
733
+ // Add the horizontal lines to the path
734
+ if (prop.backgroundGridHlines) {
735
+
736
+ var count = typeof prop.backgroundGridHlinesCount === 'number' ? prop.backgroundGridHlinesCount : (obj.type === 'hbar' ? (prop.yaxisLabels.length || obj.data.length || 5) : prop.yaxisLabelsCount);
737
+
738
+ for (var i=0; i<count; ++i) {
739
+ parts.push('M{1} {2} L{3} {4}'.format(
740
+ prop.gutterLeft,
741
+ prop.gutterTop + (obj.graphHeight / count) * i,
742
+ obj.width - prop.gutterRight,
743
+ prop.gutterTop + (obj.graphHeight / count) * i
744
+ ));
745
+ }
746
+
747
+ // Add an extra background grid line to the path - this its
748
+ // underneath the X axis and shows up if its not there.
749
+ parts.push('M{1} {2} L{3} {4}'.format(
750
+ prop.gutterLeft,
751
+ obj.height - prop.gutterBottom,
752
+ obj.width - prop.gutterRight,
753
+ obj.height - prop.gutterBottom
754
+ ));
755
+ }
756
+
757
+
758
+
759
+ // Add the vertical lines to the path
760
+ if (prop.backgroundGridVlines) {
761
+
762
+ if (obj.type === 'line' && RG.SVG.isArray(obj.data[0])) {
763
+ var len = obj.data[0].length;
764
+ } else if (obj.type === 'hbar') {
765
+ var len = prop.xaxisLabelsCount;
766
+ } else {
767
+ var len = obj.data.length;
768
+ }
769
+
770
+ var count = typeof prop.backgroundGridVlinesCount === 'number' ? prop.backgroundGridVlinesCount : len;
771
+
772
+ if (prop.xaxisLabelsPosition === 'edge') {
773
+ count--;
774
+ }
775
+
776
+ for (var i=0; i<=count; ++i) {
777
+ parts.push('M{1} {2} L{3} {4}'.format(
778
+ prop.gutterLeft + ((obj.graphWidth / count) * i),
779
+ prop.gutterTop,
780
+ prop.gutterLeft + ((obj.graphWidth / count) * i),
781
+ obj.height - prop.gutterBottom
782
+ ));
783
+ }
784
+ }
785
+
786
+
787
+
788
+
789
+
790
+ // Add the box around the grid
791
+ if (prop.backgroundGridBorder) {
792
+ parts.push('M{1} {2} L{3} {4} L{5} {6} L{7} {8} z'.format(
793
+
794
+ prop.gutterLeft,
795
+ prop.gutterTop,
796
+
797
+ obj.width - prop.gutterRight,
798
+ prop.gutterTop,
799
+
800
+ obj.width - prop.gutterRight,
801
+ obj.height - prop.gutterBottom,
802
+
803
+ prop.gutterLeft,
804
+ obj.height - prop.gutterBottom
805
+ ));
806
+ }
807
+
808
+
809
+
810
+ // Now draw the path
811
+ var grid = RG.SVG.create({
812
+ svg: obj.svg,
813
+ type: 'path',
814
+ attr: {
815
+ d: parts.join(' '),
816
+ stroke: prop.backgroundGridColor,
817
+ fill: 'rgba(0,0,0,0)',
818
+ 'stroke-width': prop.backgroundGridLinewidth,
819
+ 'shape-rendering': "crispEdges"
820
+ }
821
+ });
822
+
823
+ }
824
+
825
+
826
+
827
+ // Draw the title and subtitle
828
+ RG.SVG.drawTitle(obj);
829
+ };
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+ /**
839
+ * Returns true/false as to whether the given variable is null or not
840
+ *
841
+ * @param mixed arg The argument to check
842
+ */
843
+ RG.SVG.isNull = function (arg)
844
+ {
845
+ // must BE DOUBLE EQUALS - NOT TRIPLE
846
+ if (arg == null || typeof arg === 'object' && !arg) {
847
+ return true;
848
+ }
849
+
850
+ return false;
851
+ };
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+ /**
861
+ * Returns an appropriate scale. The return value is actualy an object consisting of:
862
+ * scale.max
863
+ * scale.min
864
+ * scale.scale
865
+ *
866
+ * @param obj object The graph object
867
+ * @param prop object An object consisting of configuration properties
868
+ * @return object An object containg scale information
869
+ */
870
+ RG.SVG.getScale = function (opt)
871
+ {
872
+ var obj = opt.object,
873
+ prop = obj.properties,
874
+ numlabels = opt.numlabels,
875
+ unitsPre = opt.unitsPre,
876
+ unitsPost = opt.unitsPost,
877
+ max = Number(opt.max),
878
+ min = Number(opt.min),
879
+ strict = opt.strict,
880
+ decimals = Number(opt.decimals),
881
+ point = opt.point,
882
+ thousand = opt.thousand,
883
+ originalMax = max,
884
+ round = opt.round,
885
+ scale = {max:1,labels:[],values:[]},
886
+ formatter = opt.formatter;
887
+
888
+
889
+ /**
890
+ * Special case for 0
891
+ *
892
+ * ** Must be first **
893
+ */
894
+
895
+ if (!max) {
896
+
897
+ var max = 1;
898
+
899
+ for (var i=0; i<numlabels; ++i) {
900
+
901
+ var label = ((((max - min) / numlabels) + min) * (i + 1)).toFixed(decimals);
902
+
903
+ scale.labels.push(unitsPre + label + unitsPost);
904
+ scale.values.push(parseFloat(label))
905
+ }
906
+
907
+ /**
908
+ * Manually do decimals
909
+ */
910
+ } else if (max <= 1 && !strict) {
911
+
912
+ var arr = [
913
+ 1,0.5,
914
+ 0.10,0.05,
915
+ 0.010,0.005,
916
+ 0.0010,0.0005,
917
+ 0.00010,0.00005,
918
+ 0.000010,0.000005,
919
+ 0.0000010,0.0000005,
920
+ 0.00000010,0.00000005,
921
+ 0.000000010,0.000000005,
922
+ 0.0000000010,0.0000000005,
923
+ 0.00000000010,0.00000000005,
924
+ 0.000000000010,0.000000000005,
925
+ 0.0000000000010,0.0000000000005
926
+ ], vals = [];
927
+
928
+
929
+
930
+ for (var i=0; i<arr.length; ++i) {
931
+ if (max > arr[i]) {
932
+ i--;
933
+ break;
934
+ }
935
+ }
936
+
937
+
938
+ scale.max = arr[i]
939
+ scale.labels = [];
940
+ scale.values = [];
941
+
942
+
943
+ for (var j=0; j<numlabels; ++j) {
944
+
945
+ var value = ((((arr[i] - min) / numlabels) * (j + 1)) + min).toFixed(decimals);
946
+
947
+ scale.values.push(value);
948
+ scale.labels.push(RG.SVG.numberFormat({
949
+ object: obj,
950
+ num: value,
951
+ prepend: unitsPre,
952
+ append: unitsPost,
953
+ point: prop.yaxisPoint,
954
+ thousand: prop.yaxisThousand,
955
+ formatter: formatter
956
+ }));
957
+ }
958
+
959
+
960
+
961
+
962
+ } else if (!strict) {
963
+
964
+ /**
965
+ * Now comes the scale handling for integer values
966
+ */
967
+
968
+ // This accomodates decimals by rounding the max up to the next integer
969
+ max = ma.ceil(max);
970
+
971
+ var interval = ma.pow(10, ma.max(1, Number(String(Number(max) - Number(min)).length - 1)) );
972
+ var topValue = interval;
973
+
974
+ while (topValue < max) {
975
+ topValue += (interval / 2);
976
+ }
977
+
978
+ // Handles cases where the max is (for example) 50.5
979
+ if (Number(originalMax) > Number(topValue)) {
980
+ topValue += (interval / 2);
981
+ }
982
+
983
+ // Custom if the max is greater than 5 and less than 10
984
+ if (max <= 10) {
985
+ topValue = (Number(originalMax) <= 5 ? 5 : 10);
986
+ }
987
+
988
+
989
+ // Added 02/11/2010 to create "nicer" scales
990
+ if (obj && typeof(round) == 'boolean' && round) {
991
+ topValue = 10 * interval;
992
+ }
993
+
994
+ scale.max = topValue;
995
+
996
+
997
+ for (var i=0; i<numlabels; ++i) {
998
+
999
+ var label = RG.SVG.numberFormat({
1000
+ object: obj,
1001
+ num: ((((i+1) / numlabels) * (topValue - min)) + min).toFixed(decimals),
1002
+ prepend: unitsPre,
1003
+ append: unitsPost,
1004
+ point: point,
1005
+ thousand: thousand,
1006
+ formatter: formatter
1007
+ });
1008
+
1009
+ scale.labels.push(label);
1010
+ scale.values.push(((((i+1) / numlabels) * (topValue - min)) + min).toFixed(decimals));
1011
+ }
1012
+
1013
+ } else if (typeof max === 'number' && strict) {
1014
+
1015
+ /**
1016
+ * ymax is set and also strict
1017
+ */
1018
+ for (var i=0; i<numlabels; ++i) {
1019
+
1020
+ scale.labels.push(RG.SVG.numberFormat({
1021
+ object: obj,
1022
+ formatter: formatter,
1023
+ num: ((((i+1) / numlabels) * (max - min)) + min).toFixed(decimals),
1024
+ prepend: unitsPre,
1025
+ append: unitsPost,
1026
+ point: point,
1027
+ thousand: thousand
1028
+ }));
1029
+
1030
+
1031
+ scale.values.push(
1032
+ ((((i+1) / numlabels) * (max - min)) + min).toFixed(decimals)
1033
+ );
1034
+ }
1035
+
1036
+ // ???
1037
+ scale.max = max;
1038
+ }
1039
+
1040
+
1041
+ scale.unitsPre = unitsPre;
1042
+ scale.unitsPost = unitsPost;
1043
+ scale.point = point;
1044
+ scale.decimals = decimals;
1045
+ scale.thousand = thousand;
1046
+ scale.numlabels = numlabels;
1047
+ scale.round = Boolean(round);
1048
+ scale.min = min;
1049
+
1050
+ //
1051
+ // Convert all of the scale values to numbers
1052
+ //
1053
+ for (var i=0; i<scale.values.length; ++i) {
1054
+ scale.values[i] = parseFloat(scale.values[i]);
1055
+ }
1056
+
1057
+ return scale;
1058
+ };
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+
1065
+
1066
+
1067
+ /**
1068
+ * Pads/fills the array
1069
+ *
1070
+ * @param array arr The array
1071
+ * @param int len The length to pad the array to
1072
+ * @param mixed The value to use to pad the array (optional)
1073
+ */
1074
+ RG.SVG.arrayFill =
1075
+ RG.SVG.arrayPad = function (opt)
1076
+ {
1077
+ var arr = opt.array,
1078
+ len = opt.length,
1079
+ value = (opt.value ? opt.value : null);
1080
+
1081
+ if (arr.length < len) {
1082
+ for (var i=arr.length; i<len; i+=1) {
1083
+ arr[i] = value;
1084
+ }
1085
+ }
1086
+
1087
+ return arr;
1088
+ };
1089
+
1090
+
1091
+
1092
+
1093
+
1094
+
1095
+
1096
+
1097
+ /**
1098
+ * An array sum function
1099
+ *
1100
+ * @param array arr The array to calculate the total of
1101
+ * @return int The summed total of the arrays elements
1102
+ */
1103
+ RG.SVG.arraySum = function (arr)
1104
+ {
1105
+ // Allow integers
1106
+ if (typeof arr === 'number') {
1107
+ return arr;
1108
+ }
1109
+
1110
+ // Account for null
1111
+ if (RG.SVG.isNull(arr)) {
1112
+ return 0;
1113
+ }
1114
+
1115
+ var i, sum, len = arr.length;
1116
+
1117
+ for(i=0,sum=0;i<len;sum+=arr[i++]);
1118
+
1119
+ return sum;
1120
+ };
1121
+
1122
+
1123
+
1124
+
1125
+
1126
+
1127
+
1128
+
1129
+ /**
1130
+ * Returns the maximum numeric value which is in an array. This function IS NOT
1131
+ * recursive
1132
+ *
1133
+ * @param array arr The array (can also be a number, in which case it's returned as-is)
1134
+ * @param int Whether to ignore signs (ie negative/positive)
1135
+ * @return int The maximum value in the array
1136
+ */
1137
+ RG.SVG.arrayMax = function (arr)
1138
+ {
1139
+ var max = null
1140
+
1141
+ if (typeof arr === 'number') {
1142
+ return arr;
1143
+ }
1144
+
1145
+ if (RG.SVG.isNull(arr)) {
1146
+ return 0;
1147
+ }
1148
+
1149
+ for (var i=0,len=arr.length; i<len; ++i) {
1150
+ if (typeof arr[i] === 'number') {
1151
+
1152
+ var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
1153
+
1154
+ if (typeof max === 'number') {
1155
+ max = ma.max(max, val);
1156
+ } else {
1157
+ max = val;
1158
+ }
1159
+ }
1160
+ }
1161
+
1162
+ return max;
1163
+ };
1164
+
1165
+
1166
+
1167
+
1168
+
1169
+
1170
+
1171
+
1172
+ /**
1173
+ * Returns the minimum numeric value which is in an array
1174
+ *
1175
+ * @param array arr The array (can also be a number, in which case it's returned as-is)
1176
+ * @param int Whether to ignore signs (ie negative/positive)
1177
+ * @return int The minimum value in the array
1178
+ */
1179
+ RG.SVG.arrayMin = function (arr)
1180
+ {
1181
+ var max = null,
1182
+ min = null,
1183
+ ma = Math;
1184
+
1185
+ if (typeof arr === 'number') {
1186
+ return arr;
1187
+ }
1188
+
1189
+ if (RG.SVG.isNull(arr)) {
1190
+ return 0;
1191
+ }
1192
+
1193
+ for (var i=0,len=arr.length; i<len; ++i) {
1194
+ if (typeof arr[i] === 'number') {
1195
+
1196
+ var val = arguments[1] ? ma.abs(arr[i]) : arr[i];
1197
+
1198
+ if (typeof min === 'number') {
1199
+ min = ma.min(min, val);
1200
+ } else {
1201
+ min = val;
1202
+ }
1203
+ }
1204
+ }
1205
+
1206
+ return min;
1207
+ };
1208
+
1209
+
1210
+
1211
+
1212
+
1213
+
1214
+
1215
+
1216
+ /**
1217
+ * Returns the maximum value which is in an array
1218
+ *
1219
+ * @param array arr The array
1220
+ * @param int len The length to pad the array to
1221
+ * @param mixed The value to use to pad the array (optional)
1222
+ */
1223
+ RG.SVG.arrayPad = function (arr, len)
1224
+ {
1225
+ if (arr.length < len) {
1226
+ var val = arguments[2] ? arguments[2] : null;
1227
+
1228
+ for (var i=arr.length; i<len; i+=1) {
1229
+ arr[i] = val;
1230
+ }
1231
+ }
1232
+
1233
+ return arr;
1234
+ };
1235
+
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+
1242
+
1243
+ /**
1244
+ * An array sum function
1245
+ *
1246
+ * @param array arr The array to calculate the total of
1247
+ * @return int The summed total of the arrays elements
1248
+ */
1249
+ RG.SVG.arraySum = function (arr)
1250
+ {
1251
+ // Allow integers
1252
+ if (typeof arr === 'number') {
1253
+ return arr;
1254
+ }
1255
+
1256
+ // Account for null
1257
+ if (RG.SVG.isNull(arr)) {
1258
+ return 0;
1259
+ }
1260
+
1261
+ var i, sum, len = arr.length;
1262
+
1263
+ for(i=0,sum=0;i<len;sum+=arr[i++]);
1264
+
1265
+ return sum;
1266
+ };
1267
+
1268
+
1269
+
1270
+
1271
+
1272
+
1273
+
1274
+
1275
+ /**
1276
+ * Takes any number of arguments and adds them to one big linear array
1277
+ * which is then returned
1278
+ *
1279
+ * @param ... mixed The data to linearise. You can strings, booleans, numbers or arrays
1280
+ */
1281
+ RG.SVG.arrayLinearize = function ()
1282
+ {
1283
+ var arr = [],
1284
+ args = arguments
1285
+
1286
+ for (var i=0,len=args.length; i<len; ++i) {
1287
+
1288
+ if (typeof args[i] === 'object' && args[i]) {
1289
+ for (var j=0,len2=args[i].length; j<len2; ++j) {
1290
+ var sub = RG.SVG.arrayLinearize(args[i][j]);
1291
+
1292
+ for (var k=0,len3=sub.length; k<len3; ++k) {
1293
+ arr.push(sub[k]);
1294
+ }
1295
+ }
1296
+ } else {
1297
+ arr.push(args[i]);
1298
+ }
1299
+ }
1300
+
1301
+ return arr;
1302
+ };
1303
+
1304
+
1305
+
1306
+
1307
+
1308
+
1309
+
1310
+
1311
+ /**
1312
+ * Takes one off the front of the given array and returns the new array.
1313
+ *
1314
+ * @param array arr The array from which to take one off the front of array
1315
+ *
1316
+ * @return array The new array
1317
+ */
1318
+ RG.SVG.arrayShift = function(arr)
1319
+ {
1320
+ var ret = [];
1321
+
1322
+ for(var i=1,len=arr.length; i<len; ++i) {
1323
+ ret.push(arr[i]);
1324
+ }
1325
+
1326
+ return ret;
1327
+ };
1328
+
1329
+
1330
+
1331
+
1332
+
1333
+
1334
+
1335
+
1336
+ /**
1337
+ * Reverses the order of an array
1338
+ *
1339
+ * @param array arr The array to reverse
1340
+ */
1341
+ RG.SVG.arrayReverse = function (arr)
1342
+ {
1343
+ if (!arr) {
1344
+ return;
1345
+ }
1346
+
1347
+ var newarr=[];
1348
+
1349
+ for(var i=arr.length - 1; i>=0; i-=1) {
1350
+ newarr.push(arr[i]);
1351
+ }
1352
+
1353
+ return newarr;
1354
+ };
1355
+
1356
+
1357
+
1358
+
1359
+
1360
+
1361
+
1362
+
1363
+ /**
1364
+ * Makes a clone of an object
1365
+ *
1366
+ * @param obj val The object to clone
1367
+ */
1368
+ RG.SVG.arrayClone = function (obj)
1369
+ {
1370
+ if(obj === null || typeof obj !== 'object') {
1371
+ return obj;
1372
+ }
1373
+
1374
+ if (RG.SVG.isArray(obj)) {
1375
+
1376
+ var temp = [];
1377
+
1378
+ for (var i=0,len=obj.length;i<len; ++i) {
1379
+
1380
+ if (typeof obj[i] === 'number') {
1381
+ temp[i] = (function (arg) {return Number(arg);})(obj[i]);
1382
+
1383
+ } else if (typeof obj[i] === 'string') {
1384
+ temp[i] = (function (arg) {return String(arg);})(obj[i]);
1385
+
1386
+ } else if (typeof obj[i] === 'function') {
1387
+ temp[i] = obj[i];
1388
+
1389
+ } else {
1390
+ temp[i] = RG.SVG.arrayClone(obj[i]);
1391
+ }
1392
+ }
1393
+ } else if (typeof obj === 'object') {
1394
+
1395
+ var temp = {};
1396
+
1397
+ for (var i in obj) {
1398
+ if (typeof i === 'string') {
1399
+ temp[i] = obj[i];
1400
+ }
1401
+ }
1402
+ }
1403
+
1404
+ return temp;
1405
+ };
1406
+
1407
+
1408
+
1409
+
1410
+
1411
+
1412
+
1413
+
1414
+ //
1415
+ // Converts an the truthy values to falsey values and vice-versa
1416
+ //
1417
+ RG.SVG.arrayInvert = function (arr)
1418
+ {
1419
+ for (var i=0,len=arr.length; i<len; ++i) {
1420
+ arr[i] = !arr[i];
1421
+ }
1422
+
1423
+ return arr;
1424
+ };
1425
+
1426
+
1427
+
1428
+
1429
+
1430
+
1431
+
1432
+
1433
+ //
1434
+ // An array_trim function that removes the empty elements off
1435
+ //both ends
1436
+ //
1437
+ RG.SVG.arrayTrim = function (arr)
1438
+ {
1439
+ var out = [], content = false;
1440
+
1441
+ // Trim the start
1442
+ for (var i=0; i<arr.length; i++) {
1443
+
1444
+ if (arr[i]) {
1445
+ content = true;
1446
+ }
1447
+
1448
+ if (content) {
1449
+ out.push(arr[i]);
1450
+ }
1451
+ }
1452
+
1453
+ // Reverse the array and trim the start again
1454
+ out = RG.SVG.arrayReverse(out);
1455
+
1456
+ var out2 = [], content = false ;
1457
+ for (var i=0; i<out.length; i++) {
1458
+
1459
+ if (out[i]) {
1460
+ content = true;
1461
+ }
1462
+
1463
+ if (content) {
1464
+ out2.push(out[i]);
1465
+ }
1466
+ }
1467
+
1468
+ // Now reverse the array and return it
1469
+ out2 = RG.SVG.arrayReverse(out2);
1470
+
1471
+ return out2;
1472
+ };
1473
+
1474
+
1475
+
1476
+
1477
+
1478
+
1479
+
1480
+
1481
+ /**
1482
+ * Determines if the given object is an array or not
1483
+ *
1484
+ * @param mixed obj The variable to test
1485
+ */
1486
+ RG.SVG.isArray = function (obj)
1487
+ {
1488
+ if (obj && obj.constructor) {
1489
+ var pos = obj.constructor.toString().indexOf('Array');
1490
+ } else {
1491
+ return false;
1492
+ }
1493
+
1494
+ return obj != null &&
1495
+ typeof pos === 'number' &&
1496
+ pos > 0 &&
1497
+ pos < 20;
1498
+ };
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+ /**
1508
+ * Returns the absolute value of a number. You can also pass in an
1509
+ * array and it will run the abs() function on each element. It
1510
+ * operates recursively so sub-arrays are also traversed.
1511
+ *
1512
+ * @param array arr The number or array to work on
1513
+ */
1514
+ RG.SVG.abs = function (value)
1515
+ {
1516
+ if (typeof value === 'string') {
1517
+ value = parseFloat(value) || 0;
1518
+ }
1519
+
1520
+ if (typeof value === 'number') {
1521
+ return ma.abs(value);
1522
+ }
1523
+
1524
+ if (typeof value === 'object') {
1525
+ for (i in value) {
1526
+ if ( typeof i === 'string'
1527
+ || typeof i === 'number'
1528
+ || typeof i === 'object') {
1529
+
1530
+ value[i] = RG.SVG.abs(value[i]);
1531
+ }
1532
+ }
1533
+
1534
+ return value;
1535
+ }
1536
+
1537
+ return 0;
1538
+ };
1539
+
1540
+
1541
+
1542
+
1543
+
1544
+
1545
+
1546
+
1547
+ //
1548
+ // Formats a number with thousand seperators so it's easier to read
1549
+ //
1550
+ // @param opt object The options to the function
1551
+ //
1552
+ RG.SVG.numberFormat = function (opt)
1553
+ {
1554
+ var obj = opt.object,
1555
+ prepend = opt.prepend ? String(opt.prepend) : '',
1556
+ append = opt.append ? String(opt.append) : '',
1557
+ output = '',
1558
+ decimal_seperator = typeof opt.point === 'string' ? opt.point : '.',
1559
+ thousand_seperator = typeof opt.thousand === 'string' ? opt.thousand : ',',
1560
+ num = opt.num;
1561
+
1562
+ RegExp.$1 = '';
1563
+
1564
+ if (typeof opt.formatter === 'function') {
1565
+ return opt.formatter(obj, num);
1566
+ }
1567
+
1568
+ // Ignore the preformatted version of "1e-2"
1569
+ if (String(num).indexOf('e') > 0) {
1570
+ return String(prepend + String(num) + append);
1571
+ }
1572
+
1573
+ // We need then number as a string
1574
+ num = String(num);
1575
+
1576
+ // Take off the decimal part - we re-append it later
1577
+ if (num.indexOf('.') > 0) {
1578
+ var tmp = num;
1579
+ num = num.replace(/\.(.*)/, ''); // The front part of the number
1580
+ decimal = tmp.replace(/(.*)\.(.*)/, '$2'); // The decimal part of the number
1581
+ } else {
1582
+ decimal = '';
1583
+ }
1584
+
1585
+ // Thousand seperator
1586
+ //var seperator = arguments[1] ? String(arguments[1]) : ',';
1587
+ var seperator = thousand_seperator;
1588
+
1589
+ /**
1590
+ * Work backwards adding the thousand seperators
1591
+ */
1592
+ var foundPoint;
1593
+ for (i=(num.length - 1),j=0; i>=0; j++,i--) {
1594
+ var character = num.charAt(i);
1595
+
1596
+ if ( j % 3 == 0 && j != 0) {
1597
+ output += seperator;
1598
+ }
1599
+
1600
+ /**
1601
+ * Build the output
1602
+ */
1603
+ output += character;
1604
+ }
1605
+
1606
+ /**
1607
+ * Now need to reverse the string
1608
+ */
1609
+ var rev = output;
1610
+ output = '';
1611
+ for (i=(rev.length - 1); i>=0; i--) {
1612
+ output += rev.charAt(i);
1613
+ }
1614
+
1615
+ // Tidy up
1616
+ //output = output.replace(/^-,/, '-');
1617
+ if (output.indexOf('-' + thousand_seperator) == 0) {
1618
+ output = '-' + output.substr(('-' + thousand_seperator).length);
1619
+ }
1620
+
1621
+ // Reappend the decimal
1622
+ if (decimal.length) {
1623
+ output = output + decimal_seperator + decimal;
1624
+ decimal = '';
1625
+ RegExp.$1 = '';
1626
+ }
1627
+
1628
+ // Minor bugette
1629
+ if (output.charAt(0) == '-') {
1630
+ output = output.replace(/-/, '');
1631
+ prepend = '-' + prepend;
1632
+ }
1633
+
1634
+ return prepend + output + append;
1635
+ };
1636
+
1637
+
1638
+
1639
+
1640
+
1641
+
1642
+
1643
+
1644
+ //
1645
+ // A function that adds text to the chart
1646
+ //
1647
+ RG.SVG.text = function (opt)
1648
+ {
1649
+ var obj = opt.object,
1650
+ parent = opt.parent,
1651
+ size = opt.size,
1652
+ bold = opt.bold,
1653
+ font = opt.font,
1654
+ italic = opt.italic,
1655
+ halign = opt.halign,
1656
+ valign = opt.valign,
1657
+ str = opt.text,
1658
+ x = opt.x,
1659
+ y = opt.y,
1660
+ color = opt.color ? opt.color : 'black',
1661
+ background = opt.background || null,
1662
+ padding = opt.padding || 0;
1663
+
1664
+
1665
+
1666
+
1667
+
1668
+ // Horizontal alignment
1669
+ if (halign === 'right') {
1670
+ halign = 'end';
1671
+ } else if (halign === 'center' || halign === 'middle') {
1672
+ halign = 'middle';
1673
+ } else {
1674
+ halign = 'start';
1675
+ }
1676
+
1677
+ // Vertical alignment
1678
+ if (valign === 'top') {
1679
+ valign = 'hanging';
1680
+ } else if (valign === 'center' || valign === 'middle') {
1681
+ valign = 'central';
1682
+ valign = 'middle';
1683
+ } else {
1684
+ valign = 'bottom';
1685
+ }
1686
+
1687
+
1688
+ var text = RG.SVG.create({
1689
+ svg: obj.svg,
1690
+ parent: opt.parent || null,
1691
+ type: 'text',
1692
+ attr: {
1693
+ fill: color,
1694
+ x: x,
1695
+ y: y,
1696
+ 'font-size': typeof size === 'number' ? size + 'pt' : size,
1697
+ 'font-weight': bold ? 900 : 100,
1698
+ 'font-family': font ? font : 'sans-serif',
1699
+ 'font-style': italic ? 'italic' : 'normal',
1700
+ 'text-anchor': halign,
1701
+ 'dominant-baseline': valign
1702
+ }
1703
+ });
1704
+
1705
+ var textNode = document.createTextNode(str);
1706
+ text.appendChild(textNode);
1707
+
1708
+
1709
+
1710
+ //
1711
+ // Add a background color if specified
1712
+ //
1713
+ if (typeof background === 'string') {
1714
+
1715
+ var bbox = text.getBBox(),
1716
+ rect = RG.SVG.create({
1717
+ svg: obj.svg,
1718
+ type: 'rect',
1719
+ attr: {
1720
+ x: bbox.x - padding,
1721
+ y: bbox.y - padding,
1722
+ width: bbox.width + (padding * 2),
1723
+ height: bbox.height + (padding * 2),
1724
+ fill: background
1725
+ }
1726
+ });
1727
+ obj.svg.insertBefore(rect, text);
1728
+ }
1729
+
1730
+
1731
+
1732
+ if (RG.SVG.ISIE && (valign === 'hanging') ) {
1733
+ text.setAttribute('y', y + (text.scrollHeight / 2));
1734
+
1735
+ } else if (RG.SVG.ISIE && valign === 'middle') {
1736
+ text.setAttribute('y', y + (text.scrollHeight / 3));
1737
+ }
1738
+
1739
+
1740
+
1741
+
1742
+ if (RG.SVG.ISFF) {
1743
+ Y = y + (text.scrollHeight / 3);
1744
+ }
1745
+
1746
+ return text;
1747
+ };
1748
+
1749
+
1750
+
1751
+
1752
+
1753
+
1754
+
1755
+
1756
+ //
1757
+ // Creates a UID that is applied to the object
1758
+ //
1759
+ RG.SVG.createUID = function ()
1760
+ {
1761
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
1762
+ {
1763
+ var r = ma.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
1764
+ return v.toString(16);
1765
+ });
1766
+ };
1767
+
1768
+
1769
+
1770
+
1771
+
1772
+
1773
+
1774
+
1775
+ //
1776
+ // Determines if the SVG DIV container is fixed
1777
+ //
1778
+ RG.SVG.isFixed = function (svg)
1779
+ {
1780
+ var obj = svg.parentNode,
1781
+ i = 0;
1782
+
1783
+ while (obj && obj.tagName.toLowerCase() != 'body' && i < 99) {
1784
+
1785
+ if (obj.style.position === 'fixed') {
1786
+ return obj;
1787
+ }
1788
+
1789
+ obj = obj.offsetParent;
1790
+ }
1791
+
1792
+ return false;
1793
+ };
1794
+
1795
+
1796
+
1797
+
1798
+
1799
+
1800
+
1801
+
1802
+ /**
1803
+ * Sets an object in the RGraph registry
1804
+ *
1805
+ * @param string name The name of the value to set
1806
+ */
1807
+ RG.SVG.REG.set = function (name, value)
1808
+ {
1809
+ RG.SVG.REG.store[name] = value;
1810
+
1811
+ return value;
1812
+ };
1813
+
1814
+
1815
+
1816
+
1817
+
1818
+
1819
+
1820
+
1821
+ /**
1822
+ * Gets an object from the RGraph registry
1823
+ *
1824
+ * @param string name The name of the value to fetch
1825
+ */
1826
+ RG.SVG.REG.get = function (name)
1827
+ {
1828
+ return RG.SVG.REG.store[name];
1829
+ };
1830
+
1831
+
1832
+
1833
+
1834
+
1835
+
1836
+
1837
+
1838
+ /**
1839
+ * Removes white-space from the start aqnd end of a string
1840
+ *
1841
+ * @param string str The string to trim
1842
+ */
1843
+ RG.SVG.trim = function (str)
1844
+ {
1845
+ return RG.SVG.ltrim(RG.SVG.rtrim(str));
1846
+ };
1847
+
1848
+
1849
+
1850
+
1851
+
1852
+
1853
+
1854
+
1855
+ /**
1856
+ * Trims the white-space from the start of a string
1857
+ *
1858
+ * @param string str The string to trim
1859
+ */
1860
+ RG.SVG.ltrim = function (str)
1861
+ {
1862
+ return str.replace(/^(\s|\0)+/, '');
1863
+ };
1864
+
1865
+
1866
+
1867
+
1868
+
1869
+
1870
+
1871
+
1872
+ /**
1873
+ * Trims the white-space off of the end of a string
1874
+ *
1875
+ * @param string str The string to trim
1876
+ */
1877
+ RG.SVG.rtrim = function (str)
1878
+ {
1879
+ return str.replace(/(\s|\0)+$/, '');
1880
+ };
1881
+
1882
+
1883
+
1884
+
1885
+
1886
+
1887
+
1888
+
1889
+ //
1890
+ // Hides the currently shown tooltip
1891
+ //
1892
+ RG.SVG.hideTooltip = function ()
1893
+ {
1894
+ var tooltip = RG.SVG.REG.get('tooltip');
1895
+ //uid = arguments[0] && arguments[0].uid ? arguments[0].uid : null;
1896
+
1897
+ if (tooltip && tooltip.parentNode /*&& (!uid || uid == tooltip.__canvas__.uid)*/) {
1898
+ tooltip.parentNode.removeChild(tooltip);
1899
+ tooltip.style.display = 'none';
1900
+ tooltip.style.visibility = 'hidden';
1901
+ RG.SVG.REG.set('tooltip', null);
1902
+ }
1903
+
1904
+ if (tooltip && tooltip.__object__) {
1905
+ RG.SVG.removeHighlight(tooltip.__object__);
1906
+ }
1907
+ };
1908
+
1909
+
1910
+
1911
+
1912
+
1913
+
1914
+
1915
+
1916
+ //
1917
+ // Creates a shadow
1918
+ //
1919
+ RG.SVG.setShadow = function (options)
1920
+ {
1921
+ var obj = options.object,
1922
+ offsetx = options.offsetx || 0,
1923
+ offsety = options.offsety || 0,
1924
+ blur = options.blur || 0,
1925
+ opacity = options.opacity || 0,
1926
+ id = options.id;
1927
+
1928
+ var filter = RG.SVG.create({
1929
+ svg: obj.svg,
1930
+ parent: obj.defs,
1931
+ type: 'filter',
1932
+ attr: {
1933
+ id: id,
1934
+ width: "130%",
1935
+ height: "130%"
1936
+ }
1937
+ });
1938
+
1939
+ RG.SVG.create({
1940
+ svg: obj.svg,
1941
+ parent: filter,
1942
+ type: 'feOffset',
1943
+ attr: {
1944
+ result: 'offOut',
1945
+ 'in': 'SourceGraphic',
1946
+ dx: offsetx,
1947
+ dy: offsety
1948
+ }
1949
+ });
1950
+
1951
+ RG.SVG.create({
1952
+ svg: obj.svg,
1953
+ parent: filter,
1954
+ type: 'feColorMatrix',
1955
+ attr: {
1956
+ result: 'matrixOut',
1957
+ 'in': 'offOut',
1958
+ type: 'matrix',
1959
+ values: '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {1} 0'.format(
1960
+ opacity
1961
+ )
1962
+ }
1963
+ });
1964
+
1965
+ RG.SVG.create({
1966
+ svg: obj.svg,
1967
+ parent: filter,
1968
+ type: 'feGaussianBlur',
1969
+ attr: {
1970
+ result: 'blurOut',
1971
+ 'in': 'matrixOut',
1972
+ stdDeviation: blur
1973
+ }
1974
+ });
1975
+
1976
+ RG.SVG.create({
1977
+ svg: obj.svg,
1978
+ parent: filter,
1979
+ type: 'feBlend',
1980
+ attr: {
1981
+ 'in': 'SourceGraphic',
1982
+ 'in2': 'blurOut',
1983
+ mode: 'normal'
1984
+ }
1985
+ });
1986
+ };
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+
1993
+
1994
+
1995
+ /**
1996
+ * Takes a sequential index abd returns the group/index variation of it. Eg if you have a
1997
+ * sequential index from a grouped bar chart this function can be used to convert that into
1998
+ * an appropriate group/index combination
1999
+ *
2000
+ * @param nindex number The sequential index
2001
+ * @param data array The original data (which is grouped)
2002
+ * @return The group/index information
2003
+ */
2004
+ RG.SVG.sequentialIndexToGrouped = function (index, data)
2005
+ {
2006
+ var group = 0,
2007
+ grouped_index = 0;
2008
+
2009
+ while (--index >= 0) {
2010
+
2011
+ if (RG.SVG.isNull(data[group])) {
2012
+ group++;
2013
+ grouped_index = 0;
2014
+ continue;
2015
+ }
2016
+
2017
+ // Allow for numbers as well as arrays in the dataset
2018
+ if (typeof data[group] == 'number') {
2019
+ group++
2020
+ grouped_index = 0;
2021
+ continue;
2022
+ }
2023
+
2024
+
2025
+ grouped_index++;
2026
+
2027
+ if (grouped_index >= data[group].length) {
2028
+ group++;
2029
+ grouped_index = 0;
2030
+ }
2031
+ }
2032
+
2033
+ return [group, grouped_index];
2034
+ };
2035
+
2036
+
2037
+
2038
+
2039
+
2040
+
2041
+
2042
+
2043
+ //
2044
+ // This function converts coordinates into the type understood by
2045
+ // SVG for drawing arcs
2046
+ //
2047
+ RG.SVG.TRIG.toCartesian = function (options)
2048
+ {
2049
+ return {
2050
+ x: options.cx + (options.r * ma.cos(options.angle)),
2051
+ y: options.cy + (options.r * ma.sin(options.angle))
2052
+ };
2053
+ };
2054
+
2055
+
2056
+
2057
+
2058
+
2059
+
2060
+
2061
+
2062
+ //
2063
+ // Gets a path that is usable by the SVG A path command
2064
+ //
2065
+ // @patam object options The options/arg to the function
2066
+ //
2067
+ RG.SVG.TRIG.getArcPath = function (options)
2068
+ {
2069
+ //
2070
+ // Make circles start at the top instead of the right hand side
2071
+ //
2072
+ options.start -= 1.57;
2073
+ options.end -= 1.57;
2074
+
2075
+ var start = RG.SVG.TRIG.toCartesian({
2076
+ cx: options.cx,
2077
+ cy: options.cy,
2078
+ r: options.r,
2079
+ angle: options.start}
2080
+ );
2081
+
2082
+ var end = RG.SVG.TRIG.toCartesian({
2083
+ cx: options.cx,
2084
+ cy: options.cy,
2085
+ r: options.r,
2086
+ angle: options.end
2087
+ });
2088
+
2089
+ var diff = options.end - options.start;
2090
+
2091
+ // Initial values
2092
+ var largeArc = '0';
2093
+ var sweep = '0';
2094
+
2095
+ if (options.anticlockwise && diff > 3.14) {
2096
+ largeArc = '0';
2097
+ sweep = '0';
2098
+ } else if (options.anticlockwise && diff <= 3.14) {
2099
+ largeArc = '1';
2100
+ sweep = '0';
2101
+ } else if (!options.anticlockwise && diff > 3.14) {
2102
+ largeArc = '1';
2103
+ sweep = '1';
2104
+ } else if (!options.anticlockwise && diff <= 3.14) {
2105
+ largeArc = '0';
2106
+ sweep = '1';
2107
+ }
2108
+
2109
+ if (options.start > options.end && options.anticlockwise && diff <= 3.14) {
2110
+ largeArc = '0';
2111
+ sweep = '0';
2112
+ }
2113
+
2114
+ if (options.start > options.end && options.anticlockwise && diff > 3.14) {
2115
+ largeArc = '1';
2116
+ sweep = '1';
2117
+ }
2118
+
2119
+
2120
+ if (typeof options.moveto === 'boolean' && options.moveto === false) {
2121
+ var d = [
2122
+ "A", options.r, options.r, 0, largeArc, sweep, end.x, end.y
2123
+ ];
2124
+ } else {
2125
+ var d = [
2126
+ "M", start.x, start.y,
2127
+ "A", options.r, options.r, 0, largeArc, sweep, end.x, end.y
2128
+ ];
2129
+ }
2130
+
2131
+ if (options.array === true) {
2132
+ return d;
2133
+ } else {
2134
+ return d.join(" ");
2135
+ }
2136
+ };
2137
+
2138
+
2139
+
2140
+
2141
+
2142
+
2143
+
2144
+
2145
+ /**
2146
+ * This function gets the end point (X/Y coordinates) of a given radius.
2147
+ * You pass it the center X/Y and the radius and this function will return
2148
+ * the endpoint X/Y coordinates.
2149
+ *
2150
+ * @param number cx The center X coord
2151
+ * @param number cy The center Y coord
2152
+ * @param number r The lrngth of the radius
2153
+ */
2154
+ RG.SVG.TRIG.getRadiusEndPoint = function (opt)
2155
+ {
2156
+ // Allow for two arguments style
2157
+ if (arguments.length === 1) {
2158
+
2159
+ var angle = opt.angle,
2160
+ r = opt.r;
2161
+
2162
+ } else if (arguments.length === 4) {
2163
+
2164
+ var angle = arguments[0],
2165
+ r = arguments[1];
2166
+ }
2167
+
2168
+ var x = ma.cos(angle) * r,
2169
+ y = ma.sin(angle) * r;
2170
+
2171
+ return [x, y];
2172
+ };
2173
+
2174
+
2175
+
2176
+
2177
+
2178
+
2179
+
2180
+
2181
+ /**
2182
+ * This function draws the title. This function also draws the subtitle.
2183
+ */
2184
+ RG.SVG.drawTitle = function (obj)
2185
+ {
2186
+ var prop = obj.properties;
2187
+
2188
+ //
2189
+ // The Pie chart title should default to being above the centerx
2190
+ //
2191
+ if (obj.type === 'pie') {
2192
+ if (RG.SVG.isNull(prop.titleX)) {
2193
+ prop.titleX = obj.centerx;
2194
+ prop.titleSubtitleX = obj.centerx;
2195
+ }
2196
+
2197
+ if (RG.SVG.isNull(prop.titleY)) {
2198
+ prop.titleY = obj.centery - obj.radius - 10;
2199
+ }
2200
+ }
2201
+
2202
+
2203
+
2204
+
2205
+
2206
+ prop.titleY = typeof prop.titleY === 'number' ? prop.titleY : prop.gutterTop - 10;
2207
+
2208
+ // If a subtitle is specified move the title up a bit in
2209
+ // order to accommodate it
2210
+ if (prop.titleSubtitle && typeof prop.titleSubtitleY !== 'number') {
2211
+ prop.titleY = prop.titleY - (prop.titleSubtitleSize * 1.5);
2212
+ }
2213
+
2214
+ // Work out the subtitle size
2215
+ prop.titleSubTitleSize = prop.titleSubTitleSize || prop.textSize;
2216
+
2217
+ // Work out the subtitle Y position
2218
+ prop.titleSubtitleY = prop.titleSubtitleY || prop.titleY + 8;
2219
+
2220
+
2221
+
2222
+
2223
+
2224
+
2225
+ // Draw the title
2226
+ if (prop.title) {
2227
+
2228
+ RG.SVG.text({
2229
+ object: obj,
2230
+ svg: obj.svg,
2231
+ text: prop.title.toString(),
2232
+ size: prop.titleSize || (prop.textSize + 4) || 16,
2233
+
2234
+ x: typeof prop.titleX === 'number' ? prop.titleX : prop.gutterLeft + obj.graphWidth / 2,
2235
+ y: prop.titleY,
2236
+
2237
+ halign: prop.titleHalign || 'center',
2238
+ valign: prop.titleValign || 'bottom',
2239
+ color: prop.titleColor || prop.textColor || 'black',
2240
+ bold: prop.titleBold || false,
2241
+ italic: prop.titleItalic || false,
2242
+ font: prop.titleFont || prop.textFont || 'Arial'
2243
+ });
2244
+ }
2245
+
2246
+
2247
+
2248
+ // Draw the subtitle
2249
+ if (prop.titleSubtitle) {
2250
+ RG.SVG.text({
2251
+ object: obj,
2252
+ svg: obj.svg,
2253
+ text: prop.titleSubtitle,
2254
+ size: prop.titleSubtitleSize,
2255
+ x: typeof prop.titleSubtitleX === 'number' ? prop.titleSubtitleX : prop.gutterLeft + obj.graphWidth / 2,
2256
+ y: prop.titleSubtitleY,
2257
+ halign: prop.titleSubtitleHalign || 'center',
2258
+ valign: prop.titleSubtitleValign || 'bottom',
2259
+ color: prop.titleSubtitleColor || prop.textColor || '#aaa',
2260
+ bold: prop.titleSubtitleBold || false,
2261
+ italic: prop.titleSubtitleItalic || false,
2262
+ font: prop.titleSubtitleFont || prop.textFont || 'Arial'
2263
+ });
2264
+ }
2265
+ };
2266
+
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+
2273
+
2274
+ /**
2275
+ * Removes white-space from the start and end of a string
2276
+ *
2277
+ * @param string str The string to trim
2278
+ */
2279
+ RG.SVG.trim = function (str)
2280
+ {
2281
+ return RG.SVG.ltrim(RG.SVG.rtrim(str));
2282
+ };
2283
+
2284
+
2285
+
2286
+
2287
+
2288
+
2289
+
2290
+
2291
+ /**
2292
+ * Trims the white-space from the start of a string
2293
+ *
2294
+ * @param string str The string to trim
2295
+ */
2296
+ RG.SVG.ltrim = function (str)
2297
+ {
2298
+ return String(str).replace(/^(\s|\0)+/, '');
2299
+ };
2300
+
2301
+
2302
+
2303
+
2304
+
2305
+
2306
+
2307
+
2308
+ /**
2309
+ * Trims the white-space off of the end of a string
2310
+ *
2311
+ * @param string str The string to trim
2312
+ */
2313
+ RG.SVG.rtrim = function (str)
2314
+ {
2315
+ return String(str).replace(/(\s|\0)+$/, '');
2316
+ };
2317
+
2318
+
2319
+
2320
+
2321
+
2322
+
2323
+
2324
+
2325
+ /**
2326
+ * This parses a single color value
2327
+ */
2328
+ RG.SVG.parseColorLinear = function (opt)
2329
+ {
2330
+ var obj = opt.object,
2331
+ color = opt.color;
2332
+
2333
+ if (!color || typeof color !== 'string') {
2334
+ return color;
2335
+ }
2336
+
2337
+ if (color.match(/^gradient\((.*)\)$/i)) {
2338
+
2339
+ var parts = RegExp.$1.split(':'),
2340
+ diff = 1 / (parts.length - 1);
2341
+
2342
+ if (opt && opt.direction && opt.direction === 'horizontal') {
2343
+ var grad = RG.SVG.create({
2344
+ type: 'linearGradient',
2345
+ parent: obj.defs,
2346
+ attr: {
2347
+ id: 'RGraph-linear-gradient' + obj.gradientCounter,
2348
+ x1: opt.start || 0,
2349
+ x2: opt.end || '100%',
2350
+ y1: 0,
2351
+ y2: 0,
2352
+ gradientUnits: "userSpaceOnUse"
2353
+ }
2354
+ });
2355
+
2356
+ } else {
2357
+
2358
+ var grad = RG.SVG.create({
2359
+ type: 'linearGradient',
2360
+ parent: obj.defs,
2361
+ attr: {
2362
+ id: 'RGraph-linear-gradient' + obj.gradientCounter,
2363
+ x1: 0,
2364
+ x2: 0,
2365
+ y1: opt.start || 0,
2366
+ y2: opt.end || '100%',
2367
+ gradientUnits: "userSpaceOnUse"
2368
+ }
2369
+ });
2370
+ }
2371
+
2372
+ // Add the first color stop
2373
+ var stop = RG.SVG.create({
2374
+ type: 'stop',
2375
+ parent: grad,
2376
+ attr: {
2377
+ offset: '0%',
2378
+ 'stop-color': RG.SVG.trim(parts[0])
2379
+ }
2380
+ });
2381
+
2382
+ // Add the rest of the color stops
2383
+ for (var j=1,len=parts.length; j<len; ++j) {
2384
+
2385
+ RG.SVG.create({
2386
+ type: 'stop',
2387
+ parent: grad,
2388
+ attr: {
2389
+ offset: (j * diff * 100) + '%',
2390
+ 'stop-color': RG.SVG.trim(parts[j])
2391
+ }
2392
+ });
2393
+ }
2394
+ }
2395
+
2396
+ color = grad ? 'url(#RGraph-linear-gradient' + (obj.gradientCounter++) + ')' : color;
2397
+
2398
+ return color;
2399
+ };
2400
+
2401
+
2402
+
2403
+
2404
+
2405
+
2406
+
2407
+
2408
+ /**
2409
+ * This parses a single color value
2410
+ */
2411
+ RG.SVG.parseColorRadial = function (opt)
2412
+ {
2413
+ var obj = opt.object,
2414
+ color = opt.color;
2415
+
2416
+ if (!color || typeof color !== 'string') {
2417
+ return color;
2418
+ }
2419
+
2420
+ if (color.match(/^gradient\((.*)\)$/i)) {
2421
+
2422
+ var parts = RegExp.$1.split(':'),
2423
+ diff = 1 / (parts.length - 1);
2424
+
2425
+
2426
+ var grad = RG.SVG.create({
2427
+ type: 'radialGradient',
2428
+ parent: obj.defs,
2429
+ attr: {
2430
+ id: 'RGraph-radial-gradient' + obj.gradientCounter,
2431
+ gradientUnits: 'userSpaceOnUse',
2432
+ cx: obj.centerx,
2433
+ cy: obj.centery,
2434
+ fx: obj.centerx,
2435
+ fy: obj.centery,
2436
+ r: obj.radius
2437
+ }
2438
+ });
2439
+
2440
+ // Add the first color stop
2441
+ var stop = RG.SVG.create({
2442
+ type: 'stop',
2443
+ parent: grad,
2444
+ attr: {
2445
+ offset: '0%',
2446
+ 'stop-color': RG.SVG.trim(parts[0])
2447
+ }
2448
+ });
2449
+
2450
+ // Add the rest of the color stops
2451
+ for (var j=1,len=parts.length; j<len; ++j) {
2452
+
2453
+ RG.SVG.create({
2454
+ type: 'stop',
2455
+ parent: grad,
2456
+ attr: {
2457
+ offset: (j * diff * 100) + '%',
2458
+ 'stop-color': RG.SVG.trim(parts[j])
2459
+ }
2460
+ });
2461
+ }
2462
+ }
2463
+
2464
+ color = grad ? 'url(#RGraph-radial-gradient' + (obj.gradientCounter++) + ')' : color;
2465
+
2466
+ return color;
2467
+ };
2468
+
2469
+
2470
+
2471
+
2472
+
2473
+
2474
+
2475
+
2476
+ /**
2477
+ * Reset all of the color values to their original values
2478
+ *
2479
+ * @param object
2480
+ */
2481
+ RG.SVG.resetColorsToOriginalValues = function (opt)
2482
+ {
2483
+ var obj = opt.object;
2484
+
2485
+ if (obj.originalColors) {
2486
+ // Reset the colors to their original values
2487
+ for (var j in obj.originalColors) {
2488
+ if (typeof j === 'string') {
2489
+ obj.properties[j] = RG.SVG.arrayClone(obj.originalColors[j]);
2490
+ }
2491
+ }
2492
+ }
2493
+
2494
+ /**
2495
+ * If the function is present on the object to reset specific
2496
+ * colors - use that
2497
+ */
2498
+ if (typeof obj.resetColorsToOriginalValues === 'function') {
2499
+ obj.resetColorsToOriginalValues();
2500
+ }
2501
+
2502
+ // Hmmm... Should this be necessary?
2503
+ obj.originalColors = {};
2504
+
2505
+
2506
+
2507
+ // Reset the colorsParsed flag so that they're parsed for gradients again
2508
+ obj.colorsParsed = false;
2509
+
2510
+ // Reset the gradient counter
2511
+ obj.gradientCounter = 1;
2512
+ };
2513
+
2514
+
2515
+
2516
+
2517
+
2518
+
2519
+
2520
+
2521
+ //
2522
+ // Clear the SVG tag by deleting all of its
2523
+ // child nodes
2524
+ //
2525
+ // @param object svg The SVG tag (same as what is returned
2526
+ // by document.getElementById() )
2527
+ //
2528
+ RG.SVG.clear = function (svg)
2529
+ {
2530
+ while (svg.lastChild) {
2531
+ svg.removeChild(svg.lastChild);
2532
+ }
2533
+ };
2534
+
2535
+
2536
+
2537
+
2538
+
2539
+
2540
+
2541
+
2542
+ /**
2543
+ * Adds an event listener
2544
+ *
2545
+ * @param object obj The graph object
2546
+ * @param string event The name of the event, eg ontooltip
2547
+ * @param object func The callback function
2548
+ */
2549
+ RG.SVG.addCustomEventListener = function (obj, name, func)
2550
+ {
2551
+ // Initialise the events array if necessary
2552
+ if (typeof RG.SVG.events[obj.uid] === 'undefined') {
2553
+ RG.SVG.events[obj.uid] = [];
2554
+ }
2555
+
2556
+ // Prepend "on" if necessary
2557
+ if (name.substr(0, 2) !== 'on') {
2558
+ name = 'on' + name;
2559
+ }
2560
+
2561
+ RG.SVG.events[obj.uid].push({
2562
+ object: obj,
2563
+ event: name,
2564
+ func: func
2565
+ });
2566
+
2567
+ return RG.SVG.events[obj.uid].length - 1;
2568
+ };
2569
+
2570
+
2571
+
2572
+
2573
+
2574
+
2575
+
2576
+
2577
+ /**
2578
+ * Used to fire one of the RGraph custom events
2579
+ *
2580
+ * @param object obj The graph object that fires the event
2581
+ * @param string event The name of the event to fire
2582
+ */
2583
+ RG.SVG.fireCustomEvent = function (obj, name)
2584
+ {
2585
+ if (obj && obj.isRGraph) {
2586
+
2587
+ var uid = obj.uid;
2588
+
2589
+ if ( typeof uid === 'string'
2590
+ && typeof RG.SVG.events === 'object'
2591
+ && typeof RG.SVG.events[uid] === 'object'
2592
+ && RG.SVG.events[uid].length > 0) {
2593
+
2594
+ for(var j=0,len=RG.SVG.events[uid].length; j<len; ++j) {
2595
+ if (RG.SVG.events[uid][j] && RG.SVG.events[uid][j].event === name) {
2596
+ RG.SVG.events[uid][j].func(obj);
2597
+ }
2598
+ }
2599
+ }
2600
+ }
2601
+ };
2602
+
2603
+
2604
+
2605
+
2606
+
2607
+
2608
+
2609
+
2610
+ /**
2611
+ * Clears all the custom event listeners that have been registered
2612
+ *
2613
+ * @param string optional Limits the clearing to this object UID
2614
+ */
2615
+ RG.SVG.removeAllCustomEventListeners = function ()
2616
+ {
2617
+ var uid = arguments[0];
2618
+
2619
+ if (uid && RG.SVG.events[uid]) {
2620
+ RG.SVG.events[uid] = {};
2621
+ } else {
2622
+ RG.SVG.events = [];
2623
+ }
2624
+ };
2625
+
2626
+
2627
+
2628
+
2629
+
2630
+
2631
+
2632
+
2633
+ /**
2634
+ * Clears a particular custom event listener
2635
+ *
2636
+ * @param object obj The graph object
2637
+ * @param number i This is the index that is return by .addCustomEventListener()
2638
+ */
2639
+ RG.SVG.removeCustomEventListener = function (obj, i)
2640
+ {
2641
+ if ( typeof RG.SVG.events === 'object'
2642
+ && typeof RG.SVG.events[obj.uid] === 'object'
2643
+ && typeof RG.SVG.events[obj.uid][i] === 'object') {
2644
+
2645
+ RG.SVG.events[obj.uid][i] = null;
2646
+ }
2647
+ };
2648
+
2649
+
2650
+
2651
+
2652
+
2653
+
2654
+
2655
+
2656
+ //
2657
+ // Removes the highlight from the chart added by tooltips (possibly others too)
2658
+ //
2659
+ RG.SVG.removeHighlight = function (obj)
2660
+ {
2661
+ var highlight = RG.SVG.REG.get('highlight');
2662
+
2663
+ if (highlight && RG.SVG.isArray(highlight) && highlight.length) {
2664
+ for (var i=0,len=highlight.length; i<len; ++i) {
2665
+ if (highlight[i].parentNode) {
2666
+ obj.svg.removeChild(highlight[i]);
2667
+ }
2668
+ }
2669
+ } else if (highlight && highlight.parentNode) {
2670
+ highlight.parentNode.removeChild(highlight);
2671
+ }
2672
+ };
2673
+
2674
+
2675
+
2676
+
2677
+
2678
+
2679
+
2680
+
2681
+ //
2682
+ // Removes the highlight from the chart added by tooltips (possibly others too)
2683
+ //
2684
+ RG.SVG.redraw = function ()
2685
+ {
2686
+ if (arguments.length === 1) {
2687
+
2688
+ var svg = arguments[0];
2689
+
2690
+ RG.SVG.clear(svg);
2691
+
2692
+ var objects = RG.SVG.OR.get('id:' + svg.parentNode.id);
2693
+
2694
+ for (var i=0,len=objects.length; i<len; ++i) {
2695
+
2696
+ // Reset the colors to the original values
2697
+ RG.SVG.resetColorsToOriginalValues({object: objects[i]});
2698
+
2699
+
2700
+ objects[i].draw();
2701
+ }
2702
+ } else {
2703
+
2704
+ var tags = RG.SVG.OR.tags();
2705
+
2706
+ for (var i in tags) {
2707
+ RG.SVG.redraw(tags[i]);
2708
+ }
2709
+ }
2710
+ };
2711
+
2712
+
2713
+
2714
+
2715
+
2716
+
2717
+
2718
+
2719
+ /**
2720
+ * This is the same as Date.parse - though a little more flexible and accepts
2721
+ * a few more formats.
2722
+ *
2723
+ * @param string str The date string to parse
2724
+ * @return Returns the same thing as Date.parse
2725
+ */
2726
+ RG.SVG.parseDate = function (str)
2727
+ {
2728
+ str = RG.SVG.trim(str);
2729
+
2730
+ // Allow for: now (just the word "now")
2731
+ if (str === 'now') {
2732
+ str = (new Date()).toString();
2733
+ }
2734
+
2735
+
2736
+ // Allow for: 22-11-2013
2737
+ // Allow for: 22/11/2013
2738
+ // Allow for: 22-11-2013 12:09:09
2739
+ // Allow for: 22/11/2013 12:09:09
2740
+ if (str.match(/^(\d\d)(?:-|\/)(\d\d)(?:-|\/)(\d\d\d\d)(.*)$/)) {
2741
+ str = '{1}/{2}/{3}{4}'.format(
2742
+ RegExp.$3,
2743
+ RegExp.$2,
2744
+ RegExp.$1,
2745
+ RegExp.$4
2746
+ );
2747
+ }
2748
+
2749
+ // Allow for: 2013-11-22 12:12:12 or 2013/11/22 12:12:12
2750
+ if (str.match(/^(\d\d\d\d)(-|\/)(\d\d)(-|\/)(\d\d)( |T)(\d\d):(\d\d):(\d\d)$/)) {
2751
+ str = RegExp.$1 + '-' + RegExp.$3 + '-' + RegExp.$5 + 'T' + RegExp.$7 + ':' + RegExp.$8 + ':' + RegExp.$9;
2752
+ }
2753
+
2754
+ // Allow for: 2013-11-22
2755
+ if (str.match(/^\d\d\d\d-\d\d-\d\d$/)) {
2756
+ str = str.replace(/-/g, '/');
2757
+ }
2758
+
2759
+
2760
+ // Allow for: 12:09:44 (time only using todays date)
2761
+ if (str.match(/^\d\d:\d\d:\d\d$/)) {
2762
+
2763
+ var dateObj = new Date();
2764
+ var date = dateObj.getDate();
2765
+ var month = dateObj.getMonth() + 1;
2766
+ var year = dateObj.getFullYear();
2767
+
2768
+ // Pad the date/month with a zero if it's not two characters
2769
+ if (String(month).length === 1) month = '0' + month;
2770
+ if (String(date).length === 1) date = '0' + date;
2771
+
2772
+ str = (year + '/' + month + '/' + date) + ' ' + str;
2773
+ }
2774
+
2775
+ return Date.parse(str);
2776
+ };
2777
+
2778
+
2779
+
2780
+
2781
+
2782
+
2783
+
2784
+
2785
+ // The ObjectRegistry add function
2786
+ RG.SVG.OR.add = function (obj)
2787
+ {
2788
+ RG.SVG.OR.objects.push(obj);
2789
+
2790
+ return obj;
2791
+ };
2792
+
2793
+
2794
+
2795
+
2796
+
2797
+
2798
+
2799
+
2800
+ // The ObjectRegistry function that returns all of the objects. Th argument
2801
+ // can aither be:
2802
+ //
2803
+ // o omitted All of the registered objects are returned
2804
+ // o id:XXX All of the objects on that SVG tag are returned
2805
+ // o type:XXX All the objects of that type are returned
2806
+ //
2807
+ RG.SVG.OR.get = function ()
2808
+ {
2809
+ // Fetch objects that are on a particular SVG tag
2810
+ if (typeof arguments[0] === 'string' && arguments[0].substr(0, 3).toLowerCase() === 'id:') {
2811
+
2812
+ var ret = [];
2813
+
2814
+ for (var i=0; i<RG.SVG.OR.objects.length; ++i) {
2815
+ if (RG.SVG.OR.objects[i].id === arguments[0].substr(3)) {
2816
+ ret.push(RG.SVG.OR.objects[i]);
2817
+ }
2818
+ }
2819
+
2820
+ return ret;
2821
+ }
2822
+
2823
+
2824
+ // Fetch objects that are of a particular type
2825
+ //
2826
+ // TODO Allow multiple types to be specified
2827
+ if (typeof arguments[0] === 'string' && arguments[0].substr(0, 4).toLowerCase() === 'type') {
2828
+
2829
+ var ret = [];
2830
+
2831
+ for (var i=0; i<RG.SVG.OR.objects.length; ++i) {
2832
+ if (RG.SVG.OR.objects[i].type === arguments[0].substr(5)) {
2833
+ ret.push(RG.SVG.OR.objects[i]);
2834
+ }
2835
+ }
2836
+
2837
+ return ret;
2838
+ }
2839
+
2840
+
2841
+ // Fetch an object that has a specific UID
2842
+ if (typeof arguments[0] === 'string' && arguments[0].substr(0, 3).toLowerCase() === 'uid') {
2843
+
2844
+ var ret = [];
2845
+
2846
+ for (var i=0; i<RG.SVG.OR.objects.length; ++i) {
2847
+ if (RG.SVG.OR.objects[i].uid === arguments[0].substr(4)) {
2848
+ ret.push(RG.SVG.OR.objects[i]);
2849
+ }
2850
+ }
2851
+
2852
+ return ret;
2853
+ }
2854
+
2855
+ return RG.SVG.OR.objects;
2856
+ };
2857
+
2858
+
2859
+
2860
+
2861
+
2862
+
2863
+
2864
+
2865
+ // The ObjectRegistry function that returns all of the registeredt SVG tags
2866
+ //
2867
+ RG.SVG.OR.tags = function ()
2868
+ {
2869
+ var tags = [];
2870
+
2871
+ for (var i=0; i<RG.SVG.OR.objects.length; ++i) {
2872
+ if (!tags[RG.SVG.OR.objects[i].svg.parentNode.id]) {
2873
+ tags[RG.SVG.OR.objects[i].svg.parentNode.id] = RG.SVG.OR.objects[i].svg;
2874
+ }
2875
+ }
2876
+
2877
+ return tags;
2878
+ };
2879
+
2880
+
2881
+
2882
+
2883
+
2884
+
2885
+
2886
+
2887
+ //
2888
+ // This function returns a two element array of the SVG x/y position in
2889
+ // relation to the page
2890
+ //
2891
+ // @param object svg
2892
+ //
2893
+ RG.SVG.getSVGXY = function (svg)
2894
+ {
2895
+ var x = 0,
2896
+ y = 0,
2897
+ el = svg.parentNode; // !!!
2898
+
2899
+ do {
2900
+
2901
+ x += el.offsetLeft;
2902
+ y += el.offsetTop;
2903
+
2904
+ // Account for tables in webkit
2905
+ if (el.tagName.toLowerCase() == 'table' && (RG.SVG.ISCHROME || RG.SVG.ISSAFARI)) {
2906
+ x += parseInt(el.border) || 0;
2907
+ y += parseInt(el.border) || 0;
2908
+ }
2909
+
2910
+ el = el.offsetParent;
2911
+
2912
+ } while (el && el.tagName && el.tagName.toLowerCase() != 'body');
2913
+
2914
+
2915
+ var paddingLeft = svg.style.paddingLeft ? parseInt(svg.style.paddingLeft) : 0,
2916
+ paddingTop = svg.style.paddingTop ? parseInt(svg.style.paddingTop) : 0,
2917
+ borderLeft = svg.style.borderLeftWidth ? parseInt(svg.style.borderLeftWidth) : 0,
2918
+ borderTop = svg.style.borderTopWidth ? parseInt(svg.style.borderTopWidth) : 0;
2919
+
2920
+ if (navigator.userAgent.indexOf('Firefox') > 0) {
2921
+ x += parseInt(document.body.style.borderLeftWidth) || 0;
2922
+ y += parseInt(document.body.style.borderTopWidth) || 0;
2923
+ }
2924
+
2925
+ return [x + paddingLeft + borderLeft, y + paddingTop + borderTop];
2926
+ };
2927
+
2928
+
2929
+
2930
+
2931
+ /**
2932
+ * This function determines whther a canvas is fixed (CSS positioning) or not. If not it returns
2933
+ * false. If it is then the element that is fixed is returned (it may be a parent of the canvas).
2934
+ *
2935
+ * @return Either false or the fixed positioned element
2936
+ */
2937
+ RG.isFixed = function (canvas)
2938
+ {
2939
+ var obj = canvas;
2940
+ var i = 0;
2941
+
2942
+ while (obj && obj.tagName.toLowerCase() != 'body' && i < 99) {
2943
+
2944
+ if (obj.style.position == 'fixed') {
2945
+ return obj;
2946
+ }
2947
+
2948
+ obj = obj.offsetParent;
2949
+ }
2950
+
2951
+ return false;
2952
+ };
2953
+
2954
+
2955
+
2956
+
2957
+
2958
+
2959
+
2960
+
2961
+ //
2962
+ // This function is a compatibility wrapper around
2963
+ // the requestAnimationFrame function.
2964
+ //
2965
+ // @param function func The function to give to the
2966
+ // requestAnimationFrame function
2967
+ //
2968
+ RG.SVG.FX.update = function (func)
2969
+ {
2970
+ win.requestAnimationFrame =
2971
+ win.requestAnimationFrame ||
2972
+ win.webkitRequestAnimationFrame ||
2973
+ win.msRequestAnimationFrame ||
2974
+ win.mozRequestAnimationFrame ||
2975
+ (function (func){setTimeout(func, 16.666);});
2976
+
2977
+ win.requestAnimationFrame(func);
2978
+ };
2979
+
2980
+
2981
+
2982
+
2983
+
2984
+
2985
+
2986
+
2987
+ /**
2988
+ * This function returns an easing multiplier for effects so they eas out towards the
2989
+ * end of the effect.
2990
+ *
2991
+ * @param number frames The total number of frames
2992
+ * @param number frame The frame number
2993
+ */
2994
+ RG.SVG.FX.getEasingMultiplier = function (frames, frame)
2995
+ {
2996
+ var multiplier = ma.pow(ma.sin((frame / frames) * RG.SVG.TRIG.HALFPI), 3);
2997
+
2998
+ return multiplier;
2999
+ };
3000
+
3001
+
3002
+
3003
+
3004
+
3005
+
3006
+
3007
+
3008
+ /**
3009
+ * Measures text by creating a DIV in the document and adding the relevant
3010
+ * text to it, then checking the .offsetWidth and .offsetHeight.
3011
+ *
3012
+ * @param object opt An object containing the following:
3013
+ * o text( string) The text to measure
3014
+ * o bold (bool) Whether the text is bold or not
3015
+ * o font (string) The font to use
3016
+ * o size (number) The size of the text (in pts)
3017
+ *
3018
+ * @return array A two element array of the width and height of the text
3019
+ */
3020
+ RG.SVG.measureText = function (opt)
3021
+ {
3022
+ //text, bold, font, size
3023
+ var text = opt.text || '',
3024
+ bold = opt.bold || false,
3025
+ font = opt.font || 'Arial',
3026
+ size = opt.size || 10,
3027
+ str = text + ':' + bold + ':' + font + ':' + size;
3028
+
3029
+ // Add the sizes to the cache as adding DOM elements is costly and causes slow downs
3030
+ if (typeof RG.SVG.measuretext_cache === 'undefined') {
3031
+ RG.SVG.measuretext_cache = [];
3032
+ }
3033
+
3034
+ if (typeof RG.SVG.measuretext_cache == 'object' && RG.SVG.measuretext_cache[str]) {
3035
+ return RG.SVG.measuretext_cache[str];
3036
+ }
3037
+
3038
+ if (!RG.SVG.measuretext_cache['text-div']) {
3039
+ var div = document.createElement('DIV');
3040
+ div.style.position = 'absolute';
3041
+ div.style.top = '-100px';
3042
+ div.style.left = '-100px';
3043
+ document.body.appendChild(div);
3044
+
3045
+ // Now store the newly created DIV
3046
+ RG.SVG.measuretext_cache['text-div'] = div;
3047
+
3048
+ } else if (RG.SVG.measuretext_cache['text-div']) {
3049
+ var div = RG.SVG.measuretext_cache['text-div'];
3050
+ }
3051
+
3052
+ div.innerHTML = text.replace(/\r\n/g, '<br />');
3053
+ div.style.fontFamily = font;
3054
+ div.style.fontWeight = bold ? 'bold' : 'normal';
3055
+ div.style.fontSize = size + 'pt';
3056
+
3057
+ var sizes = [div.offsetWidth, div.offsetHeight];
3058
+
3059
+ //document.body.removeChild(div);
3060
+ RG.SVG.measuretext_cache[str] = sizes;
3061
+
3062
+ return sizes;
3063
+ };
3064
+
3065
+
3066
+
3067
+
3068
+
3069
+
3070
+
3071
+
3072
+ /**
3073
+ * This function converts an array of strings to an array of numbers. Its used by the meter/gauge
3074
+ * style charts so that if you want you can pass in a string. It supports various formats:
3075
+ *
3076
+ * '45.2'
3077
+ * '-45.2'
3078
+ * ['45.2']
3079
+ * ['-45.2']
3080
+ * '45.2,45.2,45.2' // A CSV style string
3081
+ *
3082
+ * @param number frames The string or array to parse
3083
+ */
3084
+ RG.SVG.stringsToNumbers = function (str)
3085
+ {
3086
+ // An optional seperator to use intead of a comma
3087
+ var sep = arguments[1] || ',';
3088
+
3089
+
3090
+ // If it's already a number just return it
3091
+ if (typeof str === 'number') {
3092
+ return str;
3093
+ }
3094
+
3095
+
3096
+
3097
+
3098
+
3099
+ if (typeof str === 'string') {
3100
+ if (str.indexOf(sep) != -1) {
3101
+ str = str.split(sep);
3102
+ } else {
3103
+ str = parseFloat(str);
3104
+ }
3105
+ }
3106
+
3107
+
3108
+
3109
+
3110
+
3111
+ if (typeof str === 'object') {
3112
+ for (var i=0,len=str.length; i<len; i+=1) {
3113
+ str[i] = parseFloat(str[i]);
3114
+ }
3115
+ }
3116
+
3117
+ return str;
3118
+ };
3119
+
3120
+
3121
+
3122
+
3123
+
3124
+
3125
+
3126
+
3127
+ // This function allows for numbers that are given as a +/- adjustment
3128
+ RG.SVG.getAdjustedNumber = function (opt)
3129
+ {
3130
+ var value = opt.value,
3131
+ prop = opt.prop;
3132
+
3133
+ if (typeof prop === 'string' && match(/^(\+|-)([0-9.]+)/)) {
3134
+ if (RegExp.$1 === '+') {
3135
+ value += parseFloat(RegExp.$2);
3136
+ } else if (RegExp.$1 === '-') {
3137
+ value -= parseFloat(RegExp.$2);
3138
+ }
3139
+ }
3140
+
3141
+ return value;
3142
+ };
3143
+
3144
+
3145
+
3146
+
3147
+
3148
+
3149
+
3150
+
3151
+ //
3152
+ // Adds the attribution link to the chart in the
3153
+ // (by default) bottom right corner
3154
+ //
3155
+ //@param ibject obj The chart object
3156
+ //
3157
+ RG.SVG.attribution = function (obj)
3158
+ {
3159
+ var prop = obj.properties;
3160
+
3161
+ if (!prop.attribution && typeof prop.attribution !== 'undefined') {
3162
+ return false;
3163
+ }
3164
+
3165
+
3166
+ // Create the A tag
3167
+ var a = RG.SVG.create({
3168
+ svg: obj.svg,
3169
+ type: 'a',
3170
+ attr: {
3171
+ 'xlink:href': prop.attributionHref || 'http://www.rgraph.net'
3172
+ }
3173
+ });
3174
+
3175
+
3176
+
3177
+ // Work out the X/Y coords
3178
+ var x = parseFloat(obj.svg.getAttribute('width')) - 2,
3179
+ y = parseFloat(obj.svg.getAttribute('height')) - 2;
3180
+
3181
+ // Allow the X coord to be a +/- string
3182
+ if (typeof prop.attributionX === 'string') {
3183
+ x += parseFloat(prop.attributionX);
3184
+ } else if (typeof prop.attributionX === 'number') {
3185
+ x = parseFloat(prop.attributionX);
3186
+ }
3187
+
3188
+ // Allow the Y coord to be a +/- string|
3189
+ if (typeof prop.attributionY === 'string') {
3190
+ y += parseFloat(prop.attributionY);
3191
+ } else if (typeof prop.attributionY === 'number') {
3192
+ y = parseFloat(prop.attributionY);
3193
+ }
3194
+
3195
+ // Add a text tag to the attribution A tag
3196
+ var text = RG.SVG.text({
3197
+ object: obj,
3198
+ parent: a,
3199
+ text: typeof prop.attributionText === 'string' ? prop.attributionText : 'RGraph.net',
3200
+ x: x,
3201
+ y: y,
3202
+ halign: prop.attributionHalign || 'right',
3203
+ valign: prop.attributionValign || 'bottom',
3204
+ font: prop.attributionFont || 'sans-serif',
3205
+ size: prop.attributionSize || 7,
3206
+ color: prop.attributionColor || 'gray',
3207
+ italic: prop.attributionItalic,
3208
+ bold: prop.attributionBold
3209
+ });
3210
+ };
3211
+
3212
+
3213
+
3214
+
3215
+
3216
+
3217
+
3218
+
3219
+ /**
3220
+ * Generates a random number between the minimum and maximum
3221
+ *
3222
+ * @param number min The minimum value
3223
+ * @param number max The maximum value
3224
+ * @param number OPTIONAL Number of decimal places
3225
+ */
3226
+ RG.SVG.random = function (min, max)
3227
+ {
3228
+ var dp = arguments[2] ? arguments[2] : 0;
3229
+ var r = ma.random();
3230
+
3231
+ return Number((((max - min) * r) + min).toFixed(dp));
3232
+ };
3233
+
3234
+
3235
+
3236
+
3237
+
3238
+
3239
+
3240
+
3241
+ //
3242
+ // This is here so that if the tooltip library has not
3243
+ // been included, this function will show an alert
3244
+ //informing the user
3245
+ //
3246
+ if (typeof RG.SVG.tooltip !== 'function') {
3247
+ RG.SVG.tooltip = function ()
3248
+ {
3249
+ $a('The tooltip library has not been included!');
3250
+ };
3251
+ }
3252
+
3253
+
3254
+
3255
+
3256
+
3257
+
3258
+
3259
+
3260
+ // End module pattern
3261
+ })(window, document);
3262
+
3263
+
3264
+
3265
+
3266
+ /**
3267
+ * Loosly mimicks the PHP function print_r();
3268
+ */
3269
+ window.$p = function (obj)
3270
+ {
3271
+ var indent = (arguments[2] ? arguments[2] : ' ');
3272
+ var str = '';
3273
+
3274
+ var counter = typeof arguments[3] == 'number' ? arguments[3] : 0;
3275
+
3276
+ if (counter >= 5) {
3277
+ return '';
3278
+ }
3279
+
3280
+ switch (typeof obj) {
3281
+
3282
+ case 'string': str += obj + ' (' + (typeof obj) + ', ' + obj.length + ')'; break;
3283
+ case 'number': str += obj + ' (' + (typeof obj) + ')'; break;
3284
+ case 'boolean': str += obj + ' (' + (typeof obj) + ')'; break;
3285
+ case 'function': str += 'function () {}'; break;
3286
+ case 'undefined': str += 'undefined'; break;
3287
+ case 'null': str += 'null'; break;
3288
+
3289
+ case 'object':
3290
+ // In case of null
3291
+ if (RGraph.SVG.isNull(obj)) {
3292
+ str += indent + 'null\n';
3293
+ } else {
3294
+ str += indent + 'Object {' + '\n'
3295
+ for (j in obj) {
3296
+ str += indent + ' ' + j + ' => ' + window.$p(obj[j], true, indent + ' ', counter + 1) + '\n';
3297
+ }
3298
+ str += indent + '}';
3299
+ }
3300
+ break;
3301
+
3302
+
3303
+ default:
3304
+ str += 'Unknown type: ' + typeof obj + '';
3305
+ break;
3306
+ }
3307
+
3308
+
3309
+ /**
3310
+ * Finished, now either return if we're in a recursed call, or alert()
3311
+ * if we're not.
3312
+ */
3313
+ if (!arguments[1]) {
3314
+ alert(str);
3315
+ }
3316
+
3317
+ return str;
3318
+ };
3319
+
3320
+
3321
+
3322
+ /**
3323
+ * A shorthand for the default alert() function
3324
+ */
3325
+ window.$a = function (v)
3326
+ {
3327
+ alert(v);
3328
+ };
3329
+
3330
+
3331
+
3332
+
3333
+ /**
3334
+ * Short-hand for console.log
3335
+ *
3336
+ * @param mixed v The variable to log to the console
3337
+ */
3338
+ window.$cl = function (v)
3339
+ {
3340
+ return console.log(v);
3341
+ };
3342
+
3343
+
3344
+
3345
+
3346
+ /**
3347
+ * A basic string formatting function. Use it like this:
3348
+ *
3349
+ * var str = '{0} {1} {2}'.format('a', 'b', 'c');
3350
+ *
3351
+ * Outputs: a b c
3352
+ */
3353
+ if (!String.prototype.format) {
3354
+ String.prototype.format = function()
3355
+ {
3356
+ var args = arguments;
3357
+
3358
+ return this.replace(/{(\d+)}/g, function(str, idx)
3359
+ {
3360
+ return typeof args[idx - 1] !== 'undefined' ? args[idx - 1] : str;
3361
+ });
3362
+ };
3363
+ }