flashgrid-ext 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
+ }