flot-rails 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.md +2 -2
  2. data/lib/flot/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/excanvas.js +1428 -1427
  4. data/vendor/assets/javascripts/excanvas.min.js +1 -1
  5. data/vendor/assets/javascripts/jquery.colorhelpers.js +179 -179
  6. data/vendor/assets/javascripts/jquery.colorhelpers.min.js +21 -1
  7. data/vendor/assets/javascripts/jquery.flot.canvas.js +317 -0
  8. data/vendor/assets/javascripts/jquery.flot.canvas.min.js +28 -0
  9. data/vendor/assets/javascripts/jquery.flot.categories.js +190 -0
  10. data/vendor/assets/javascripts/jquery.flot.categories.min.js +44 -0
  11. data/vendor/assets/javascripts/jquery.flot.crosshair.js +176 -167
  12. data/vendor/assets/javascripts/jquery.flot.crosshair.min.js +59 -1
  13. data/vendor/assets/javascripts/jquery.flot.errorbars.js +353 -0
  14. data/vendor/assets/javascripts/jquery.flot.errorbars.min.js +63 -0
  15. data/vendor/assets/javascripts/jquery.flot.fillbetween.js +226 -183
  16. data/vendor/assets/javascripts/jquery.flot.fillbetween.min.js +30 -1
  17. data/vendor/assets/javascripts/jquery.flot.image.js +241 -238
  18. data/vendor/assets/javascripts/jquery.flot.image.min.js +53 -1
  19. data/vendor/assets/javascripts/jquery.flot.js +2980 -2599
  20. data/vendor/assets/javascripts/jquery.flot.min.js +26 -4
  21. data/vendor/assets/javascripts/jquery.flot.navigate.js +345 -336
  22. data/vendor/assets/javascripts/jquery.flot.navigate.min.js +96 -1
  23. data/vendor/assets/javascripts/jquery.flot.pie.js +561 -499
  24. data/vendor/assets/javascripts/jquery.flot.pie.min.js +56 -1
  25. data/vendor/assets/javascripts/jquery.flot.resize.js +60 -60
  26. data/vendor/assets/javascripts/jquery.flot.resize.min.js +21 -1
  27. data/vendor/assets/javascripts/jquery.flot.selection.js +360 -344
  28. data/vendor/assets/javascripts/jquery.flot.selection.min.js +79 -1
  29. data/vendor/assets/javascripts/jquery.flot.stack.js +188 -184
  30. data/vendor/assets/javascripts/jquery.flot.stack.min.js +36 -1
  31. data/vendor/assets/javascripts/jquery.flot.symbol.js +71 -70
  32. data/vendor/assets/javascripts/jquery.flot.symbol.min.js +14 -1
  33. data/vendor/assets/javascripts/jquery.flot.threshold.js +142 -103
  34. data/vendor/assets/javascripts/jquery.flot.threshold.min.js +43 -1
  35. data/vendor/assets/javascripts/jquery.flot.time.js +424 -0
  36. data/vendor/assets/javascripts/jquery.flot.time.min.js +9 -0
  37. metadata +25 -7
