flashgrid-ext 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 931b3a73e6120bdc07b888fb9e9f6094f274bdd5
4
- data.tar.gz: 12bd0d94b4c518f57b25de67c7d14e47824b8e31
3
+ metadata.gz: 9f70ded0530ea93c0b17e3a4aefd1b5346fb258e
4
+ data.tar.gz: 20a532b3e9346f73aef852b7a34150e8c5c94d98
5
5
  SHA512:
6
- metadata.gz: 2b6fd1d2a41a1c642a2f4b93e37f483ab3273b824f281f8fae9570edb82b61b47dbfdba471ebe551d9ffa7f842ac022a7561c3bbb30c7728281b03d7a2238d9f
7
- data.tar.gz: 7f02a7858aeed64ffe4fffb8def1757fb8c78fb9e52acab181a97bc5efb2cc304381f9f28eca7d8633d08d372c564f2ebb6ac455429de05c9c2db2276db2d90c
6
+ metadata.gz: b1d1259f4ad339b718406bc37ad2c21f6262cf877de5167244c4c391ac86a64612432953c1fc05c8f8f164bd1f3495b686fd963067d180971970f0635836db0c
7
+ data.tar.gz: a23fb84e41d504691fc36b9276e65483a9b38e9ddd3628526e898b03390a35e900650f9af6ebf0c2addf58c9d9343201e4fdbdc0075cccdc02b779971b4d9b0d
data/README.md CHANGED
@@ -38,6 +38,7 @@ Add the JS files you want to include:
38
38
  //= require animate.js
39
39
  //= require calendar.js
40
40
  //= require carousel.js
41
+ //= require chart.js
41
42
  //= require editor.js
42
43
  //= require hoverdown.js
43
44
  //= require input_mask.js
@@ -1,5 +1,5 @@
1
1
  module Flashgrid
2
2
  module Ext
3
- VERSION = "1.0.9"
3
+ VERSION = "1.0.10"
4
4
  end
5
5
  end
@@ -39,7 +39,7 @@
39
39
 
40
40
  Carousel.prototype.getActiveIndex = function () {
41
41
  this.$active = this.$element.find('.item.active')
42
- this.$items = this.$active.parent().children()
42
+ this.$items = this.$active.parent().children('.item')
43
43
 
44
44
  return this.$items.index(this.$active)
45
45
  }