@@ -1 +1,96 @@
1
- (function(i){i.fn.drag=function(j,k,l){if(k){this.bind("dragstart",j)}if(l){this.bind("dragend",l)}return !j?this.trigger("drag"):this.bind("drag",k?k:j)};var d=i.event,c=d.special,h=c.drag={not:":input",distance:0,which:1,dragging:false,setup:function(j){j=i.extend({distance:h.distance,which:h.which,not:h.not},j||{});j.distance=e(j.distance);d.add(this,"mousedown",f,j);if(this.attachEvent){this.attachEvent("ondragstart",a)}},teardown:function(){d.remove(this,"mousedown",f);if(this===h.dragging){h.dragging=h.proxy=false}g(this,true);if(this.detachEvent){this.detachEvent("ondragstart",a)}}};c.dragstart=c.dragend={setup:function(){},teardown:function(){}};function f(j){var k=this,l,m=j.data||{};if(m.elem){k=j.dragTarget=m.elem;j.dragProxy=h.proxy||k;j.cursorOffsetX=m.pageX-m.left;j.cursorOffsetY=m.pageY-m.top;j.offsetX=j.pageX-j.cursorOffsetX;j.offsetY=j.pageY-j.cursorOffsetY}else{if(h.dragging||(m.which>0&&j.which!=m.which)||i(j.target).is(m.not)){return}}switch(j.type){case"mousedown":i.extend(m,i(k).offset(),{elem:k,target:j.target,pageX:j.pageX,pageY:j.pageY});d.add(document,"mousemove mouseup",f,m);g(k,false);h.dragging=null;return false;case !h.dragging&&"mousemove":if(e(j.pageX-m.pageX)+e(j.pageY-m.pageY)<m.distance){break}j.target=m.target;l=b(j,"dragstart",k);if(l!==false){h.dragging=k;h.proxy=j.dragProxy=i(l||k)[0]}case"mousemove":if(h.dragging){l=b(j,"drag",k);if(c.drop){c.drop.allowed=(l!==false);c.drop.handler(j)}if(l!==false){break}j.type="mouseup"}case"mouseup":d.remove(document,"mousemove mouseup",f);if(h.dragging){if(c.drop){c.drop.handler(j)}b(j,"dragend",k)}g(k,true);h.dragging=h.proxy=m.elem=false;break}return true}function b(m,k,j){m.type=k;var l=i.event.handle.call(j,m);return l===false?false:l||m.result}function e(j){return Math.pow(j,2)}function a(){return(h.dragging===false)}function g(j,k){if(!j){return}j.unselectable=k?"off":"on";j.onselectstart=function(){return k};if(j.style){j.style.MozUserSelect=k?"":"none"}}})(jQuery);(function(f){var e=["DOMMouseScroll","mousewheel"];f.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var a=e.length;a;){this.addEventListener(e[--a],d,false)}}else{this.onmousewheel=d}},teardown:function(){if(this.removeEventListener){for(var a=e.length;a;){this.removeEventListener(e[--a],d,false)}}else{this.onmousewheel=null}}};f.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}});function d(b){var h=[].slice.call(arguments,1),a=0,c=true;b=f.event.fix(b||window.event);b.type="mousewheel";if(b.wheelDelta){a=b.wheelDelta/120}if(b.detail){a=-b.detail/3}h.unshift(b,a);return f.event.handle.apply(this,h)}})(jQuery);(function(b){var a={xaxis:{zoomRange:null,panRange:null},zoom:{interactive:false,trigger:"dblclick",amount:1.5},pan:{interactive:false,cursor:"move",frameRate:20}};function c(o){function m(q,p){var r=o.offset();r.left=q.pageX-r.left;r.top=q.pageY-r.top;if(p){o.zoomOut({center:r})}else{o.zoom({center:r})}}function d(p,q){m(p,q<0);return false}var i="default",g=0,e=0,n=null;function f(p){if(p.which!=1){return false}var q=o.getPlaceholder().css("cursor");if(q){i=q}o.getPlaceholder().css("cursor",o.getOptions().pan.cursor);g=p.pageX;e=p.pageY}function j(q){var p=o.getOptions().pan.frameRate;if(n||!p){return}n=setTimeout(function(){o.pan({left:g-q.pageX,top:e-q.pageY});g=q.pageX;e=q.pageY;n=null},1/p*1000)}function h(p){if(n){clearTimeout(n);n=null}o.getPlaceholder().css("cursor",i);o.pan({left:g-p.pageX,top:e-p.pageY})}function l(q,p){var r=q.getOptions();if(r.zoom.interactive){p[r.zoom.trigger](m);p.mousewheel(d)}if(r.pan.interactive){p.bind("dragstart",{distance:10},f);p.bind("drag",j);p.bind("dragend",h)}}o.zoomOut=function(p){if(!p){p={}}if(!p.amount){p.amount=o.getOptions().zoom.amount}p.amount=1/p.amount;o.zoom(p)};o.zoom=function(q){if(!q){q={}}var x=q.center,r=q.amount||o.getOptions().zoom.amount,p=o.width(),t=o.height();if(!x){x={left:p/2,top:t/2}}var s=x.left/p,v=x.top/t,u={x:{min:x.left-s*p/r,max:x.left+(1-s)*p/r},y:{min:x.top-v*t/r,max:x.top+(1-v)*t/r}};b.each(o.getAxes(),function(z,C){var D=C.options,B=u[C.direction].min,w=u[C.direction].max,E=D.zoomRange;if(E===false){return}B=C.c2p(B);w=C.c2p(w);if(B>w){var A=B;B=w;w=A}var y=w-B;if(E&&((E[0]!=null&&y<E[0])||(E[1]!=null&&y>E[1]))){return}D.min=B;D.max=w});o.setupGrid();o.draw();if(!q.preventEvent){o.getPlaceholder().trigger("plotzoom",[o])}};o.pan=function(p){var q={x:+p.left,y:+p.top};if(isNaN(q.x)){q.x=0}if(isNaN(q.y)){q.y=0}b.each(o.getAxes(),function(s,u){var v=u.options,t,r,w=q[u.direction];t=u.c2p(u.p2c(u.min)+w),r=u.c2p(u.p2c(u.max)+w);var x=v.panRange;if(x===false){return}if(x){if(x[0]!=null&&x[0]>t){w=x[0]-t;t+=w;r+=w}if(x[1]!=null&&x[1]<r){w=x[1]-r;t+=w;r+=w}}v.min=t;v.max=r});o.setupGrid();o.draw();if(!p.preventEvent){o.getPlaceholder().trigger("plotpan",[o])}};function k(q,p){p.unbind(q.getOptions().zoom.trigger,m);p.unbind("mousewheel",d);p.unbind("dragstart",f);p.unbind("drag",j);p.unbind("dragend",h);if(n){clearTimeout(n)}}o.hooks.bindEvents.push(l);o.hooks.shutdown.push(k)}b.plot.plugins.push({init:c,options:a,name:"navigate",version:"1.3"})})(jQuery);
1
+ /* Flot plugin for adding the ability to pan and zoom the plot.
2
+
3
+ Copyright (c) 2007-2013 IOLA and Ole Laursen.
4
+ Licensed under the MIT license.
5
+
6
+ The default behaviour is double click and scrollwheel up/down to zoom in, drag
7
+ to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
8
+ plot.pan( offset ) so you easily can add custom controls. It also fires
9
+ "plotpan" and "plotzoom" events, useful for synchronizing plots.
10
+
11
+ The plugin supports these options:
12
+
13
+ zoom: {
14
+ interactive: false
15
+ trigger: "dblclick" // or "click" for single click
16
+ amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
17
+ }
18
+
19
+ pan: {
20
+ interactive: false
21
+ cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer"
22
+ frameRate: 20
23
+ }
24
+
25
+ xaxis, yaxis, x2axis, y2axis: {
26
+ zoomRange: null // or [ number, number ] (min range, max range) or false
27
+ panRange: null // or [ number, number ] (min, max) or false
28
+ }
29
+
30
+ "interactive" enables the built-in drag/click behaviour. If you enable
31
+ interactive for pan, then you'll have a basic plot that supports moving
32
+ around; the same for zoom.
33
+
34
+ "amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
35
+ the current viewport.
36
+
37
+ "cursor" is a standard CSS mouse cursor string used for visual feedback to the
38
+ user when dragging.
39
+
40
+ "frameRate" specifies the maximum number of times per second the plot will
41
+ update itself while the user is panning around on it (set to null to disable
42
+ intermediate pans, the plot will then not update until the mouse button is
43
+ released).
44
+
45
+ "zoomRange" is the interval in which zooming can happen, e.g. with zoomRange:
46
+ [1, 100] the zoom will never scale the axis so that the difference between min
47
+ and max is smaller than 1 or larger than 100. You can set either end to null
48
+ to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
49
+ will be disabled.
50
+
51
+ "panRange" confines the panning to stay within a range, e.g. with panRange:
52
+ [-10, 20] panning stops at -10 in one end and at 20 in the other. Either can
53
+ be null, e.g. [-10, null]. If you set panRange to false, panning on that axis
54
+ will be disabled.
55
+
56
+ Example API usage:
57
+
58
+ plot = $.plot(...);
59
+
60
+ // zoom default amount in on the pixel ( 10, 20 )
61
+ plot.zoom({ center: { left: 10, top: 20 } });
62
+
63
+ // zoom out again
64
+ plot.zoomOut({ center: { left: 10, top: 20 } });
65
+
66
+ // zoom 200% in on the pixel (10, 20)
67
+ plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
68
+
69
+ // pan 100 pixels to the left and 20 down
70
+ plot.pan({ left: -100, top: 20 })
71
+
72
+ Here, "center" specifies where the center of the zooming should happen. Note
73
+ that this is defined in pixel space, not the space of the data points (you can
74
+ use the p2c helpers on the axes in Flot to help you convert between these).
75
+
76
+ "amount" is the amount to zoom the viewport relative to the current range, so
77
+ 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
78
+ can set the default in the options.
79
+
80
+ */
81
+ /*
82
+ jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
83
+ Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
84
+ */(function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY)<l.distance)break;h.target=l.target,k=f(h,"dragstart",j),k!==!1&&(d.dragging=j,d.proxy=h.dragProxy=a(k||j)[0]);case"mousemove":if(d.dragging){if(k=f(h,"drag",j),c.drop&&(c.drop.allowed=k!==!1,c.drop.handler(h)),k!==!1)break;h.type="mouseup"}case"mouseup":b.remove(document,"mousemove mouseup",e),d.dragging&&(c.drop&&c.drop.handler(h),f(h,"dragend",j)),i(j,!0),d.dragging=d.proxy=l.elem=!1}return!0}function f(b,c,d){b.type=c;var e=a.event.dispatch.call(d,b);return e===!1?!1:e||b.result}function g(a){return Math.pow(a,2)}function h(){return d.dragging===!1}function i(a,b){a&&(a.unselectable=b?"off":"on",a.onselectstart=function(){return b},a.style&&(a.style.MozUserSelect=b?"":"none"))}a.fn.drag=function(a,b,c){return b&&this.bind("dragstart",a),c&&this.bind("dragend",c),a?this.bind("drag",b?b:a):this.trigger("drag")};var b=a.event,c=b.special,d=c.drag={not:":input",distance:0,which:1,dragging:!1,setup:function(c){c=a.extend({distance:d.distance,which:d.which,not:d.not},c||{}),c.distance=g(c.distance),b.add(this,"mousedown",e,c),this.attachEvent&&this.attachEvent("ondragstart",h)},teardown:function(){b.remove(this,"mousedown",e),this===d.dragging&&(d.dragging=d.proxy=!1),i(this,!0),this.detachEvent&&this.detachEvent("ondragstart",h)}};c.dragstart=c.dragend={setup:function(){},teardown:function(){}}})(jQuery);
85
+ /* jquery.mousewheel.min.js
86
+ * Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
87
+ * Licensed under the MIT License (LICENSE.txt).
88
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
89
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
90
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
91
+ *
92
+ * Version: 3.0.6
93
+ *
94
+ * Requires: 1.2.2+
95
+ */(function(d){function e(a){var b=a||window.event,c=[].slice.call(arguments,1),f=0,e=0,g=0,a=d.event.fix(b);a.type="mousewheel";b.wheelDelta&&(f=b.wheelDelta/120);b.detail&&(f=-b.detail/3);g=f;void 0!==b.axis&&b.axis===b.HORIZONTAL_AXIS&&(g=0,e=-1*f);void 0!==b.wheelDeltaY&&(g=b.wheelDeltaY/120);void 0!==b.wheelDeltaX&&(e=-1*b.wheelDeltaX/120);c.unshift(a,f,e,g);return(d.event.dispatch||d.event.handle).apply(this,c)}var c=["DOMMouseScroll","mousewheel"];if(d.event.fixHooks)for(var h=c.length;h;)d.event.fixHooks[c[--h]]=d.event.mouseHooks;d.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.addEventListener(c[--a],e,!1);else this.onmousewheel=e},teardown:function(){if(this.removeEventListener)for(var a=c.length;a;)this.removeEventListener(c[--a],e,!1);else this.onmousewheel=null}};d.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);
96
+ (function(e){function n(t){function n(e,n){var r=t.offset();r.left=e.pageX-r.left,r.top=e.pageY-r.top,n?t.zoomOut({center:r}):t.zoom({center:r})}function r(e,t){return n(e,t<0),!1}function a(e){if(e.which!=1)return!1;var n=t.getPlaceholder().css("cursor");n&&(i=n),t.getPlaceholder().css("cursor",t.getOptions().pan.cursor),s=e.pageX,o=e.pageY}function f(e){var n=t.getOptions().pan.frameRate;if(u||!n)return;u=setTimeout(function(){t.pan({left:s-e.pageX,top:o-e.pageY}),s=e.pageX,o=e.pageY,u=null},1/n*1e3)}function l(e){u&&(clearTimeout(u),u=null),t.getPlaceholder().css("cursor",i),t.pan({left:s-e.pageX,top:o-e.pageY})}function c(e,t){var i=e.getOptions();i.zoom.interactive&&(t[i.zoom.trigger](n),t.mousewheel(r)),i.pan.interactive&&(t.bind("dragstart",{distance:10},a),t.bind("drag",f),t.bind("dragend",l))}function h(e,t){t.unbind(e.getOptions().zoom.trigger,n),t.unbind("mousewheel",r),t.unbind("dragstart",a),t.unbind("drag",f),t.unbind("dragend",l),u&&clearTimeout(u)}var i="default",s=0,o=0,u=null;t.zoomOut=function(e){e||(e={}),e.amount||(e.amount=t.getOptions().zoom.amount),e.amount=1/e.amount,t.zoom(e)},t.zoom=function(n){n||(n={});var r=n.center,i=n.amount||t.getOptions().zoom.amount,s=t.width(),o=t.height();r||(r={left:s/2,top:o/2});var u=r.left/s,a=r.top/o,f={x:{min:r.left-u*s/i,max:r.left+(1-u)*s/i},y:{min:r.top-a*o/i,max:r.top+(1-a)*o/i}};e.each(t.getAxes(),function(e,t){var n=t.options,r=f[t.direction].min,i=f[t.direction].max,s=n.zoomRange,o=n.panRange;if(s===!1)return;r=t.c2p(r),i=t.c2p(i);if(r>i){var u=r;r=i,i=u}o&&(o[0]!=null&&r<o[0]&&(r=o[0]),o[1]!=null&&i>o[1]&&(i=o[1]));var a=i-r;if(s&&(s[0]!=null&&a<s[0]||s[1]!=null&&a>s[1]))return;n.min=r,n.max=i}),t.setupGrid(),t.draw(),n.preventEvent||t.getPlaceholder().trigger("plotzoom",[t,n])},t.pan=function(n){var r={x:+n.left,y:+n.top};isNaN(r.x)&&(r.x=0),isNaN(r.y)&&(r.y=0),e.each(t.getAxes(),function(e,t){var n=t.options,i,s,o=r[t.direction];i=t.c2p(t.p2c(t.min)+o),s=t.c2p(t.p2c(t.max)+o);var u=n.panRange;if(u===!1)return;u&&(u[0]!=null&&u[0]>i&&(o=u[0]-i,i+=o,s+=o),u[1]!=null&&u[1]<s&&(o=u[1]-s,i+=o,s+=o)),n.min=i,n.max=s}),t.setupGrid(),t.draw(),n.preventEvent||t.getPlaceholder().trigger("plotpan",[t,n])},t.hooks.bindEvents.push(c),t.hooks.shutdown.push(h)}var t={xaxis:{zoomRange:null,panRange:null},zoom:{interactive:!1,trigger:"dblclick",amount:1.5},pan:{interactive:!1,cursor:"move",frameRate:20}};e.plot.plugins.push({init:n,options:t,name:"navigate",version:"1.3"})})(jQuery);
@@ -1,659 +1,714 @@
1
- /*
2
- Flot plugin for rendering pie charts. The plugin assumes the data is
3
- coming is as a single data value for each series, and each of those
4
- values is a positive value or zero (negative numbers don't make
5
- any sense and will cause strange effects). The data values do
6
- NOT need to be passed in as percentage values because it
7
- internally calculates the total and percentages.
8
-
9
- * Created by Brian Medendorp, June 2009
10
- * Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
11
-
12
- * Changes:
13
- 2009-10-22: lineJoin set to round
14
- 2009-10-23: IE full circle fix, donut
15
- 2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
16
- 2009-11-17: Added IE hover capability submitted by Anthony Aragues
17
- 2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
18
-
19
-
20
- Available options are:
21
- series: {
22
- pie: {
23
- show: true/false
24
- radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
25
- innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
26
- startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
27
- tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
28
- offset: {
29
- top: integer value to move the pie up or down
30
- left: integer value to move the pie left or right, or 'auto'
31
- },
32
- stroke: {
33
- color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
34
- width: integer pixel width of the stroke
35
- },
36
- label: {
37
- show: true/false, or 'auto'
38
- formatter: a user-defined function that modifies the text/style of the label text
39
- radius: 0-1 for percentage of fullsize, or a specified pixel length
40
- background: {
41
- color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
42
- opacity: 0-1
1
+ /* Flot plugin for rendering pie charts.
2
+
3
+ Copyright (c) 2007-2013 IOLA and Ole Laursen.
4
+ Licensed under the MIT license.
5
+
6
+ The plugin assumes that each series has a single data value, and that each
7
+ value is a positive integer or zero. Negative numbers don't make sense for a
8
+ pie chart, and have unpredictable results. The values do NOT need to be
9
+ passed in as percentages; the plugin will calculate the total and per-slice
10
+ percentages internally.
11
+
12
+ * Created by Brian Medendorp
13
+
14
+ * Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars
15
+
16
+ The plugin supports these options:
17
+
18
+ series: {
19
+ pie: {
20
+ show: true/false
21
+ radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
22
+ innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
23
+ startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
24
+ tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
25
+ offset: {
26
+ top: integer value to move the pie up or down
27
+ left: integer value to move the pie left or right, or 'auto'
43
28
  },
44
- threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
45
- },
46
- combine: {
47
- threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
48
- color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
49
- label: any text value of what the combined slice should be labeled
50
- }
51
- highlight: {
52
- opacity: 0-1
29
+ stroke: {
30
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
31
+ width: integer pixel width of the stroke
32
+ },
33
+ label: {
34
+ show: true/false, or 'auto'
35
+ formatter: a user-defined function that modifies the text/style of the label text
36
+ radius: 0-1 for percentage of fullsize, or a specified pixel length
37
+ background: {
38
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
39
+ opacity: 0-1
40
+ },
41
+ threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
42
+ },
43
+ combine: {
44
+ threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
45
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
46
+ label: any text value of what the combined slice should be labeled
47
+ }
48
+ highlight: {
49
+ opacity: 0-1
50
+ }
53
51
  }
54
52
  }
55
- }
56
53
 
57
54
  More detail and specific examples can be found in the included HTML file.
58
55
 
59
56
  */