@@ -0,0 +1,1751 @@
1
+ window.Chart = function(context, options){
2
+ var chart = this;
3
+
4
+ var animationOptions = {
5
+ linear : function (t){
6
+ return t;
7
+ },
8
+ easeInQuad: function (t) {
9
+ return t*t;
10
+ },
11
+ easeOutQuad: function (t) {
12
+ return -1 *t*(t-2);
13
+ },
14
+ easeInOutQuad: function (t) {
15
+ if ((t/=1/2) < 1) return 1/2*t*t;
16
+ return -1/2 * ((--t)*(t-2) - 1);
17
+ },
18
+ easeInCubic: function (t) {
19
+ return t*t*t;
20
+ },
21
+ easeOutCubic: function (t) {
22
+ return 1*((t=t/1-1)*t*t + 1);
23
+ },
24
+ easeInOutCubic: function (t) {
25
+ if ((t/=1/2) < 1) return 1/2*t*t*t;
26
+ return 1/2*((t-=2)*t*t + 2);
27
+ },
28
+ easeInQuart: function (t) {
29
+ return t*t*t*t;
30
+ },
31
+ easeOutQuart: function (t) {
32
+ return -1 * ((t=t/1-1)*t*t*t - 1);
33
+ },
34
+ easeInOutQuart: function (t) {
35
+ if ((t/=1/2) < 1) return 1/2*t*t*t*t;
36
+ return -1/2 * ((t-=2)*t*t*t - 2);
37
+ },
38
+ easeInQuint: function (t) {
39
+ return 1*(t/=1)*t*t*t*t;
40
+ },
41
+ easeOutQuint: function (t) {
42
+ return 1*((t=t/1-1)*t*t*t*t + 1);
43
+ },
44
+ easeInOutQuint: function (t) {
45
+ if ((t/=1/2) < 1) return 1/2*t*t*t*t*t;
46
+ return 1/2*((t-=2)*t*t*t*t + 2);
47
+ },
48
+ easeInSine: function (t) {
49
+ return -1 * Math.cos(t/1 * (Math.PI/2)) + 1;
50
+ },
51
+ easeOutSine: function (t) {
52
+ return 1 * Math.sin(t/1 * (Math.PI/2));
53
+ },
54
+ easeInOutSine: function (t) {
55
+ return -1/2 * (Math.cos(Math.PI*t/1) - 1);
56
+ },
57
+ easeInExpo: function (t) {
58
+ return (t==0) ? 1 : 1 * Math.pow(2, 10 * (t/1 - 1));
59
+ },
60
+ easeOutExpo: function (t) {
61
+ return (t==1) ? 1 : 1 * (-Math.pow(2, -10 * t/1) + 1);
62
+ },
63
+ easeInOutExpo: function (t) {
64
+ if (t==0) return 0;
65
+ if (t==1) return 1;
66
+ if ((t/=1/2) < 1) return 1/2 * Math.pow(2, 10 * (t - 1));
67
+ return 1/2 * (-Math.pow(2, -10 * --t) + 2);
68
+ },
69
+ easeInCirc: function (t) {
70
+ if (t>=1) return t;
71
+ return -1 * (Math.sqrt(1 - (t/=1)*t) - 1);
72
+ },
73
+ easeOutCirc: function (t) {
74
+ return 1 * Math.sqrt(1 - (t=t/1-1)*t);
75
+ },
76
+ easeInOutCirc: function (t) {
77
+ if ((t/=1/2) < 1) return -1/2 * (Math.sqrt(1 - t*t) - 1);
78
+ return 1/2 * (Math.sqrt(1 - (t-=2)*t) + 1);
79
+ },
80
+ easeInElastic: function (t) {
81
+ var s=1.70158;var p=0;var a=1;
82
+ if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
83
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
84
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
85
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
86
+ },
87
+ easeOutElastic: function (t) {
88
+ var s=1.70158;var p=0;var a=1;
89
+ if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3;
90
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
91
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
92
+ return a*Math.pow(2,-10*t) * Math.sin( (t*1-s)*(2*Math.PI)/p ) + 1;
93
+ },
94
+ easeInOutElastic: function (t) {
95
+ var s=1.70158;var p=0;var a=1;
96
+ if (t==0) return 0; if ((t/=1/2)==2) return 1; if (!p) p=1*(.3*1.5);
97
+ if (a < Math.abs(1)) { a=1; var s=p/4; }
98
+ else var s = p/(2*Math.PI) * Math.asin (1/a);
99
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p ));
100
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )*.5 + 1;
101
+ },
102
+ easeInBack: function (t) {
103
+ var s = 1.70158;
104
+ return 1*(t/=1)*t*((s+1)*t - s);
105
+ },
106
+ easeOutBack: function (t) {
107
+ var s = 1.70158;
108
+ return 1*((t=t/1-1)*t*((s+1)*t + s) + 1);
109
+ },
110
+ easeInOutBack: function (t) {
111
+ var s = 1.70158;
112
+ if ((t/=1/2) < 1) return 1/2*(t*t*(((s*=(1.525))+1)*t - s));
113
+ return 1/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2);
114
+ },
115
+ easeInBounce: function (t) {
116
+ return 1 - animationOptions.easeOutBounce (1-t);
117
+ },
118
+ easeOutBounce: function (t) {
119
+ if ((t/=1) < (1/2.75)) {
120
+ return 1*(7.5625*t*t);
121
+ } else if (t < (2/2.75)) {
122
+ return 1*(7.5625*(t-=(1.5/2.75))*t + .75);
123
+ } else if (t < (2.5/2.75)) {
124
+ return 1*(7.5625*(t-=(2.25/2.75))*t + .9375);
125
+ } else {
126
+ return 1*(7.5625*(t-=(2.625/2.75))*t + .984375);
127
+ }
128
+ },
129
+ easeInOutBounce: function (t) {
130
+ if (t < 1/2) return animationOptions.easeInBounce (t*2) * .5;
131
+ return animationOptions.easeOutBounce (t*2-1) * .5 + 1*.5;
132
+ }
133
+ };
134
+
135
+ this.tooltips = [],
136
+ defaults = {
137
+ tooltips: {
138
+ background: 'rgba(71,74,84,1)',
139
+ fontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
140
+ fontStyle : "bold",
141
+ fontColor: 'rgba(255,255,255,1)',
142
+ fontSize: '11px',
143
+ labelTemplate: '<%=label%>: <%=value%>',
144
+ padding: {
145
+ top: 10,
146
+ right: 5,
147
+ bottom: 12,
148
+ left: 5
149
+ },
150
+ offset: {
151
+ left: 15,
152
+ top: 0
153
+ },
154
+ border: {
155
+ radius: 3
156
+ },
157
+ showHighlight: true,
158
+ highlight: {
159
+ stroke: {
160
+ width: 1,
161
+ color: 'rgba(255,255,255,0.25)'
162
+ },
163
+ fill: 'rgba(255,255,255,0.25)'
164
+ }
165
+ }
166
+ },
167
+ options = (options) ? mergeChartConfig(defaults, options) : defaults;
168
+
169
+ function registerTooltip(ctx,areaObj,data,type) {
170
+ chart.tooltips.push(new Tooltip(
171
+ ctx,
172
+ areaObj,
173
+ data,
174
+ type
175
+ ));
176
+ }
177
+
178
+ var Tooltip = function(ctx, areaObj, data, type) {
179
+ this.ctx = ctx;
180
+ this.areaObj = areaObj;
181
+ this.data = data;
182
+ this.savedState = null;
183
+ this.highlightState = null;
184
+ this.x = null;
185
+ this.y = null;
186
+
187
+ this.inRange = function(x,y) {
188
+ if(this.areaObj.type) {
189
+ switch(this.areaObj.type) {
190
+ case 'rect':
191
+ return (x >= this.areaObj.x && x <= this.areaObj.x+this.areaObj.width) &&
192
+ (y >= this.areaObj.y && y <= this.areaObj.y+this.areaObj.height);
193
+ break;
194
+ case 'circle':
195
+ return ((Math.pow(x-this.areaObj.x, 2)+Math.pow(y-this.areaObj.y, 2)) < Math.pow(this.areaObj.r,2));
196
+ break;
197
+ case 'shape':
198
+ var poly = this.areaObj.points;
199
+ for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
200
+ ((poly[i].y <= y && y < poly[j].y) || (poly[j].y <= y && y < poly[i].y))
201
+ && (x < (poly[j].x - poly[i].x) * (y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)
202
+ && (c = !c);
203
+ return c;
204
+ break;
205
+ }
206
+ }
207
+ }
208
+
209
+ this.render = function(x,y) {
210
+ if(this.savedState == null) {
211
+ this.ctx.putImageData(chart.savedState,0,0);
212
+ this.savedState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height);
213
+ }
214
+ this.ctx.putImageData(this.savedState,0,0);
215
+ if(options.tooltips.showHighlight) {
216
+ if(this.highlightState == null) {
217
+ this.ctx.strokeStyle = options.tooltips.highlight.stroke.color;
218
+ this.ctx.lineWidth = options.tooltips.highlight.stroke.width;
219
+ this.ctx.fillStyle = options.tooltips.highlight.fill;
220
+ switch(this.areaObj.type) {
221
+ case 'rect':
222
+ this.ctx.strokeRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height);
223
+ this.ctx.fillStyle = options.tooltips.highlight.fill;
224
+ this.ctx.fillRect(this.areaObj.x, this.areaObj.y, this.areaObj.width, this.areaObj.height);
225
+ break;
226
+ case 'circle':
227
+ this.ctx.beginPath();
228
+ this.ctx.arc(this.areaObj.x, this.areaObj.y, this.areaObj.r, 0, 2*Math.PI, false);
229
+ this.ctx.stroke();
230
+ this.ctx.fill();
231
+ break;
232
+ case 'shape':
233
+ this.ctx.beginPath();
234
+ this.ctx.moveTo(this.areaObj.points[0].x, this.areaObj.points[0].y);
235
+ for(var p in this.areaObj.points) {
236
+ this.ctx.lineTo(this.areaObj.points[p].x, this.areaObj.points[p].y);
237
+ }
238
+ this.ctx.stroke();
239
+ this.ctx.fill();
240
+ break;
241
+ }
242
+ this.highlightState = this.ctx.getImageData(0,0,this.ctx.canvas.width,this.ctx.canvas.height);
243
+ } else {
244
+ this.ctx.putImageData(this.highlightState,0,0);
245
+ }
246
+ }
247
+ //if(this.x != x || this.y != y) {
248
+ var posX = x+options.tooltips.offset.left,
249
+ posY = y+options.tooltips.offset.top,
250
+ tpl = tmpl(options.tooltips.labelTemplate, this.data),
251
+ rectWidth = options.tooltips.padding.left+this.ctx.measureText(tpl).width+options.tooltips.padding.right;
252
+ if(posX + rectWidth > this.ctx.canvas.width) {
253
+ posX -= posX-rectWidth < 0 ? posX : rectWidth;
254
+ }
255
+ if(posY + 24 > this.ctx.canvas.height) {
256
+ posY -= 24;
257
+ }
258
+ this.ctx.fillStyle = options.tooltips.background;
259
+ this.ctx.fillRect(posX, posY, rectWidth, 24);
260
+ if(options.tooltips.border.width > 0) {
261
+ this.ctx.fillStyle = options.tooltips.order.color;
262
+ this.ctx.lineWidth = options.tooltips.border.width;
263
+ this.ctx.strokeRect(posX, posY, rectWidth, 24);
264
+ }
265
+ this.ctx.font = options.tooltips.fontStyle+ " "+options.tooltips.fontSize+" " + options.tooltips.fontFamily;
266
+ this.ctx.fillStyle = options.tooltips.fontColor;
267
+ this.ctx.textAlign = 'center';
268
+ this.ctx.textBaseline = 'middle';
269
+ this.ctx.fillText(tpl, posX+rectWidth/2, posY+12);
270
+ this.x = x;
271
+ this.y = y;
272
+ //}
273
+ }
274
+ }
275
+
276
+ //Variables global to the chart
277
+ var width = context.canvas.width,
278
+ height = context.canvas.height;
279
+
280
+ this.savedState = null;
281
+
282
+ function getPosition(e) {
283
+ var xPosition = 0;
284
+ var yPosition = 0;
285
+
286
+ while(e) {
287
+ xPosition += (e.offsetLeft - e.scrollLeft + e.clientLeft);
288
+ yPosition += (e.offsetTop - e.scrollTop + e.clientTop);
289
+ e = e.offsetParent;
290
+ }
291
+ return { x: xPosition, y: yPosition };
292
+ }
293
+
294
+ function tooltipEventHandler(e) {
295
+ if(chart.tooltips.length > 0) {
296
+ chart.savedState = chart.savedState == null ? context.getImageData(0,0,context.canvas.width,context.canvas.height) : chart.savedState;
297
+ var rendered = 0;
298
+ for(var i in chart.tooltips) {
299
+ var position = getPosition(context.canvas),
300
+ mx = (e.clientX)-position.x,
301
+ my = (e.clientY)-position.y;
302
+ if(chart.tooltips[i].inRange(mx,my)) {
303
+ chart.tooltips[i].render(mx,my);
304
+ rendered++;
305
+ }
306
+ }
307
+ if(rendered == 0) {
308
+ context.putImageData(chart.savedState,0,0);
309
+ }
310
+ }
311
+ }
312
+
313
+ if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
314
+ context.canvas.ontouchstart = function(e) {
315
+ e.clientX = e.targetTouches[0].clientX;
316
+ e.clientY = e.targetTouches[0].clientY;
317
+ tooltipEventHandler(e);
318
+ }
319
+ context.canvas.ontouchmove = function(e) {
320
+ e.clientX = e.targetTouches[0].clientX;
321
+ e.clientY = e.targetTouches[0].clientY;
322
+ tooltipEventHandler(e);
323
+ }
324
+ } else {
325
+ context.canvas.onmousemove = function(e) {
326
+ tooltipEventHandler(e);
327
+ }
328
+ }
329
+ context.canvas.onmouseout = function(e) {
330
+ if(chart.savedState != null) {
331
+ context.putImageData(chart.savedState,0,0);
332
+ }
333
+ }
334
+
335
+
336
+ //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
337
+ if (window.devicePixelRatio) {
338
+ context.canvas.style.width = width + "px";
339
+ context.canvas.style.height = height + "px";
340
+ context.canvas.height = height * window.devicePixelRatio;
341
+ context.canvas.width = width * window.devicePixelRatio;
342
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
343
+ }
344
+
345
+ this.PolarArea = function(data,options){
346
+
347
+ chart.PolarArea.defaults = {
348
+ scaleOverlay : true,
349
+ scaleOverride : false,
350
+ scaleSteps : null,
351
+ scaleStepWidth : null,
352
+ scaleStartValue : null,
353
+ scaleShowLine : true,
354
+ scaleLineColor : "rgba(0,0,0,0.1)",
355
+ scaleLineWidth : 1,
356
+ scaleShowLabels : true,
357
+ scaleLabel : "<%=value%>",
358
+ scaleFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
359
+ scaleFontSize : 12,
360
+ scaleFontStyle : "normal",
361
+ scaleFontColor : "rgba(71,74,84,1)",
362
+ scaleShowLabelBackdrop : true,
363
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
364
+ scaleBackdropPaddingY : 2,
365
+ scaleBackdropPaddingX : 2,
366
+ segmentShowStroke : true,
367
+ segmentStrokeColor : "#fff",
368
+ segmentStrokeWidth : 1,
369
+ animation : true,
370
+ animationSteps : 100,
371
+ animationEasing : "easeOutBounce",
372
+ animateRotate : true,
373
+ animateScale : false,
374
+ onAnimationComplete : null,
375
+ showTooltips : true
376
+ };
377
+
378
+ var config = (options)? mergeChartConfig(chart.PolarArea.defaults,options) : chart.PolarArea.defaults;
379
+
380
+ return new PolarArea(data,config,context);
381
+ };
382
+
383
+ this.Radar = function(data,options){
384
+
385
+ chart.Radar.defaults = {
386
+ scaleOverlay : false,
387
+ scaleOverride : false,
388
+ scaleSteps : null,
389
+ scaleStepWidth : null,
390
+ scaleStartValue : null,
391
+ scaleShowLine : true,
392
+ scaleLineColor : "rgba(0,0,0,0.1)",
393
+ scaleLineWidth : 1,
394
+ scaleShowLabels : false,
395
+ scaleLabel : "<%=value%>",
396
+ scaleFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
397
+ scaleFontSize : 12,
398
+ scaleFontStyle : "normal",
399
+ scaleFontColor : "rgba(71,74,84,1)",
400
+ scaleShowLabelBackdrop : true,
401
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
402
+ scaleBackdropPaddingY : 2,
403
+ scaleBackdropPaddingX : 2,
404
+ angleShowLineOut : true,
405
+ angleLineColor : "rgba(0,0,0,0.1)",
406
+ angleLineWidth : 1,
407
+ pointLabelFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
408
+ pointLabelFontStyle : "normal",
409
+ pointLabelFontSize : 12,
410
+ pointLabelFontColor : "rgba(71,74,84,1)",
411
+ pointDot : true,
412
+ pointDotRadius : 3,
413
+ pointDotStrokeWidth : 1,
414
+ datasetStroke : true,
415
+ datasetStrokeWidth : 1,
416
+ datasetFill : true,
417
+ animation : true,
418
+ animationSteps : 60,
419
+ animationEasing : "easeOutQuart",
420
+ onAnimationComplete : null,
421
+ showTooltips : true
422
+ };
423
+
424
+ var config = (options)? mergeChartConfig(chart.Radar.defaults,options) : chart.Radar.defaults;
425
+
426
+ return new Radar(data,config,context);
427
+ };
428
+
429
+ this.Pie = function(data,options){
430
+ chart.Pie.defaults = {
431
+ segmentShowStroke : true,
432
+ segmentStrokeColor : "#fff",
433
+ segmentStrokeWidth : 1,
434
+ animation : true,
435
+ animationSteps : 100,
436
+ animationEasing : "easeOutBounce",
437
+ animateRotate : true,
438
+ animateScale : false,
439
+ onAnimationComplete : null,
440
+ labelFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
441
+ labelFontStyle : "normal",
442
+ labelFontSize : 12,
443
+ labelFontColor : "rgba(71,74,84,1)",
444
+ labelAlign : 'right',
445
+ showTooltips : true
446
+ };
447
+
448
+ var config = (options)? mergeChartConfig(chart.Pie.defaults,options) : chart.Pie.defaults;
449
+
450
+ return new Pie(data,config,context);
451
+ };
452
+
453
+ this.Doughnut = function(data,options){
454
+
455
+ chart.Doughnut.defaults = {
456
+ segmentShowStroke : true,
457
+ segmentStrokeColor : "#fff",
458
+ segmentStrokeWidth : 1,
459
+ percentageInnerCutout : 50,
460
+ animation : true,
461
+ animationSteps : 100,
462
+ animationEasing : "easeOutBounce",
463
+ animateRotate : true,
464
+ animateScale : false,
465
+ onAnimationComplete : null,
466
+ showTooltips : true
467
+ };
468
+
469
+ var config = (options)? mergeChartConfig(chart.Doughnut.defaults,options) : chart.Doughnut.defaults;
470
+
471
+ return new Doughnut(data,config,context);
472
+
473
+ };
474
+
475
+ this.Line = function(data,options){
476
+
477
+ chart.Line.defaults = {
478
+ scaleOverlay : false,
479
+ scaleOverride : false,
480
+ scaleSteps : null,
481
+ scaleStepWidth : null,
482
+ scaleStartValue : null,
483
+ scaleLineColor : "rgba(0,0,0,0.1)",
484
+ scaleLineWidth : 1,
485
+ scaleShowLabels : true,
486
+ scaleLabel : "<%=value%>",
487
+ scaleFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
488
+ scaleFontSize : 12,
489
+ scaleFontStyle : "normal",
490
+ scaleFontColor : "rgba(71,74,84,1)",
491
+ scaleShowGridLines : true,
492
+ scaleGridLineColor : "rgba(0,0,0,0.05)",
493
+ scaleGridLineWidth : 1,
494
+ bezierCurve : true,
495
+ pointDot : true,
496
+ pointDotRadius : 4,
497
+ pointDotStrokeWidth : 1,
498
+ datasetStroke : true,
499
+ datasetStrokeWidth : 1,
500
+ datasetFill : true,
501
+ animation : true,
502
+ animationSteps : 60,
503
+ animationEasing : "easeOutQuart",
504
+ onAnimationComplete : null,
505
+ showTooltips : true
506
+ };
507
+ var config = (options) ? mergeChartConfig(chart.Line.defaults,options) : chart.Line.defaults;
508
+
509
+ return new Line(data,config,context);
510
+ }
511
+
512
+ this.Bar = function(data,options){
513
+ chart.Bar.defaults = {
514
+ scaleOverlay : false,
515
+ scaleOverride : false,
516
+ scaleSteps : null,
517
+ scaleStepWidth : null,
518
+ scaleStartValue : null,
519
+ scaleLineColor : "rgba(0,0,0,0.1)",
520
+ scaleLineWidth : 1,
521
+ scaleShowLabels : true,
522
+ scaleLabel : "<%=value%>",
523
+ scaleFontFamily : "'Gotham', 'Helvetica', Helvetica, Arial, sans-serif",
524
+ scaleFontSize : 12,
525
+ scaleFontStyle : "normal",
526
+ scaleFontColor : "rgba(71,74,84,1)",
527
+ scaleShowGridLines : true,
528
+ scaleGridLineColor : "rgba(0,0,0,0.05)",
529
+ scaleGridLineWidth : 1,
530
+ barShowStroke : true,
531
+ barStrokeWidth : 1,
532
+ barValueSpacing : 5,
533
+ barDatasetSpacing : 2,
534
+ animation : true,
535
+ animationSteps : 60,
536
+ animationEasing : "easeOutQuart",
537
+ onAnimationComplete : null,
538
+ showTooltips : true
539
+ };
540
+ var config = (options) ? mergeChartConfig(chart.Bar.defaults,options) : chart.Bar.defaults;
541
+
542
+ return new Bar(data,config,context);
543
+ }
544
+
545
+ var clear = function(c){
546
+ c.clearRect(0, 0, width, height);
547
+ };
548
+
549
+ var PolarArea = function(data,config,ctx){
550
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString;
551
+
552
+
553
+ calculateDrawingSizes();
554
+
555
+ valueBounds = getValueBounds();
556
+
557
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null;
558
+
559
+ //Check and set the scale
560
+ if (!config.scaleOverride){
561
+
562
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
563
+ }
564
+ else {
565
+ calculatedScale = {
566
+ steps : config.scaleSteps,
567
+ stepValue : config.scaleStepWidth,
568
+ graphMin : config.scaleStartValue,
569
+ labels : []
570
+ }
571
+ populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth);
572
+ }
573
+
574
+ scaleHop = maxSize/(calculatedScale.steps);
575
+
576
+ //Wrap in an animation loop wrapper
577
+ animationLoop(config,drawScale,drawAllSegments,ctx);
578
+
579
+ function calculateDrawingSizes(){
580
+ maxSize = (Min([width,height])/2);
581
+ //Remove whatever is larger - the font size or line width.
582
+
583
+ maxSize -= Max([config.scaleFontSize*0.5,config.scaleLineWidth*0.5]);
584
+
585
+ labelHeight = config.scaleFontSize*2;
586
+ //If we're drawing the backdrop - add the Y padding to the label height and remove from drawing region.
587
+ if (config.scaleShowLabelBackdrop){
588
+ labelHeight += (2 * config.scaleBackdropPaddingY);
589
+ maxSize -= config.scaleBackdropPaddingY*1.5;
590
+ }
591
+
592
+ scaleHeight = maxSize;
593
+ //If the label height is less than 5, set it to 5 so we don't have lines on top of each other.
594
+ labelHeight = Default(labelHeight,5);
595
+ }
596
+ function drawScale(){
597
+ for (var i=0; i<calculatedScale.steps; i++){
598
+ //If the line object is there
599
+ if (config.scaleShowLine){
600
+ ctx.beginPath();
601
+ ctx.arc(width/2, height/2, scaleHop * (i + 1), 0, (Math.PI * 2), true);
602
+ ctx.strokeStyle = config.scaleLineColor;
603
+ ctx.lineWidth = config.scaleLineWidth;
604
+ ctx.stroke();
605
+ }
606
+
607
+ if (config.scaleShowLabels){
608
+ ctx.textAlign = "center";
609
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize + "px " + config.scaleFontFamily;
610
+ var label = calculatedScale.labels[i];
611
+ //If the backdrop object is within the font object
612
+ if (config.scaleShowLabelBackdrop){
613
+ var textWidth = ctx.measureText(label).width;
614
+ ctx.fillStyle = config.scaleBackdropColor;
615
+ ctx.beginPath();
616
+ ctx.rect(
617
+ Math.round(width/2 - textWidth/2 - config.scaleBackdropPaddingX), //X
618
+ Math.round(height/2 - (scaleHop * (i + 1)) - config.scaleFontSize*0.5 - config.scaleBackdropPaddingY),//Y
619
+ Math.round(textWidth + (config.scaleBackdropPaddingX*2)), //Width
620
+ Math.round(config.scaleFontSize + (config.scaleBackdropPaddingY*2)) //Height
621
+ );
622
+ ctx.fill();
623
+ }
624
+ ctx.textBaseline = "middle";
625
+ ctx.fillStyle = config.scaleFontColor;
626
+ ctx.fillText(label,width/2,height/2 - (scaleHop * (i + 1)));
627
+ }
628
+ }
629
+ }
630
+ function drawAllSegments(animationDecimal){
631
+ var startAngle = -Math.PI/2,
632
+ angleStep = (Math.PI*2)/data.length,
633
+ scaleAnimation = 1,
634
+ rotateAnimation = 1;
635
+ if (config.animation) {
636
+ if (config.animateScale) {
637
+ scaleAnimation = animationDecimal;
638
+ }
639
+ if (config.animateRotate){
640
+ rotateAnimation = animationDecimal;
641
+ }
642
+ }
643
+
644
+ for (var i=0; i<data.length; i++){
645
+
646
+ ctx.beginPath();
647
+ ctx.arc(width/2,height/2,scaleAnimation * calculateOffset(data[i].value,calculatedScale,scaleHop),startAngle, startAngle + rotateAnimation*angleStep, false);
648
+ ctx.lineTo(width/2,height/2);
649
+ ctx.closePath();
650
+ ctx.fillStyle = data[i].color;
651
+ ctx.fill();
652
+
653
+ if(animationDecimal >= 1 && config.showTooltips) {
654
+ var points = [{x:width/2,y:height/2}],
655
+ pAmount = 50,
656
+ radius = calculateOffset(data[i].value,calculatedScale,scaleHop);
657
+ points.push({x:width/2+radius*Math.cos(startAngle),y:height/2+radius*Math.sin(startAngle)});
658
+ for(var p = 0; p <= pAmount; p++) {
659
+ points.push({x:width/2+radius*Math.cos(startAngle+p/pAmount*rotateAnimation*angleStep),y:height/2+radius*Math.sin(startAngle+p/pAmount*rotateAnimation*angleStep)});
660
+ }
661
+ registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'PolarArea');
662
+ }
663
+
664
+ if(config.segmentShowStroke){
665
+ ctx.strokeStyle = config.segmentStrokeColor;
666
+ ctx.lineWidth = config.segmentStrokeWidth;
667
+ ctx.stroke();
668
+ }
669
+ startAngle += rotateAnimation*angleStep;
670
+ }
671
+ }
672
+ function getValueBounds() {
673
+ var upperValue = Number.MIN_VALUE;
674
+ var lowerValue = Number.MAX_VALUE;
675
+ for (var i=0; i<data.length; i++){
676
+ if (data[i].value > upperValue) {upperValue = data[i].value;}
677
+ if (data[i].value < lowerValue) {lowerValue = data[i].value;}
678
+ };
679
+
680
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
681
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
682
+
683
+ return {
684
+ maxValue : upperValue,
685
+ minValue : lowerValue,
686
+ maxSteps : maxSteps,
687
+ minSteps : minSteps
688
+ };
689
+
690
+
691
+ }
692
+ }
693
+
694
+ var Radar = function (data,config,ctx) {
695
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString;
696
+
697
+ //If no labels are defined set to an empty array, so referencing length for looping doesn't blow up.
698
+ if (!data.labels) data.labels = [];
699
+
700
+ calculateDrawingSizes();
701
+
702
+ var valueBounds = getValueBounds();
703
+
704
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null;
705
+
706
+ //Check and set the scale
707
+ if (!config.scaleOverride){
708
+
709
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
710
+ }
711
+ else {
712
+ calculatedScale = {
713
+ steps : config.scaleSteps,
714
+ stepValue : config.scaleStepWidth,
715
+ graphMin : config.scaleStartValue,
716
+ labels : []
717
+ }
718
+ populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth);
719
+ }
720
+
721
+ scaleHop = maxSize/(calculatedScale.steps);
722
+
723
+ animationLoop(config,drawScale,drawAllDataPoints,ctx);
724
+
725
+ //Radar specific functions.
726
+ function drawAllDataPoints(animationDecimal){
727
+ var rotationDegree = (2*Math.PI)/data.datasets[0].data.length;
728
+
729
+ ctx.save();
730
+ //translate to the centre of the canvas.
731
+ ctx.translate(width/2,height/2);
732
+ //We accept multiple data sets for radar charts, so show loop through each set
733
+ for (var i=0; i<data.datasets.length; i++){
734
+ var offset = calculateOffset(data.datasets[i].data[0],calculatedScale,scaleHop);
735
+ ctx.beginPath();
736
+ ctx.moveTo(0,animationDecimal*(-1*offset));
737
+ if(animationDecimal >= 1 && config.showTooltips) {
738
+ var curX = width/2+offset*Math.cos(0-Math.PI/2),
739
+ curY = height/2+offset*Math.sin(0-Math.PI/2),
740
+ pointRadius = config.pointDot ? config.pointDotRadius+config.pointDotStrokeWidth : 10,
741
+ ttData = data.labels[0].trim() != "" ? data.labels[0]+": "+data.datasets[i].data[0] : data.datasets[i].data[0];
742
+ registerTooltip(ctx,{type:'circle',x:curX,y:curY,r:pointRadius},{label:data.labels[0],value:data.datasets[i].data[0]},'Radar');
743
+ }
744
+ for (var j=1; j<data.datasets[i].data.length; j++){
745
+ offset = calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop);
746
+ ctx.rotate(rotationDegree);
747
+ ctx.lineTo(0,animationDecimal*(-1*offset));
748
+ if(animationDecimal >= 1 && config.showTooltips) {
749
+ var curX = width/2+offset*Math.cos(j*rotationDegree-Math.PI/2),
750
+ curY = height/2+offset*Math.sin(j*rotationDegree-Math.PI/2),
751
+ pointRadius = config.pointDot ? config.pointDotRadius+config.pointDotStrokeWidth : 10,
752
+ ttData = data.labels[j].trim() != "" ? data.labels[j]+": "+data.datasets[i].data[j] : data.datasets[i].data[j];
753
+ registerTooltip(ctx,{type:'circle',x:curX,y:curY,r:pointRadius},{label:data.labels[j],value:data.datasets[i].data[j]},'Radar');
754
+ }
755
+ }
756
+ ctx.closePath();
757
+
758
+
759
+ ctx.fillStyle = data.datasets[i].fillColor;
760
+ ctx.strokeStyle = data.datasets[i].strokeColor;
761
+ ctx.lineWidth = config.datasetStrokeWidth;
762
+ ctx.fill();
763
+ ctx.stroke();
764
+
765
+
766
+ if (config.pointDot){
767
+ ctx.fillStyle = data.datasets[i].pointColor;
768
+ ctx.strokeStyle = data.datasets[i].pointStrokeColor;
769
+ ctx.lineWidth = config.pointDotStrokeWidth;
770
+ for (var k=0; k<data.datasets[i].data.length; k++){
771
+ ctx.rotate(rotationDegree);
772
+ ctx.beginPath();
773
+ ctx.arc(0,animationDecimal*(-1*calculateOffset(data.datasets[i].data[k],calculatedScale,scaleHop)),config.pointDotRadius,2*Math.PI,false);
774
+ ctx.fill();
775
+ ctx.stroke();
776
+ }
777
+
778
+ }
779
+ ctx.rotate(rotationDegree);
780
+ }
781
+ ctx.restore();
782
+
783
+
784
+ }
785
+ function drawScale(){
786
+ var rotationDegree = (2*Math.PI)/data.datasets[0].data.length;
787
+ ctx.save();
788
+ ctx.translate(width / 2, height / 2);
789
+
790
+ if (config.angleShowLineOut){
791
+ ctx.strokeStyle = config.angleLineColor;
792
+ ctx.lineWidth = config.angleLineWidth;
793
+ for (var h=0; h<data.datasets[0].data.length; h++){
794
+
795
+ ctx.rotate(rotationDegree);
796
+ ctx.beginPath();
797
+ ctx.moveTo(0,0);
798
+ ctx.lineTo(0,-maxSize);
799
+ ctx.stroke();
800
+ }
801
+ }
802
+
803
+ for (var i=0; i<calculatedScale.steps; i++){
804
+ ctx.beginPath();
805
+
806
+ if(config.scaleShowLine){
807
+ ctx.strokeStyle = config.scaleLineColor;
808
+ ctx.lineWidth = config.scaleLineWidth;
809
+ ctx.moveTo(0,-scaleHop * (i+1));
810
+ for (var j=0; j<data.datasets[0].data.length; j++){
811
+ ctx.rotate(rotationDegree);
812
+ ctx.lineTo(0,-scaleHop * (i+1));
813
+ }
814
+ ctx.closePath();
815
+ ctx.stroke();
816
+
817
+ }
818
+
819
+ if (config.scaleShowLabels){
820
+ ctx.textAlign = 'center';
821
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
822
+ ctx.textBaseline = "middle";
823
+
824
+ if (config.scaleShowLabelBackdrop){
825
+ var textWidth = ctx.measureText(calculatedScale.labels[i]).width;
826
+ ctx.fillStyle = config.scaleBackdropColor;
827
+ ctx.beginPath();
828
+ ctx.rect(
829
+ Math.round(- textWidth/2 - config.scaleBackdropPaddingX), //X
830
+ Math.round((-scaleHop * (i + 1)) - config.scaleFontSize*0.5 - config.scaleBackdropPaddingY),//Y
831
+ Math.round(textWidth + (config.scaleBackdropPaddingX*2)), //Width
832
+ Math.round(config.scaleFontSize + (config.scaleBackdropPaddingY*2)) //Height
833
+ );
834
+ ctx.fill();
835
+ }
836
+ ctx.fillStyle = config.scaleFontColor;
837
+ ctx.fillText(calculatedScale.labels[i],0,-scaleHop*(i+1));
838
+ }
839
+
840
+ }
841
+ for (var k=0; k<data.labels.length; k++){
842
+ ctx.font = config.pointLabelFontStyle + " " + config.pointLabelFontSize+"px " + config.pointLabelFontFamily;
843
+ ctx.fillStyle = config.pointLabelFontColor;
844
+ var opposite = Math.sin(rotationDegree*k) * (maxSize + config.pointLabelFontSize);
845
+ var adjacent = Math.cos(rotationDegree*k) * (maxSize + config.pointLabelFontSize);
846
+
847
+ if(rotationDegree*k == Math.PI || rotationDegree*k == 0){
848
+ ctx.textAlign = "center";
849
+ }
850
+ else if(rotationDegree*k > Math.PI){
851
+ ctx.textAlign = "right";
852
+ }
853
+ else{
854
+ ctx.textAlign = "left";
855
+ }
856
+
857
+ ctx.textBaseline = "middle";
858
+
859
+ ctx.fillText(data.labels[k],opposite,-adjacent);
860
+
861
+ }
862
+ ctx.restore();
863
+ };
864
+ function calculateDrawingSizes(){
865
+ maxSize = (Min([width,height])/2);
866
+
867
+ labelHeight = config.scaleFontSize*2;
868
+
869
+ var labelLength = 0;
870
+ for (var i=0; i<data.labels.length; i++){
871
+ ctx.font = config.pointLabelFontStyle + " " + config.pointLabelFontSize+"px " + config.pointLabelFontFamily;
872
+ var textMeasurement = ctx.measureText(data.labels[i]).width;
873
+ if(textMeasurement>labelLength) labelLength = textMeasurement;
874
+ }
875
+
876
+ //Figure out whats the largest - the height of the text or the width of what's there, and minus it from the maximum usable size.
877
+ maxSize -= Max([labelLength,((config.pointLabelFontSize/2)*1.5)]);
878
+
879
+ maxSize -= config.pointLabelFontSize;
880
+ maxSize = CapValue(maxSize, null, 0);
881
+ scaleHeight = maxSize;
882
+ //If the label height is less than 5, set it to 5 so we don't have lines on top of each other.
883
+ labelHeight = Default(labelHeight,5);
884
+ };
885
+ function getValueBounds() {
886
+ var upperValue = Number.MIN_VALUE;
887
+ var lowerValue = Number.MAX_VALUE;
888
+
889
+ for (var i=0; i<data.datasets.length; i++){
890
+ for (var j=0; j<data.datasets[i].data.length; j++){
891
+ if (data.datasets[i].data[j] > upperValue){upperValue = data.datasets[i].data[j]}
892
+ if (data.datasets[i].data[j] < lowerValue){lowerValue = data.datasets[i].data[j]}
893
+ }
894
+ }
895
+
896
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
897
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
898
+
899
+ return {
900
+ maxValue : upperValue,
901
+ minValue : lowerValue,
902
+ maxSteps : maxSteps,
903
+ minSteps : minSteps
904
+ };
905
+
906
+
907
+ }
908
+ }
909
+
910
+ var Pie = function(data,config,ctx){
911
+ var segmentTotal = 0;
912
+
913
+ //In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge.
914
+ var pieRadius = Min([height/2,width/2]) - 5;
915
+
916
+ for (var i=0; i<data.length; i++){
917
+ segmentTotal += data[i].value;
918
+ }
919
+ ctx.fillStyle = 'black';
920
+ ctx.textBaseline = 'base';
921
+
922
+ animationLoop(config,null,drawPieSegments,ctx);
923
+
924
+ function drawPieSegments (animationDecimal){
925
+ var cumulativeAngle = -Math.PI/2,
926
+ scaleAnimation = 1,
927
+ rotateAnimation = 1;
928
+ if (config.animation) {
929
+ if (config.animateScale) {
930
+ scaleAnimation = animationDecimal;
931
+ }
932
+ if (config.animateRotate){
933
+ rotateAnimation = animationDecimal;
934
+ }
935
+ }
936
+
937
+ for (var i=0; i<data.length; i++){
938
+ var segmentAngle = rotateAnimation * ((data[i].value/segmentTotal) * (Math.PI*2));
939
+ ctx.beginPath();
940
+ ctx.arc(width/2,height/2,scaleAnimation * pieRadius,cumulativeAngle,cumulativeAngle + segmentAngle);
941
+ ctx.lineTo(width/2,height/2);
942
+ ctx.closePath();
943
+ ctx.fillStyle = data[i].color;
944
+ ctx.fill();
945
+
946
+ if(data[i].label && scaleAnimation*pieRadius*2*segmentAngle/(2*Math.PI) > config.labelFontSize) {
947
+ function getPieLabelX(align, r) {
948
+ switch(align) {
949
+ case 'left':
950
+ return -r+20;
951
+ break;
952
+ case 'center':
953
+ return -r/2;
954
+ break;
955
+ }
956
+ return -10;
957
+ }
958
+
959
+ function reversePieLabelAlign(align) {
960
+ switch(align) {
961
+ case 'left': return 'right'; break;
962
+ case 'right': return 'left'; break;
963
+ case 'center': return align; break;
964
+ }
965
+ }
966
+
967
+ var fontSize = data[i].labelFontSize || config.labelFontSize+'px';
968
+
969
+ if(fontSize.match(/^[0-9]+$/g) != null) {
970
+ fontSize = fontSize+'px';
971
+ }
972
+ ctx.font = config.labelFontStyle+ " " +fontSize+" " + config.labelFontFamily;
973
+ ctx.fillStyle = getFadeColor(animationDecimal, data[i].labelColor || 'black', data[i].color);
974
+ ctx.textBaseline = 'middle';
975
+ // rotate text, so it perfectly fits in segments
976
+ var textRotation = -(cumulativeAngle + segmentAngle)+segmentAngle/2,
977
+ tX = width/2+scaleAnimation*pieRadius*Math.cos(textRotation),
978
+ tY = height/2-scaleAnimation*pieRadius*Math.sin(textRotation);
979
+ ctx.textAlign = data[i].labelAlign || config.labelAlign;
980
+ textX = getPieLabelX(ctx.textAlign, scaleAnimation*pieRadius);
981
+ if(textRotation < -Math.PI/2) {
982
+ textRotation -= Math.PI;
983
+ ctx.textAlign = reversePieLabelAlign(ctx.textAlign);
984
+ textX = -textX;
985
+ }
986
+ ctx.translate(tX, tY);
987
+ ctx.rotate(-textRotation);
988
+ ctx.fillText(data[i].label, textX, 0);
989
+ ctx.rotate(textRotation);
990
+ ctx.translate(-tX, -tY);
991
+ }
992
+
993
+ if(animationDecimal >= 1 && config.showTooltips) {
994
+ var points = [{x:width/2,y:height/2}],
995
+ pAmount = 50;
996
+ points.push({x:width/2+pieRadius*Math.cos(cumulativeAngle),y:height/2+pieRadius*Math.sin(cumulativeAngle)});
997
+ for(var p = 0; p <= pAmount; p++) {
998
+ points.push({x:width/2+pieRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+pieRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)});
999
+ }
1000
+ registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'Pie');
1001
+ }
1002
+
1003
+ if(config.segmentShowStroke){
1004
+ ctx.lineWidth = config.segmentStrokeWidth;
1005
+ ctx.strokeStyle = config.segmentStrokeColor;
1006
+ ctx.stroke();
1007
+ }
1008
+ cumulativeAngle += segmentAngle;
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ var Doughnut = function(data,config,ctx){
1014
+ var segmentTotal = 0;
1015
+
1016
+ //In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge.
1017
+ var doughnutRadius = Min([height/2,width/2]) - 5;
1018
+
1019
+ var cutoutRadius = doughnutRadius * (config.percentageInnerCutout/100);
1020
+
1021
+ for (var i=0; i<data.length; i++){
1022
+ segmentTotal += data[i].value;
1023
+ }
1024
+
1025
+
1026
+ animationLoop(config,null,drawPieSegments,ctx);
1027
+
1028
+
1029
+ function drawPieSegments (animationDecimal){
1030
+ var cumulativeAngle = -Math.PI/2,
1031
+ scaleAnimation = 1,
1032
+ rotateAnimation = 1;
1033
+ if (config.animation) {
1034
+ if (config.animateScale) {
1035
+ scaleAnimation = animationDecimal;
1036
+ }
1037
+ if (config.animateRotate){
1038
+ rotateAnimation = animationDecimal;
1039
+ }
1040
+ }
1041
+ for (var i=0; i<data.length; i++){
1042
+ var segmentAngle = rotateAnimation * ((data[i].value/segmentTotal) * (Math.PI*2));
1043
+ ctx.beginPath();
1044
+ ctx.arc(width/2,height/2,scaleAnimation * doughnutRadius,cumulativeAngle,cumulativeAngle + segmentAngle,false);
1045
+ ctx.arc(width/2,height/2,scaleAnimation * cutoutRadius,cumulativeAngle + segmentAngle,cumulativeAngle,true);
1046
+ ctx.closePath();
1047
+ ctx.fillStyle = data[i].color;
1048
+ ctx.fill();
1049
+
1050
+ if(animationDecimal >= 1 && config.showTooltips) {
1051
+ var points = [],
1052
+ pAmount = 50;
1053
+ points.push({x:width/2+doughnutRadius*Math.cos(cumulativeAngle),y:height/2+doughnutRadius*Math.sin(cumulativeAngle)});
1054
+ for(var p = 0; p <= pAmount; p++) {
1055
+ points.push({x:width/2+doughnutRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+doughnutRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)});
1056
+ }
1057
+ points.push({x:width/2+cutoutRadius*Math.cos(cumulativeAngle+segmentAngle),y:height/2+cutoutRadius*Math.sin(cumulativeAngle+segmentAngle)});
1058
+ for(var p = pAmount; p >= 0; p--) {
1059
+ points.push({x:width/2+cutoutRadius*Math.cos(cumulativeAngle+p/pAmount*segmentAngle),y:height/2+cutoutRadius*Math.sin(cumulativeAngle+p/pAmount*segmentAngle)});
1060
+ }
1061
+ registerTooltip(ctx,{type:'shape',points:points},{label:data[i].label,value:data[i].value},'Doughnut');
1062
+ }
1063
+
1064
+ if(config.segmentShowStroke){
1065
+ ctx.lineWidth = config.segmentStrokeWidth;
1066
+ ctx.strokeStyle = config.segmentStrokeColor;
1067
+ ctx.stroke();
1068
+ }
1069
+ cumulativeAngle += segmentAngle;
1070
+ }
1071
+ }
1072
+
1073
+
1074
+
1075
+ }
1076
+
1077
+ var Line = function(data,config,ctx){
1078
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY, rotateLabels = 0;
1079
+
1080
+ calculateDrawingSizes();
1081
+
1082
+ valueBounds = getValueBounds();
1083
+ //Check and set the scale
1084
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : "";
1085
+ if (!config.scaleOverride){
1086
+
1087
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
1088
+ }
1089
+ else {
1090
+ calculatedScale = {
1091
+ steps : config.scaleSteps,
1092
+ stepValue : config.scaleStepWidth,
1093
+ graphMin : config.scaleStartValue,
1094
+ labels : []
1095
+ }
1096
+ populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth);
1097
+ }
1098
+
1099
+ scaleHop = Math.floor(scaleHeight/calculatedScale.steps);
1100
+ calculateXAxisSize();
1101
+ animationLoop(config,drawScale,drawLines,ctx);
1102
+
1103
+ function drawLines(animPc){
1104
+ for (var i=0; i<data.datasets.length; i++){
1105
+ ctx.strokeStyle = data.datasets[i].strokeColor;
1106
+ ctx.lineWidth = config.datasetStrokeWidth;
1107
+ ctx.beginPath();
1108
+ ctx.moveTo(yAxisPosX, xAxisPosY - animPc*(calculateOffset(data.datasets[i].data[0],calculatedScale,scaleHop)))
1109
+
1110
+ for (var j=1; j<data.datasets[i].data.length; j++){
1111
+ if (config.bezierCurve){
1112
+ ctx.bezierCurveTo(xPos(j-0.5),yPos(i,j-1),xPos(j-0.5),yPos(i,j),xPos(j),yPos(i,j));
1113
+ }
1114
+ else{
1115
+ ctx.lineTo(xPos(j),yPos(i,j));
1116
+ }
1117
+ }
1118
+ var pointRadius = config.pointDot ? config.pointDotRadius+config.pointDotStrokeWidth : 10;
1119
+ for(var j = 0; j < data.datasets[i].data.length; j++) {
1120
+ if(animPc >= 1 && config.showTooltips) {
1121
+ // register tooltips
1122
+ registerTooltip(ctx,{type:'circle',x:xPos(j),y:yPos(i,j),r:pointRadius},{label:data.labels[j],value:data.datasets[i].data[j]},'Line');
1123
+ }
1124
+ }
1125
+ ctx.stroke();
1126
+ if (config.datasetFill){
1127
+ ctx.lineTo(yAxisPosX + (valueHop*(data.datasets[i].data.length-1)),xAxisPosY);
1128
+ ctx.lineTo(yAxisPosX,xAxisPosY);
1129
+ ctx.closePath();
1130
+ ctx.fillStyle = data.datasets[i].fillColor;
1131
+ ctx.fill();
1132
+ }
1133
+ else{
1134
+ ctx.closePath();
1135
+ }
1136
+ if(config.pointDot){
1137
+ ctx.fillStyle = data.datasets[i].pointColor;
1138
+ ctx.strokeStyle = data.datasets[i].pointStrokeColor;
1139
+ ctx.lineWidth = config.pointDotStrokeWidth;
1140
+ for (var k=0; k<data.datasets[i].data.length; k++){
1141
+ ctx.beginPath();
1142
+ ctx.arc(yAxisPosX + (valueHop *k),xAxisPosY - animPc*(calculateOffset(data.datasets[i].data[k],calculatedScale,scaleHop)),config.pointDotRadius,0,Math.PI*2,true);
1143
+ ctx.fill();
1144
+ ctx.stroke();
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ function yPos(dataSet,iteration){
1150
+ return xAxisPosY - animPc*(calculateOffset(data.datasets[dataSet].data[iteration],calculatedScale,scaleHop));
1151
+ }
1152
+ function xPos(iteration){
1153
+ return yAxisPosX + (valueHop * iteration);
1154
+ }
1155
+ }
1156
+ function drawScale(){
1157
+ //X axis line
1158
+ ctx.lineWidth = config.scaleLineWidth;
1159
+ ctx.strokeStyle = config.scaleLineColor;
1160
+ ctx.beginPath();
1161
+ ctx.moveTo(width-widestXLabel/2+5,xAxisPosY);
1162
+ ctx.lineTo(width-(widestXLabel/2)-xAxisLength-5,xAxisPosY);
1163
+ ctx.stroke();
1164
+
1165
+
1166
+ if (rotateLabels > 0){
1167
+ ctx.save();
1168
+ ctx.textAlign = "right";
1169
+ }
1170
+ else{
1171
+ ctx.textAlign = "center";
1172
+ }
1173
+ ctx.fillStyle = config.scaleFontColor;
1174
+ for (var i=0; i<data.labels.length; i++){
1175
+ ctx.save();
1176
+ if (rotateLabels > 0){
1177
+ ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize);
1178
+ ctx.rotate(-(rotateLabels * (Math.PI/180)));
1179
+ ctx.fillText(data.labels[i], 0,0);
1180
+ ctx.restore();
1181
+ }
1182
+
1183
+ else{
1184
+ ctx.fillText(data.labels[i], yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize+3);
1185
+ }
1186
+
1187
+ ctx.beginPath();
1188
+ ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY+3);
1189
+
1190
+ //Check i isnt 0, so we dont go over the Y axis twice.
1191
+ if(config.scaleShowGridLines && i>0){
1192
+ ctx.lineWidth = config.scaleGridLineWidth;
1193
+ ctx.strokeStyle = config.scaleGridLineColor;
1194
+ ctx.lineTo(yAxisPosX + i * valueHop, 5);
1195
+ }
1196
+ else{
1197
+ ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY+3);
1198
+ }
1199
+ ctx.stroke();
1200
+ }
1201
+
1202
+ //Y axis
1203
+ ctx.lineWidth = config.scaleLineWidth;
1204
+ ctx.strokeStyle = config.scaleLineColor;
1205
+ ctx.beginPath();
1206
+ ctx.moveTo(yAxisPosX,xAxisPosY+5);
1207
+ ctx.lineTo(yAxisPosX,5);
1208
+ ctx.stroke();
1209
+
1210
+ ctx.textAlign = "right";
1211
+ ctx.textBaseline = "middle";
1212
+ for (var j=0; j<calculatedScale.steps; j++){
1213
+ ctx.beginPath();
1214
+ ctx.moveTo(yAxisPosX-3,xAxisPosY - ((j+1) * scaleHop));
1215
+ if (config.scaleShowGridLines){
1216
+ ctx.lineWidth = config.scaleGridLineWidth;
1217
+ ctx.strokeStyle = config.scaleGridLineColor;
1218
+ ctx.lineTo(yAxisPosX + xAxisLength + 5,xAxisPosY - ((j+1) * scaleHop));
1219
+ }
1220
+ else{
1221
+ ctx.lineTo(yAxisPosX-0.5,xAxisPosY - ((j+1) * scaleHop));
1222
+ }
1223
+
1224
+ ctx.stroke();
1225
+
1226
+ if (config.scaleShowLabels){
1227
+ ctx.fillText(calculatedScale.labels[j],yAxisPosX-8,xAxisPosY - ((j+1) * scaleHop));
1228
+ }
1229
+ }
1230
+
1231
+
1232
+ }
1233
+ function calculateXAxisSize(){
1234
+ var longestText = 1;
1235
+ //if we are showing the labels
1236
+ if (config.scaleShowLabels){
1237
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
1238
+ for (var i=0; i<calculatedScale.labels.length; i++){
1239
+ var measuredText = ctx.measureText(calculatedScale.labels[i]).width;
1240
+ longestText = (measuredText > longestText)? measuredText : longestText;
1241
+ }
1242
+ //Add a little extra padding from the y axis
1243
+ longestText +=10;
1244
+ }
1245
+ xAxisLength = width - longestText - widestXLabel;
1246
+ valueHop = Math.floor(xAxisLength/(data.labels.length-1));
1247
+
1248
+ yAxisPosX = width-widestXLabel/2-xAxisLength;
1249
+ xAxisPosY = scaleHeight + config.scaleFontSize/2;
1250
+ }
1251
+ function calculateDrawingSizes(){
1252
+ maxSize = height;
1253
+
1254
+ //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees.
1255
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
1256
+ widestXLabel = 1;
1257
+ for (var i=0; i<data.labels.length; i++){
1258
+ var textLength = ctx.measureText(data.labels[i]).width;
1259
+ //If the text length is longer - make that equal to longest text!
1260
+ widestXLabel = (textLength > widestXLabel)? textLength : widestXLabel;
1261
+ }
1262
+ if (width/data.labels.length < widestXLabel){
1263
+ rotateLabels = 45;
1264
+ if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){
1265
+ rotateLabels = 90;
1266
+ maxSize -= widestXLabel;
1267
+ }
1268
+ else{
1269
+ maxSize -= Math.sin(rotateLabels) * widestXLabel;
1270
+ }
1271
+ }
1272
+ else{
1273
+ maxSize -= config.scaleFontSize;
1274
+ }
1275
+
1276
+ //Add a little padding between the x line and the text
1277
+ maxSize -= 5;
1278
+
1279
+
1280
+ labelHeight = config.scaleFontSize;
1281
+
1282
+ maxSize -= labelHeight;
1283
+ //Set 5 pixels greater than the font size to allow for a little padding from the X axis.
1284
+
1285
+ scaleHeight = maxSize;
1286
+
1287
+ //Then get the area above we can safely draw on.
1288
+
1289
+ }
1290
+ function getValueBounds() {
1291
+ var upperValue = Number.MIN_VALUE;
1292
+ var lowerValue = Number.MAX_VALUE;
1293
+ for (var i=0; i<data.datasets.length; i++){
1294
+ for (var j=0; j<data.datasets[i].data.length; j++){
1295
+ if ( data.datasets[i].data[j] > upperValue) { upperValue = data.datasets[i].data[j] };
1296
+ if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] };
1297
+ }
1298
+ };
1299
+
1300
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
1301
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
1302
+
1303
+ return {
1304
+ maxValue : upperValue,
1305
+ minValue : lowerValue,
1306
+ maxSteps : maxSteps,
1307
+ minSteps : minSteps
1308
+ };
1309
+
1310
+
1311
+ }
1312
+
1313
+
1314
+ }
1315
+
1316
+ var Bar = function(data,config,ctx){
1317
+ var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY,barWidth, rotateLabels = 0;
1318
+
1319
+ calculateDrawingSizes();
1320
+
1321
+ valueBounds = getValueBounds();
1322
+ //Check and set the scale
1323
+ labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : "";
1324
+ if (!config.scaleOverride){
1325
+
1326
+ calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString);
1327
+ }
1328
+ else {
1329
+ calculatedScale = {
1330
+ steps : config.scaleSteps,
1331
+ stepValue : config.scaleStepWidth,
1332
+ graphMin : config.scaleStartValue,
1333
+ labels : []
1334
+ }
1335
+ populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth);
1336
+ }
1337
+
1338
+ scaleHop = Math.floor(scaleHeight/calculatedScale.steps);
1339
+ calculateXAxisSize();
1340
+ animationLoop(config,drawScale,drawBars,ctx);
1341
+
1342
+ function drawBars(animPc){
1343
+ ctx.lineWidth = config.barStrokeWidth;
1344
+ for (var i=0; i<data.datasets.length; i++){
1345
+ for (var j=0; j<data.datasets[i].data.length; j++){
1346
+ var barOffset = yAxisPosX + config.barValueSpacing + valueHop*j + barWidth*i + config.barDatasetSpacing*i + config.barStrokeWidth*i;
1347
+ ctx.fillStyle = cycleColor(data.datasets[i].fillColor, j);
1348
+ ctx.strokeStyle = cycleColor(data.datasets[i].strokeColor, j);
1349
+ ctx.beginPath();
1350
+ ctx.moveTo(barOffset, xAxisPosY);
1351
+ ctx.lineTo(barOffset, xAxisPosY - animPc*calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop)+(config.barStrokeWidth/2));
1352
+ ctx.lineTo(barOffset + barWidth, xAxisPosY - animPc*calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop)+(config.barStrokeWidth/2));
1353
+ ctx.lineTo(barOffset + barWidth, xAxisPosY);
1354
+ if(config.barShowStroke){
1355
+ ctx.stroke();
1356
+ }
1357
+ ctx.closePath();
1358
+ ctx.fill();
1359
+
1360
+ if(animPc >= 1 && config.showTooltips) {
1361
+ // register tooltips
1362
+ var x = barOffset,
1363
+ height = calculateOffset(data.datasets[i].data[j],calculatedScale,scaleHop),
1364
+ y = xAxisPosY-height,
1365
+ width = barWidth;
1366
+ registerTooltip(ctx,{type:'rect',x:x,y:y,width:width,height:height},{label:data.labels[j],value:data.datasets[i].data[j]},'Bar');
1367
+ }
1368
+ }
1369
+ }
1370
+
1371
+ }
1372
+ function drawScale(){
1373
+ //X axis line
1374
+ ctx.lineWidth = config.scaleLineWidth;
1375
+ ctx.strokeStyle = config.scaleLineColor;
1376
+ ctx.beginPath();
1377
+ ctx.moveTo(width-widestXLabel/2+5,xAxisPosY);
1378
+ ctx.lineTo(width-(widestXLabel/2)-xAxisLength-5,xAxisPosY);
1379
+ ctx.stroke();
1380
+
1381
+
1382
+ if (rotateLabels > 0){
1383
+ ctx.save();
1384
+ ctx.textAlign = "right";
1385
+ }
1386
+ else{
1387
+ ctx.textAlign = "center";
1388
+ }
1389
+ ctx.fillStyle = config.scaleFontColor;
1390
+ for (var i=0; i<data.labels.length; i++){
1391
+ ctx.save();
1392
+ if (rotateLabels > 0){
1393
+ ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize);
1394
+ ctx.rotate(-(rotateLabels * (Math.PI/180)));
1395
+ ctx.fillText(data.labels[i], 0,0);
1396
+ ctx.restore();
1397
+ }
1398
+
1399
+ else{
1400
+ ctx.fillText(data.labels[i], yAxisPosX + i*valueHop + valueHop/2,xAxisPosY + config.scaleFontSize+3);
1401
+ }
1402
+
1403
+ ctx.beginPath();
1404
+ ctx.moveTo(yAxisPosX + (i+1) * valueHop, xAxisPosY+3);
1405
+
1406
+ //Check i isnt 0, so we dont go over the Y axis twice.
1407
+ ctx.lineWidth = config.scaleGridLineWidth;
1408
+ ctx.strokeStyle = config.scaleGridLineColor;
1409
+ ctx.lineTo(yAxisPosX + (i+1) * valueHop, 5);
1410
+ ctx.stroke();
1411
+ }
1412
+
1413
+ //Y axis
1414
+ ctx.lineWidth = config.scaleLineWidth;
1415
+ ctx.strokeStyle = config.scaleLineColor;
1416
+ ctx.beginPath();
1417
+ ctx.moveTo(yAxisPosX,xAxisPosY+5);
1418
+ ctx.lineTo(yAxisPosX,5);
1419
+ ctx.stroke();
1420
+
1421
+ ctx.textAlign = "right";
1422
+ ctx.textBaseline = "middle";
1423
+ for (var j=0; j<calculatedScale.steps; j++){
1424
+ ctx.beginPath();
1425
+ ctx.moveTo(yAxisPosX-3,xAxisPosY - ((j+1) * scaleHop));
1426
+ if (config.scaleShowGridLines){
1427
+ ctx.lineWidth = config.scaleGridLineWidth;
1428
+ ctx.strokeStyle = config.scaleGridLineColor;
1429
+ ctx.lineTo(yAxisPosX + xAxisLength + 5,xAxisPosY - ((j+1) * scaleHop));
1430
+ }
1431
+ else{
1432
+ ctx.lineTo(yAxisPosX-0.5,xAxisPosY - ((j+1) * scaleHop));
1433
+ }
1434
+
1435
+ ctx.stroke();
1436
+ if (config.scaleShowLabels){
1437
+ ctx.fillText(calculatedScale.labels[j],yAxisPosX-8,xAxisPosY - ((j+1) * scaleHop));
1438
+ }
1439
+ }
1440
+
1441
+
1442
+ }
1443
+ function calculateXAxisSize(){
1444
+ var longestText = 1;
1445
+ //if we are showing the labels
1446
+ if (config.scaleShowLabels){
1447
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
1448
+ for (var i=0; i<calculatedScale.labels.length; i++){
1449
+ var measuredText = ctx.measureText(calculatedScale.labels[i]).width;
1450
+ longestText = (measuredText > longestText)? measuredText : longestText;
1451
+ }
1452
+ //Add a little extra padding from the y axis
1453
+ longestText +=10;
1454
+ }
1455
+ xAxisLength = width - longestText - widestXLabel;
1456
+ valueHop = Math.floor(xAxisLength/(data.labels.length));
1457
+
1458
+ barWidth = (valueHop - config.scaleGridLineWidth*2 - (config.barValueSpacing*2) - (config.barDatasetSpacing*data.datasets.length-1) - ((config.barStrokeWidth/2)*data.datasets.length-1))/data.datasets.length;
1459
+
1460
+ yAxisPosX = width-widestXLabel/2-xAxisLength;
1461
+ xAxisPosY = scaleHeight + config.scaleFontSize/2;
1462
+ }
1463
+ function calculateDrawingSizes(){
1464
+ maxSize = height;
1465
+
1466
+ //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees.
1467
+ ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily;
1468
+ widestXLabel = 1;
1469
+ for (var i=0; i<data.labels.length; i++){
1470
+ var textLength = ctx.measureText(data.labels[i]).width;
1471
+ //If the text length is longer - make that equal to longest text!
1472
+ widestXLabel = (textLength > widestXLabel)? textLength : widestXLabel;
1473
+ }
1474
+ if (width/data.labels.length < widestXLabel){
1475
+ rotateLabels = 45;
1476
+ if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){
1477
+ rotateLabels = 90;
1478
+ maxSize -= widestXLabel;
1479
+ }
1480
+ else{
1481
+ maxSize -= Math.sin(rotateLabels) * widestXLabel;
1482
+ }
1483
+ }
1484
+ else{
1485
+ maxSize -= config.scaleFontSize;
1486
+ }
1487
+
1488
+ //Add a little padding between the x line and the text
1489
+ maxSize -= 5;
1490
+
1491
+
1492
+ labelHeight = config.scaleFontSize;
1493
+
1494
+ maxSize -= labelHeight;
1495
+ //Set 5 pixels greater than the font size to allow for a little padding from the X axis.
1496
+
1497
+ scaleHeight = maxSize;
1498
+
1499
+ //Then get the area above we can safely draw on.
1500
+
1501
+ }
1502
+ function getValueBounds() {
1503
+ var upperValue = Number.MIN_VALUE;
1504
+ var lowerValue = Number.MAX_VALUE;
1505
+ for (var i=0; i<data.datasets.length; i++){
1506
+ for (var j=0; j<data.datasets[i].data.length; j++){
1507
+ if ( data.datasets[i].data[j] > upperValue) { upperValue = data.datasets[i].data[j] };
1508
+ if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] };
1509
+ }
1510
+ };
1511
+
1512
+ var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66)));
1513
+ var minSteps = Math.floor((scaleHeight / labelHeight*0.5));
1514
+
1515
+ return {
1516
+ maxValue : upperValue,
1517
+ minValue : lowerValue,
1518
+ maxSteps : maxSteps,
1519
+ minSteps : minSteps
1520
+ };
1521
+
1522
+
1523
+ }
1524
+ }
1525
+
1526
+ function calculateOffset(val,calculatedScale,scaleHop){
1527
+ var outerValue = calculatedScale.steps * calculatedScale.stepValue;
1528
+ var adjustedValue = val - calculatedScale.graphMin;
1529
+ var scalingFactor = CapValue(adjustedValue/outerValue,1,0);
1530
+ return (scaleHop*calculatedScale.steps) * scalingFactor;
1531
+ }
1532
+
1533
+ function animationLoop(config,drawScale,drawData,ctx){
1534
+ var animFrameAmount = (config.animation)? 1/CapValue(config.animationSteps,Number.MAX_VALUE,1) : 1,
1535
+ easingFunction = animationOptions[config.animationEasing],
1536
+ percentAnimComplete =(config.animation)? 0 : 1;
1537
+
1538
+
1539
+
1540
+ if (typeof drawScale !== "function") drawScale = function(){};
1541
+
1542
+ requestAnimFrame(animLoop);
1543
+
1544
+ function animateFrame(){
1545
+ var easeAdjustedAnimationPercent =(config.animation)? CapValue(easingFunction(percentAnimComplete),null,0) : 1;
1546
+ clear(ctx);
1547
+ if(config.scaleOverlay){
1548
+ drawData(easeAdjustedAnimationPercent);
1549
+ drawScale();
1550
+ } else {
1551
+ drawScale();
1552
+ drawData(easeAdjustedAnimationPercent);
1553
+ }
1554
+ }
1555
+ function animLoop(){
1556
+ //We need to check if the animation is incomplete (less than 1), or complete (1).
1557
+ percentAnimComplete += animFrameAmount;
1558
+ animateFrame();
1559
+ //Stop the loop continuing forever
1560
+ if (percentAnimComplete <= 1){
1561
+ requestAnimFrame(animLoop);
1562
+ }
1563
+ else{
1564
+ if (typeof config.onAnimationComplete == "function") config.onAnimationComplete();
1565
+ }
1566
+
1567
+ }
1568
+
1569
+ }
1570
+
1571
+ //Declare global functions to be called within this namespace here.
1572
+
1573
+
1574
+ // shim layer with setTimeout fallback
1575
+ var requestAnimFrame = (function(){
1576
+ return window.requestAnimationFrame ||
1577
+ window.webkitRequestAnimationFrame ||
1578
+ window.mozRequestAnimationFrame ||
1579
+ window.oRequestAnimationFrame ||
1580
+ window.msRequestAnimationFrame ||
1581
+ function(callback) {
1582
+ window.setTimeout(callback, 1000 / 60);
1583
+ };
1584
+ })();
1585
+
1586
+ function calculateScale(drawingHeight,maxSteps,minSteps,maxValue,minValue,labelTemplateString){
1587
+ var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum;
1588
+ valueRange = maxValue - minValue;
1589
+ rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange);
1590
+ graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude);
1591
+ graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude);
1592
+ graphRange = graphMax - graphMin;
1593
+ stepValue = Math.pow(10, rangeOrderOfMagnitude);
1594
+ numberOfSteps = Math.round(graphRange / stepValue);
1595
+
1596
+ //Compare number of steps to the max and min for that size graph, and add in half steps if need be.
1597
+ while(numberOfSteps < minSteps || numberOfSteps > maxSteps) {
1598
+ if (numberOfSteps < minSteps){
1599
+ stepValue /= 2;
1600
+ numberOfSteps = Math.round(graphRange/stepValue);
1601
+ }
1602
+ else{
1603
+ stepValue *=2;
1604
+ numberOfSteps = Math.round(graphRange/stepValue);
1605
+ }
1606
+ }
1607
+
1608
+ var labels = [];
1609
+ populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue);
1610
+
1611
+ return {
1612
+ steps : numberOfSteps,
1613
+ stepValue : stepValue,
1614
+ graphMin : graphMin,
1615
+ labels : labels
1616
+ }
1617
+
1618
+ function calculateOrderOfMagnitude(val){
1619
+ return Math.floor(Math.log(val) / Math.LN10);
1620
+ }
1621
+ }
1622
+
1623
+ //Populate an array of all the labels by interpolating the string.
1624
+ function populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue) {
1625
+ if (labelTemplateString) {
1626
+ //Fix floating point errors by setting to fixed the on the same decimal as the stepValue.
1627
+ for (var i = 1; i < numberOfSteps + 1; i++) {
1628
+ labels.push(tmpl(labelTemplateString, {value: (graphMin + (stepValue * i)).toFixed(getDecimalPlaces(stepValue))}));
1629
+ }
1630
+ }
1631
+ }
1632
+ // Cycle a given array of colours (for multi coloured bars in bargraphs)
1633
+ function cycleColor(colors, i) {
1634
+ return (colors && colors.constructor.name == "Array") ? colors[i % colors.length] : colors;
1635
+ }
1636
+ //Max value from array
1637
+ function Max( array ){
1638
+ return Math.max.apply( Math, array );
1639
+ };
1640
+ //Min value from array
1641
+ function Min( array ){
1642
+ return Math.min.apply( Math, array );
1643
+ };
1644
+ //Default if undefined
1645
+ function Default(userDeclared,valueIfFalse){
1646
+ if(!userDeclared){
1647
+ return valueIfFalse;
1648
+ } else {
1649
+ return userDeclared;
1650
+ }
1651
+ };
1652
+ //Is a number function
1653
+ function isNumber(n) {
1654
+ return !isNaN(parseFloat(n)) && isFinite(n);
1655
+ }
1656
+ //Apply cap a value at a high or low number
1657
+ function CapValue(valueToCap, maxValue, minValue){
1658
+ if(isNumber(maxValue)) {
1659
+ if( valueToCap > maxValue ) {
1660
+ return maxValue;
1661
+ }
1662
+ }
1663
+ if(isNumber(minValue)){
1664
+ if ( valueToCap < minValue ){
1665
+ return minValue;
1666
+ }
1667
+ }
1668
+ return valueToCap;
1669
+ }
1670
+ function getDecimalPlaces (num){
1671
+ var numberOfDecimalPlaces;
1672
+ if (num%1!=0){
1673
+ return num.toString().split(".")[1].length
1674
+ }
1675
+ else{
1676
+ return 0;
1677
+ }
1678
+
1679
+ }
1680
+
1681
+ function mergeChartConfig(defaults,userDefined){
1682
+ var returnObj = {};
1683
+ for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; }
1684
+ for (var attrname in userDefined) {
1685
+ if(typeof(userDefined[attrname]) === "object" && defaults[attrname]) {
1686
+ returnObj[attrname] = mergeChartConfig(defaults[attrname], userDefined[attrname]);
1687
+ } else {
1688
+ returnObj[attrname] = userDefined[attrname];
1689
+ }
1690
+ }
1691
+ return returnObj;
1692
+ }
1693
+
1694
+ //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
1695
+ var cache = {};
1696
+
1697
+ function tmpl(str, data){
1698
+ // Figure out if we're getting a template, or if we need to
1699
+ // load the template - and be sure to cache the result.
1700
+ var fn = !/\W/.test(str) ?
1701
+ cache[str] = cache[str] ||
1702
+ tmpl(document.getElementById(str).innerHTML) :
1703
+
1704
+ // Generate a reusable function that will serve as a template
1705
+ // generator (and which will be cached).
1706
+ new Function("obj",
1707
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
1708
+
1709
+ // Introduce the data as local variables using with(){}
1710
+ "with(obj){p.push('" +
1711
+
1712
+ // Convert the template into pure JavaScript
1713
+ str
1714
+ .replace(/[\r\t\n]/g, " ")
1715
+ .split("<%").join("\t")
1716
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
1717
+ .replace(/\t=(.*?)%>/g, "',$1,'")
1718
+ .split("\t").join("');")
1719
+ .split("%>").join("p.push('")
1720
+ .split("\r").join("\\'")
1721
+ + "');}return p.join('');");
1722
+
1723
+ // Provide some basic currying to the user
1724
+ return data ? fn( data ) : fn;
1725
+ };
1726
+
1727
+ function getFadeColor(percent, primColor, secColor) {
1728
+ var pseudoEl = document.createElement('div'),
1729
+ rgbPrim,
1730
+ rgbSec;
1731
+ pseudoEl.style.color = primColor;
1732
+ document.body.appendChild(pseudoEl);
1733
+ rgbPrim = window.getComputedStyle(pseudoEl).color;
1734
+ pseudoEl.style.color = secColor;
1735
+ rgbSec = window.getComputedStyle(pseudoEl).color;
1736
+ var regex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/,
1737
+ valuesP = regex.exec(rgbPrim),
1738
+ valuesS = regex.exec(rgbSec),
1739
+ rP = Math.round(parseFloat(valuesP[1])),
1740
+ gP = Math.round(parseFloat(valuesP[2])),
1741
+ bP = Math.round(parseFloat(valuesP[3])),
1742
+ rS = Math.round(parseFloat(valuesS[1])),
1743
+ gS = Math.round(parseFloat(valuesS[2])),
1744
+ bS = Math.round(parseFloat(valuesS[3])),
1745
+ rCur = parseInt((rP-rS)*percent+rS),
1746
+ gCur = parseInt((gP-gS)*percent+gS),
1747
+ bCur = parseInt((bP-bS)*percent+bS);
1748
+ pseudoEl.parentNode.removeChild(pseudoEl);
1749
+ return "rgb("+rCur+','+gCur+','+bCur+')';
1750
+ }
1751
+ }