60
57
 
61
- (function ($)
62
- {
63
- function init(plot) // this is the "body" of the plugin
64
- {
65
- var canvas = null;
66
- var target = null;
67
- var maxRadius = null;
68
- var centerLeft = null;
69
- var centerTop = null;
70
- var total = 0;
71
- var redraw = true;
72
- var redrawAttempts = 10;
73
- var shrink = 0.95;
74
- var legendWidth = 0;
75
- var processed = false;
76
- var raw = false;
77
-
78
- // interactive variables
79
- var highlights = [];
80
-
58
+ (function($) {
59
+
60
+ // Maximum redraw attempts when fitting labels within the plot
61
+
62
+ var REDRAW_ATTEMPTS = 10;
63
+
64
+ // Factor by which to shrink the pie when fitting labels within the plot
65
+
66
+ var REDRAW_SHRINK = 0.95;
67
+
68
+ function init(plot) {
69
+
70
+ var canvas = null,
71
+ target = null,
72
+ maxRadius = null,
73
+ centerLeft = null,
74
+ centerTop = null,
75
+ processed = false,
76
+ ctx = null;
77
+
78
+ // interactive variables
79
+
80
+ var highlights = [];
81
+
81
82
  // add hook to determine if pie plugin in enabled, and then perform necessary operations
82
- plot.hooks.processOptions.push(checkPieEnabled);
83
- plot.hooks.bindEvents.push(bindEvents);
84
-
85
- // check to see if the pie plugin is enabled
86
- function checkPieEnabled(plot, options)
87
- {
88
- if (options.series.pie.show)
89
- {
90
- //disable grid
83
+
84
+ plot.hooks.processOptions.push(function(plot, options) {
85
+ if (options.series.pie.show) {
86
+
91
87
  options.grid.show = false;
92
-
88
+
93
89
  // set labels.show
94
- if (options.series.pie.label.show=='auto')
95
- if (options.legend.show)
90
+
91
+ if (options.series.pie.label.show == "auto") {
92
+ if (options.legend.show) {
96
93
  options.series.pie.label.show = false;
97
- else
94
+ } else {
98
95
  options.series.pie.label.show = true;
99
-
96
+ }
97
+ }
98
+
100
99
  // set radius
101
- if (options.series.pie.radius=='auto')
102
- if (options.series.pie.label.show)
100
+
101
+ if (options.series.pie.radius == "auto") {
102
+ if (options.series.pie.label.show) {
103
103
  options.series.pie.radius = 3/4;
104
- else
104
+ } else {
105
105
  options.series.pie.radius = 1;
106
-
106
+ }
107
+ }
108
+
107
109
  // ensure sane tilt
108
- if (options.series.pie.tilt>1)
109
- options.series.pie.tilt=1;
110
- if (options.series.pie.tilt<0)
111
- options.series.pie.tilt=0;
112
-
113
- // add processData hook to do transformations on the data
114
- plot.hooks.processDatapoints.push(processDatapoints);
115
- plot.hooks.drawOverlay.push(drawOverlay);
116
-
117
- // add draw hook
118
- plot.hooks.draw.push(draw);
110
+
111
+ if (options.series.pie.tilt > 1) {
112
+ options.series.pie.tilt = 1;
113
+ } else if (options.series.pie.tilt < 0) {
114
+ options.series.pie.tilt = 0;
115
+ }
119
116
  }
120
- }
121
-
122
- // bind hoverable events
123
- function bindEvents(plot, eventHolder)
124
- {
117
+ });
118
+
119
+ plot.hooks.bindEvents.push(function(plot, eventHolder) {
125
120
  var options = plot.getOptions();
126
-
127
- if (options.series.pie.show && options.grid.hoverable)
128
- eventHolder.unbind('mousemove').mousemove(onMouseMove);
129
-
130
- if (options.series.pie.show && options.grid.clickable)
131
- eventHolder.unbind('click').click(onClick);
132
- }
133
-
134
-
135
- // debugging function that prints out an object
136
- function alertObject(obj)
137
- {
138
- var msg = '';
139
- function traverse(obj, depth)
140
- {
141
- if (!depth)
142
- depth = 0;
143
- for (var i = 0; i < obj.length; ++i)
144
- {
145
- for (var j=0; j<depth; j++)
146
- msg += '\t';
147
-
148
- if( typeof obj[i] == "object")
149
- { // its an object
150
- msg += ''+i+':\n';
151
- traverse(obj[i], depth+1);
152
- }
153
- else
154
- { // its a value
155
- msg += ''+i+': '+obj[i]+'\n';
156
- }
121
+ if (options.series.pie.show) {
122
+ if (options.grid.hoverable) {
123
+ eventHolder.unbind("mousemove").mousemove(onMouseMove);
124
+ }
125
+ if (options.grid.clickable) {
126
+ eventHolder.unbind("click").click(onClick);
157
127
  }
158
128
  }
159
- traverse(obj);
160
- alert(msg);
161
- }
162
-
163
- function calcTotal(data)
164
- {
165
- for (var i = 0; i < data.length; ++i)
166
- {
167
- var item = parseFloat(data[i].data[0][1]);
168
- if (item)
169
- total += item;
129
+ });
130
+
131
+ plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) {
132
+ var options = plot.getOptions();
133
+ if (options.series.pie.show) {
134
+ processDatapoints(plot, series, data, datapoints);
170
135
  }
171
- }
172
-
173
- function processDatapoints(plot, series, data, datapoints)
174
- {
175
- if (!processed)
176
- {
136
+ });
137
+
138
+ plot.hooks.drawOverlay.push(function(plot, octx) {
139
+ var options = plot.getOptions();
140
+ if (options.series.pie.show) {
141
+ drawOverlay(plot, octx);
142
+ }
143
+ });
144
+
145
+ plot.hooks.draw.push(function(plot, newCtx) {
146
+ var options = plot.getOptions();
147
+ if (options.series.pie.show) {
148
+ draw(plot, newCtx);
149
+ }
150
+ });
151
+
152
+ function processDatapoints(plot, series, datapoints) {
153
+ if (!processed) {
177
154
  processed = true;
178
-
179
155
  canvas = plot.getCanvas();
180
156
  target = $(canvas).parent();
181
157
  options = plot.getOptions();
182
-
183
158
  plot.setData(combine(plot.getData()));
184
159
  }
185
160
  }
186
-
187
- function setupPie()
188
- {
189
- legendWidth = target.children().filter('.legend').children().width();
190
-
191
- // calculate maximum radius and center point
192
- maxRadius = Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;
193
- centerTop = (canvas.height/2)+options.series.pie.offset.top;
194
- centerLeft = (canvas.width/2);
195
-
196
- if (options.series.pie.offset.left=='auto')
197
- if (options.legend.position.match('w'))
198
- centerLeft += legendWidth/2;
199
- else
200
- centerLeft -= legendWidth/2;
201
- else
202
- centerLeft += options.series.pie.offset.left;
203
-
204
- if (centerLeft<maxRadius)
205
- centerLeft = maxRadius;
206
- else if (centerLeft>canvas.width-maxRadius)
207
- centerLeft = canvas.width-maxRadius;
208
- }
209
-
210
- function fixData(data)
211
- {
212
- for (var i = 0; i < data.length; ++i)
213
- {
214
- if (typeof(data[i].data)=='number')
215
- data[i].data = [[1,data[i].data]];
216
- else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')
217
- {
218
- if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')
219
- data[i].label = data[i].data.label; // fix weirdness coming from flot
220
- data[i].data = [[1,0]];
221
-
161
+
162
+ function combine(data) {
163
+
164
+ var total = 0,
165
+ combined = 0,
166
+ numCombined = 0,
167
+ color = options.series.pie.combine.color,
168
+ newdata = [];
169
+
170
+ // Fix up the raw data from Flot, ensuring the data is numeric
171
+
172
+ for (var i = 0; i < data.length; ++i) {
173
+
174
+ var value = data[i].data;
175
+
176
+ // If the data is an array, we'll assume that it's a standard
177
+ // Flot x-y pair, and are concerned only with the second value.
178
+
179
+ // Note how we use the original array, rather than creating a
180
+ // new one; this is more efficient and preserves any extra data
181
+ // that the user may have stored in higher indexes.
182
+
183
+ if ($.isArray(value)) {
184
+ if ($.isNumeric(value[1])) {
185
+ value[1] = +value[1];
186
+ } else {
187
+ value[1] = 0;
188
+ }
189
+ } else if ($.isNumeric(value)) {
190
+ value = [1, +value];
191
+ } else {
192
+ value = [1, 0];
222
193
  }
194
+
195
+ data[i].data = [value];
223
196
  }
224
- return data;
225
- }
226
-
227
- function combine(data)
228
- {
229
- data = fixData(data);
230
- calcTotal(data);
231
- var combined = 0;
232
- var numCombined = 0;
233
- var color = options.series.pie.combine.color;
234
-
235
- var newdata = [];
236
- for (var i = 0; i < data.length; ++i)
237
- {
238
- // make sure its a number
239
- data[i].data[0][1] = parseFloat(data[i].data[0][1]);
240
- if (!data[i].data[0][1])
241
- data[i].data[0][1] = 0;
242
-
243
- if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)
244
- {
245
- combined += data[i].data[0][1];
197
+
198
+ // Sum up all the slices, so we can calculate percentages for each
199
+
200
+ for (var i = 0; i < data.length; ++i) {
201
+ total += data[i].data[0][1];
202
+ }
203
+
204
+ // Count the number of slices with percentages below the combine
205
+ // threshold; if it turns out to be just one, we won't combine.
206
+
207
+ for (var i = 0; i < data.length; ++i) {
208
+ var value = data[i].data[0][1];
209
+ if (value / total <= options.series.pie.combine.threshold) {
210
+ combined += value;
246
211
  numCombined++;
247
- if (!color)
212
+ if (!color) {
248
213
  color = data[i].color;
249
- }
250
- else
251
- {
214
+ }
215
+ }
216
+ }
217
+
218
+ for (var i = 0; i < data.length; ++i) {
219
+ var value = data[i].data[0][1];
220
+ if (numCombined < 2 || value / total > options.series.pie.combine.threshold) {
252
221
  newdata.push({
253
- data: [[1,data[i].data[0][1]]],
254
- color: data[i].color,
222
+ data: [[1, value]],
223
+ color: data[i].color,
255
224
  label: data[i].label,
256
- angle: (data[i].data[0][1]*(Math.PI*2))/total,
257
- percent: (data[i].data[0][1]/total*100)
225
+ angle: value * Math.PI * 2 / total,
226
+ percent: value / (total / 100)
258
227
  });
259
228
  }
260
229
  }
261
- if (numCombined>0)
230
+
231
+ if (numCombined > 1) {
262
232
  newdata.push({
263
- data: [[1,combined]],
264
- color: color,
233
+ data: [[1, combined]],
234
+ color: color,
265
235
  label: options.series.pie.combine.label,
266
- angle: (combined*(Math.PI*2))/total,
267
- percent: (combined/total*100)
236
+ angle: combined * Math.PI * 2 / total,
237
+ percent: combined / (total / 100)
268
238
  });
239
+ }
240
+
269
241
  return newdata;
270
- }
271
-
272
- function draw(plot, newCtx)
273
- {
274
- if (!target) return; // if no series were passed
242
+ }
243
+
244
+ function draw(plot, newCtx) {
245
+
246
+ if (!target) {
247
+ return; // if no series were passed
248
+ }
249
+
250
+ var canvasWidth = plot.getPlaceholder().width(),
251
+ canvasHeight = plot.getPlaceholder().height(),
252
+ legendWidth = target.children().filter(".legend").children().width() || 0;
253
+
275
254
  ctx = newCtx;
276
-
277
- setupPie();
278
- var slices = plot.getData();
279
-
280
- var attempts = 0;
281
- while (redraw && attempts<redrawAttempts)
282
- {
283
- redraw = false;
284
- if (attempts>0)
285
- maxRadius *= shrink;
255
+
256
+ // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!
257
+
258
+ // When combining smaller slices into an 'other' slice, we need to
259
+ // add a new series. Since Flot gives plugins no way to modify the
260
+ // list of series, the pie plugin uses a hack where the first call
261
+ // to processDatapoints results in a call to setData with the new
262
+ // list of series, then subsequent processDatapoints do nothing.
263
+
264
+ // The plugin-global 'processed' flag is used to control this hack;
265
+ // it starts out false, and is set to true after the first call to
266
+ // processDatapoints.
267
+
268
+ // Unfortunately this turns future setData calls into no-ops; they
269
+ // call processDatapoints, the flag is true, and nothing happens.
270
+
271
+ // To fix this we'll set the flag back to false here in draw, when
272
+ // all series have been processed, so the next sequence of calls to
273
+ // processDatapoints once again starts out with a slice-combine.
274
+ // This is really a hack; in 0.9 we need to give plugins a proper
275
+ // way to modify series before any processing begins.
276
+
277
+ processed = false;
278
+
279
+ // calculate maximum radius and center point
280
+
281
+ maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
282
+ centerTop = canvasHeight / 2 + options.series.pie.offset.top;
283
+ centerLeft = canvasWidth / 2;
284
+
285
+ if (options.series.pie.offset.left == "auto") {
286
+ if (options.legend.position.match("w")) {
287
+ centerLeft += legendWidth / 2;
288
+ } else {
289
+ centerLeft -= legendWidth / 2;
290
+ }
291
+ } else {
292
+ centerLeft += options.series.pie.offset.left;
293
+ }
294
+
295
+ if (centerLeft < maxRadius) {
296
+ centerLeft = maxRadius;
297
+ } else if (centerLeft > canvasWidth - maxRadius) {
298
+ centerLeft = canvasWidth - maxRadius;
299
+ }
300
+
301
+ var slices = plot.getData(),
302
+ attempts = 0;
303
+
304
+ // Keep shrinking the pie's radius until drawPie returns true,
305
+ // indicating that all the labels fit, or we try too many times.
306
+
307
+ do {
308
+ if (attempts > 0) {
309
+ maxRadius *= REDRAW_SHRINK;
310
+ }
286
311
  attempts += 1;
287
312
  clear();
288
- if (options.series.pie.tilt<=0.8)
313
+ if (options.series.pie.tilt <= 0.8) {
289
314
  drawShadow();
290
- drawPie();
291
- }
292
- if (attempts >= redrawAttempts) {
315
+ }
316
+ } while (!drawPie() && attempts < REDRAW_ATTEMPTS)
317
+
318
+ if (attempts >= REDRAW_ATTEMPTS) {
293
319
  clear();
294
- target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');
320
+ target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
295
321
  }
296
-
297
- if ( plot.setSeries && plot.insertLegend )
298
- {
322
+
323
+ if (plot.setSeries && plot.insertLegend) {
299
324
  plot.setSeries(slices);
300
325
  plot.insertLegend();
301
326
  }
302
-
327
+
303
328
  // we're actually done at this point, just defining internal functions at this point
304
-
305
- function clear()
306
- {
307
- ctx.clearRect(0,0,canvas.width,canvas.height);
308
- target.children().filter('.pieLabel, .pieLabelBackground').remove();
329
+
330
+ function clear() {
331
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
332
+ target.children().filter(".pieLabel, .pieLabelBackground").remove();
309
333
  }
310
-
311
- function drawShadow()
312
- {
313
- var shadowLeft = 5;
314
- var shadowTop = 15;
334
+
335
+ function drawShadow() {
336
+
337
+ var shadowLeft = options.series.pie.shadow.left;
338
+ var shadowTop = options.series.pie.shadow.top;
315
339
  var edge = 10;
316
- var alpha = 0.02;
317
-
318
- // set radius
319
- if (options.series.pie.radius>1)
320
- var radius = options.series.pie.radius;
321
- else
322
- var radius = maxRadius * options.series.pie.radius;
323
-
324
- if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)
340
+ var alpha = options.series.pie.shadow.alpha;
341
+ var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
342
+
343
+ if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
325
344
  return; // shadow would be outside canvas, so don't draw it
326
-
345
+ }
346
+
327
347
  ctx.save();
328
348
  ctx.translate(shadowLeft,shadowTop);
329
349
  ctx.globalAlpha = alpha;
330
- ctx.fillStyle = '#000';
350
+ ctx.fillStyle = "#000";
331
351
 
332
352
  // center and rotate to starting position
353
+
333
354
  ctx.translate(centerLeft,centerTop);
334
355
  ctx.scale(1, options.series.pie.tilt);
335
-
356
+
336
357
  //radius -= edge;
337
- for (var i=1; i<=edge; i++)
338
- {
358
+
359
+ for (var i = 1; i <= edge; i++) {
339
360
  ctx.beginPath();
340
- ctx.arc(0,0,radius,0,Math.PI*2,false);
361
+ ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
341
362
  ctx.fill();
342
363
  radius -= i;
343
- }
344
-
364
+ }
365
+
345
366
  ctx.restore();
346
367
  }
347
-
348
- function drawPie()
349
- {
350
- startAngle = Math.PI*options.series.pie.startAngle;
351
-
352
- // set radius
353
- if (options.series.pie.radius>1)
354
- var radius = options.series.pie.radius;
355
- else
356
- var radius = maxRadius * options.series.pie.radius;
357
-
368
+
369
+ function drawPie() {
370
+
371
+ var startAngle = Math.PI * options.series.pie.startAngle;
372
+ var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
373
+
358
374
  // center and rotate to starting position
375
+
359
376
  ctx.save();
360
377
  ctx.translate(centerLeft,centerTop);
361
378
  ctx.scale(1, options.series.pie.tilt);
362
379
  //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
363
-
380
+
364
381
  // draw slices
382
+
365
383
  ctx.save();
366
384
  var currentAngle = startAngle;
367
- for (var i = 0; i < slices.length; ++i)
368
- {
385
+ for (var i = 0; i < slices.length; ++i) {
369
386
  slices[i].startAngle = currentAngle;
370
387
  drawSlice(slices[i].angle, slices[i].color, true);
371
388
  }
372
389
  ctx.restore();
373
-
390
+
374
391
  // draw slice outlines
375
- ctx.save();
376
- ctx.lineWidth = options.series.pie.stroke.width;
377
- currentAngle = startAngle;
378
- for (var i = 0; i < slices.length; ++i)
379
- drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
380
- ctx.restore();
381
-
392
+
393
+ if (options.series.pie.stroke.width > 0) {
394
+ ctx.save();
395
+ ctx.lineWidth = options.series.pie.stroke.width;
396
+ currentAngle = startAngle;
397
+ for (var i = 0; i < slices.length; ++i) {
398
+ drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
399
+ }
400
+ ctx.restore();
401
+ }
402
+
382
403
  // draw donut hole
404
+
383
405
  drawDonutHole(ctx);
384
-
385
- // draw labels
386
- if (options.series.pie.label.show)
387
- drawLabels();
388
-
389
- // restore to original state
406
+
390
407
  ctx.restore();
391
-
392
- function drawSlice(angle, color, fill)
393
- {
394
- if (angle<=0)
408
+
409
+ // Draw the labels, returning true if they fit within the plot
410
+
411
+ if (options.series.pie.label.show) {
412
+ return drawLabels();
413
+ } else return true;
414
+
415
+ function drawSlice(angle, color, fill) {
416
+
417
+ if (angle <= 0 || isNaN(angle)) {
395
418
  return;
396
-
397
- if (fill)
419
+ }
420
+
421
+ if (fill) {
398
422
  ctx.fillStyle = color;
399
- else
400
- {
423
+ } else {
401
424
  ctx.strokeStyle = color;
402
- ctx.lineJoin = 'round';
425
+ ctx.lineJoin = "round";
403
426
  }
404
-
427
+
405
428
  ctx.beginPath();
406
- if (Math.abs(angle - Math.PI*2) > 0.000000001)
407
- ctx.moveTo(0,0); // Center of the pie
408
- else if ($.browser.msie)
409
- angle -= 0.0001;
410
- //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera
411
- ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);
429
+ if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
430
+ ctx.moveTo(0, 0); // Center of the pie
431
+ }
432
+
433
+ //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
434
+ ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
435
+ ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
412
436
  ctx.closePath();
413
437
  //ctx.rotate(angle); // This doesn't work properly in Opera
414
438
  currentAngle += angle;
415
-
416
- if (fill)
439
+
440
+ if (fill) {
417
441
  ctx.fill();
418
- else
442
+ } else {
419
443
  ctx.stroke();
444
+ }
420
445
  }
421
-
422
- function drawLabels()
423
- {
446
+
447
+ function drawLabels() {
448
+
424
449
  var currentAngle = startAngle;
425
-
426
- // set radius
427
- if (options.series.pie.label.radius>1)
428
- var radius = options.series.pie.label.radius;
429
- else
430
- var radius = maxRadius * options.series.pie.label.radius;
431
-
432
- for (var i = 0; i < slices.length; ++i)
433
- {
434
- if (slices[i].percent >= options.series.pie.label.threshold*100)
435
- drawLabel(slices[i], currentAngle, i);
450
+ var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
451
+
452
+ for (var i = 0; i < slices.length; ++i) {
453
+ if (slices[i].percent >= options.series.pie.label.threshold * 100) {
454
+ if (!drawLabel(slices[i], currentAngle, i)) {
455
+ return false;
456
+ }
457
+ }
436
458
  currentAngle += slices[i].angle;
437
459
  }
438
-
439
- function drawLabel(slice, startAngle, index)
440
- {
441
- if (slice.data[0][1]==0)
442
- return;
443
-
460
+
461
+ return true;
462
+
463
+ function drawLabel(slice, startAngle, index) {
464
+
465
+ if (slice.data[0][1] == 0) {
466
+ return true;
467
+ }
468
+
444
469
  // format label text
470
+
445
471
  var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
446
- if (lf)
472
+
473
+ if (lf) {
447
474
  text = lf(slice.label, slice);
448
- else
475
+ } else {
449
476
  text = slice.label;
450
- if (plf)
477
+ }
478
+
479
+ if (plf) {
451
480
  text = plf(text, slice);
452
-
453
- var halfAngle = ((startAngle+slice.angle) + startAngle)/2;
481
+ }
482
+
483
+ var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
454
484
  var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
455
485
  var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
456
-
457
- var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";
486
+
487
+ var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
458
488
  target.append(html);
459
- var label = target.children('#pieLabel'+index);
460
- var labelTop = (y - label.height()/2);
461
- var labelLeft = (x - label.width()/2);
462
- label.css('top', labelTop);
463
- label.css('left', labelLeft);
464
-
489
+
490
+ var label = target.children("#pieLabel" + index);
491
+ var labelTop = (y - label.height() / 2);
492
+ var labelLeft = (x - label.width() / 2);
493
+
494
+ label.css("top", labelTop);
495
+ label.css("left", labelLeft);
496
+
465
497
  // check to make sure that the label is not outside the canvas
466
- if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)
467
- redraw = true;
468
-
498
+
499
+ if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
500
+ return false;
501
+ }
502
+
469
503
  if (options.series.pie.label.background.opacity != 0) {
504
+
470
505
  // put in the transparent background separately to avoid blended labels and label boxes
506
+
471
507
  var c = options.series.pie.label.background.color;
508
+
472
509
  if (c == null) {
473
510
  c = slice.color;
474
511
  }
475
- var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';
476
- $('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);
512
+
513
+ var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
514
+ $("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
515
+ .css("opacity", options.series.pie.label.background.opacity)
516
+ .insertBefore(label);
477
517
  }
518
+
519
+ return true;
478
520
  } // end individual label function
479
521
  } // end drawLabels function
480
522
  } // end drawPie function
481
523
  } // end draw function
482
-
483
- // Placed here because it needs to be accessed from multiple locations
484
- function drawDonutHole(layer)
485
- {
486
- // draw donut hole
487
- if(options.series.pie.innerRadius > 0)
488
- {
524
+
525
+ // Placed here because it needs to be accessed from multiple locations
526
+
527
+ function drawDonutHole(layer) {
528
+ if (options.series.pie.innerRadius > 0) {
529
+
489
530
  // subtract the center
531
+
490
532
  layer.save();
491
- innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
492
- layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color
533
+ var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
534
+ layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
493
535
  layer.beginPath();
494
536
  layer.fillStyle = options.series.pie.stroke.color;
495
- layer.arc(0,0,innerRadius,0,Math.PI*2,false);
537
+ layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
496
538
  layer.fill();
497
539
  layer.closePath();
498
540
  layer.restore();
499
-
541
+
500
542
  // add inner stroke
543
+
501
544
  layer.save();
502
545
  layer.beginPath();
503
546
  layer.strokeStyle = options.series.pie.stroke.color;
504
- layer.arc(0,0,innerRadius,0,Math.PI*2,false);
547
+ layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
505
548
  layer.stroke();
506
549
  layer.closePath();
507
550
  layer.restore();
551
+
508
552
  // TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
509
553
  }
510
554
  }
511
-
555
+
512
556
  //-- Additional Interactive related functions --
513
-
514
- function isPointInPoly(poly, pt)
515
- {
557
+
558
+ function isPointInPoly(poly, pt) {
516
559
  for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
517
560
  ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
518
561
  && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
519
562
  && (c = !c);
520
563
  return c;
521
564
  }
522
-
523
- function findNearbySlice(mouseX, mouseY)
524
- {
565
+
566
+ function findNearbySlice(mouseX, mouseY) {
567
+
525
568
  var slices = plot.getData(),
526
569
  options = plot.getOptions(),
527
- radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
528
-
529
- for (var i = 0; i < slices.length; ++i)
530
- {
531
- var s = slices[i];
532
-
533
- if(s.pie.show)
534
- {
570
+ radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius,
571
+ x, y;
572
+
573
+ for (var i = 0; i < slices.length; ++i) {
574
+
575
+ var s = slices[i];
576
+
577
+ if (s.pie.show) {
578
+
535
579
  ctx.save();
536
580
  ctx.beginPath();
537
- ctx.moveTo(0,0); // Center of the pie
581
+ ctx.moveTo(0, 0); // Center of the pie
538
582
  //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
539
- ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);
583
+ ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
584
+ ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
540
585
  ctx.closePath();
541
- x = mouseX-centerLeft;
542
- y = mouseY-centerTop;
543
- if(ctx.isPointInPath)
544
- {
545
- if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))
546
- {
547
- //alert('found slice!');
586
+ x = mouseX - centerLeft;
587
+ y = mouseY - centerTop;
588
+
589
+ if (ctx.isPointInPath) {
590
+ if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
548
591
  ctx.restore();
549
- return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
592
+ return {
593
+ datapoint: [s.percent, s.data],
594
+ dataIndex: 0,
595
+ series: s,
596
+ seriesIndex: i
597
+ };
550
598
  }
551
- }
552
- else
553
- {
554
- // excanvas for IE doesn;t support isPointInPath, this is a workaround.
555
- p1X = (radius * Math.cos(s.startAngle));
556
- p1Y = (radius * Math.sin(s.startAngle));
557
- p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));
558
- p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));
559
- p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));
560
- p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));
561
- p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));
562
- p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));
563
- p5X = (radius * Math.cos(s.startAngle+s.angle));
564
- p5Y = (radius * Math.sin(s.startAngle+s.angle));
565
- arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];
566
- arrPoint = [x,y];
599
+ } else {
600
+
601
+ // excanvas for IE doesn;t support isPointInPath, this is a workaround.
602
+
603
+ var p1X = radius * Math.cos(s.startAngle),
604
+ p1Y = radius * Math.sin(s.startAngle),
605
+ p2X = radius * Math.cos(s.startAngle + s.angle / 4),
606
+ p2Y = radius * Math.sin(s.startAngle + s.angle / 4),
607
+ p3X = radius * Math.cos(s.startAngle + s.angle / 2),
608
+ p3Y = radius * Math.sin(s.startAngle + s.angle / 2),
609
+ p4X = radius * Math.cos(s.startAngle + s.angle / 1.5),
610
+ p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5),
611
+ p5X = radius * Math.cos(s.startAngle + s.angle),
612
+ p5Y = radius * Math.sin(s.startAngle + s.angle),
613
+ arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]],
614
+ arrPoint = [x, y];
615
+
567
616
  // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
568
- if(isPointInPoly(arrPoly, arrPoint))
569
- {
617
+
618
+ if (isPointInPoly(arrPoly, arrPoint)) {
570
619
  ctx.restore();
571
- return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
572
- }
620
+ return {
621
+ datapoint: [s.percent, s.data],
622
+ dataIndex: 0,
623
+ series: s,
624
+ seriesIndex: i
625
+ };
626
+ }
573
627
  }
628
+
574
629
  ctx.restore();
575
630
  }
576
631
  }
577
-
632
+
578
633
  return null;
579
634
  }
580
635
 
581
- function onMouseMove(e)
582
- {
583
- triggerClickHoverEvent('plothover', e);
636
+ function onMouseMove(e) {
637
+ triggerClickHoverEvent("plothover", e);
638
+ }
639
+
640
+ function onClick(e) {
641
+ triggerClickHoverEvent("plotclick", e);
584
642
  }
585
-
586
- function onClick(e)
587
- {
588
- triggerClickHoverEvent('plotclick', e);
589
- }
590
643
 
591
644
  // trigger click or hover event (they send the same parameters so we share their code)
592
- function triggerClickHoverEvent(eventname, e)
593
- {
594
- var offset = plot.offset(),
595
- canvasX = parseInt(e.pageX - offset.left),
596
- canvasY = parseInt(e.pageY - offset.top),
597
- item = findNearbySlice(canvasX, canvasY);
598
-
599
- if (options.grid.autoHighlight)
600
- {
645
+
646
+ function triggerClickHoverEvent(eventname, e) {
647
+
648
+ var offset = plot.offset();
649
+ var canvasX = parseInt(e.pageX - offset.left);
650
+ var canvasY = parseInt(e.pageY - offset.top);
651
+ var item = findNearbySlice(canvasX, canvasY);
652
+
653
+ if (options.grid.autoHighlight) {
654
+
601
655
  // clear auto-highlights
602
- for (var i = 0; i < highlights.length; ++i)
603
- {
656
+
657
+ for (var i = 0; i < highlights.length; ++i) {
604
658
  var h = highlights[i];
605
- if (h.auto == eventname && !(item && h.series == item.series))
659
+ if (h.auto == eventname && !(item && h.series == item.series)) {
606
660
  unhighlight(h.series);
661
+ }
607
662
  }
608
663
  }
609
-
664
+
610
665
  // highlight the slice
611
- if (item)
612
- highlight(item.series, eventname);
613
-
666
+
667
+ if (item) {
668
+ highlight(item.series, eventname);
669
+ }
670
+
614
671
  // trigger any hover bind events
672
+
615
673
  var pos = { pageX: e.pageX, pageY: e.pageY };
616
- target.trigger(eventname, [ pos, item ]);
674
+ target.trigger(eventname, [pos, item]);
617
675
  }
618
676
 
619
- function highlight(s, auto)
620
- {
621
- if (typeof s == "number")
622
- s = series[s];
677
+ function highlight(s, auto) {
678
+ //if (typeof s == "number") {
679
+ // s = series[s];
680
+ //}
623
681
 
624
682
  var i = indexOfHighlight(s);
625
- if (i == -1)
626
- {
683
+
684
+ if (i == -1) {
627
685
  highlights.push({ series: s, auto: auto });
628
686
  plot.triggerRedrawOverlay();
629
- }
630
- else if (!auto)
687
+ } else if (!auto) {
631
688
  highlights[i].auto = false;
689
+ }
632
690
  }
633
691
 
634
- function unhighlight(s)
635
- {
636
- if (s == null)
637
- {
692
+ function unhighlight(s) {
693
+ if (s == null) {
638
694
  highlights = [];
639
695
  plot.triggerRedrawOverlay();
640
696
  }
641
-
642
- if (typeof s == "number")
643
- s = series[s];
697
+
698
+ //if (typeof s == "number") {
699
+ // s = series[s];
700
+ //}
644
701
 
645
702
  var i = indexOfHighlight(s);
646
- if (i != -1)
647
- {
703
+
704
+ if (i != -1) {
648
705
  highlights.splice(i, 1);
649
706
  plot.triggerRedrawOverlay();
650
707
  }
651
708
  }
652
709
 
653
- function indexOfHighlight(s)
654
- {
655
- for (var i = 0; i < highlights.length; ++i)
656
- {
710
+ function indexOfHighlight(s) {
711
+ for (var i = 0; i < highlights.length; ++i) {
657
712
  var h = highlights[i];
658
713
  if (h.series == s)
659
714
  return i;
@@ -661,65 +716,71 @@ More detail and specific examples can be found in the included HTML file.
661
716
  return -1;
662
717
  }
663
718
 
664
- function drawOverlay(plot, octx)
665
- {
666
- //alert(options.series.pie.radius);
719
+ function drawOverlay(plot, octx) {
720
+
667
721
  var options = plot.getOptions();
668
- //alert(options.series.pie.radius);
669
-
722
+
670
723
  var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
671
724
 
672
725
  octx.save();
673
726
  octx.translate(centerLeft, centerTop);
674
727
  octx.scale(1, options.series.pie.tilt);
675
-
676
- for (i = 0; i < highlights.length; ++i)
728
+
729
+ for (var i = 0; i < highlights.length; ++i) {
677
730
  drawHighlight(highlights[i].series);
678
-
731
+ }
732
+
679
733
  drawDonutHole(octx);
680
734
 
681
735
  octx.restore();
682
736
 
683
- function drawHighlight(series)
684
- {
685
- if (series.angle < 0) return;
686
-
737
+ function drawHighlight(series) {
738
+
739
+ if (series.angle <= 0 || isNaN(series.angle)) {
740
+ return;
741
+ }
742
+
687
743
  //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
688
- octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor
689
-
744
+ octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
690
745
  octx.beginPath();
691
- if (Math.abs(series.angle - Math.PI*2) > 0.000000001)
692
- octx.moveTo(0,0); // Center of the pie
693
- octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);
746
+ if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
747
+ octx.moveTo(0, 0); // Center of the pie
748
+ }
749
+ octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
750
+ octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
694
751
  octx.closePath();
695
752
  octx.fill();
696
753
  }
697
-
698
- }
699
-
754
+ }
700
755
  } // end init (plugin body)
701
-
756
+
702
757
  // define pie specific options and their default values
758
+
703
759
  var options = {
704
760
  series: {
705
761
  pie: {
706
762
  show: false,
707
- radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
708
- innerRadius:0, /* for donut */
763
+ radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
764
+ innerRadius: 0, /* for donut */
709
765
  startAngle: 3/2,
710
766
  tilt: 1,
767
+ shadow: {
768
+ left: 5, // shadow left offset
769
+ top: 15, // shadow top offset
770
+ alpha: 0.02 // shadow alpha
771
+ },
711
772
  offset: {
712
773
  top: 0,
713
- left: 'auto'
774
+ left: "auto"
714
775
  },
715
776
  stroke: {
716
- color: '#FFF',
777
+ color: "#fff",
717
778
  width: 1
718
779
  },
719
780
  label: {
720
- show: 'auto',
721
- formatter: function(label, slice){
722
- return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';
781
+ show: "auto",
782
+ formatter: function(label, slice) {
783
+ return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
723
784
  }, // formatter function
724
785
  radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
725
786
  background: {
@@ -731,20 +792,21 @@ More detail and specific examples can be found in the included HTML file.
731
792
  combine: {
732
793
  threshold: -1, // percentage at which to combine little slices into one larger slice
733
794
  color: null, // color to give the new slice (auto-generated if null)
734
- label: 'Other' // label to give the new slice
795
+ label: "Other" // label to give the new slice
735
796
  },
736
797
  highlight: {
737
- //color: '#FFF', // will add this functionality once parseColor is available
798
+ //color: "#fff", // will add this functionality once parseColor is available
738
799
  opacity: 0.5
739
800
  }
740
801
  }
741
802
  }
742
803
  };
743
-
804
+
744
805
  $.plot.plugins.push({
745
806
  init: init,
746
807
  options: options,
747
808
  name: "pie",
748
- version: "1.0"
809
+ version: "1.1"
749
810
  });
811
+
750
812
  })(jQuery);