flot-rails 0.0.2 → 0.0.3
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.
- data/README.md +2 -2
- data/lib/flot/rails/version.rb +2 -2
- data/vendor/assets/javascripts/excanvas.js +1428 -1427
- data/vendor/assets/javascripts/excanvas.min.js +1 -1
- data/vendor/assets/javascripts/jquery.colorhelpers.js +179 -179
- data/vendor/assets/javascripts/jquery.colorhelpers.min.js +21 -1
- data/vendor/assets/javascripts/jquery.flot.canvas.js +317 -0
- data/vendor/assets/javascripts/jquery.flot.canvas.min.js +28 -0
- data/vendor/assets/javascripts/jquery.flot.categories.js +190 -0
- data/vendor/assets/javascripts/jquery.flot.categories.min.js +44 -0
- data/vendor/assets/javascripts/jquery.flot.crosshair.js +176 -167
- data/vendor/assets/javascripts/jquery.flot.crosshair.min.js +59 -1
- data/vendor/assets/javascripts/jquery.flot.errorbars.js +353 -0
- data/vendor/assets/javascripts/jquery.flot.errorbars.min.js +63 -0
- data/vendor/assets/javascripts/jquery.flot.fillbetween.js +226 -183
- data/vendor/assets/javascripts/jquery.flot.fillbetween.min.js +30 -1
- data/vendor/assets/javascripts/jquery.flot.image.js +241 -238
- data/vendor/assets/javascripts/jquery.flot.image.min.js +53 -1
- data/vendor/assets/javascripts/jquery.flot.js +2980 -2599
- data/vendor/assets/javascripts/jquery.flot.min.js +26 -4
- data/vendor/assets/javascripts/jquery.flot.navigate.js +345 -336
- data/vendor/assets/javascripts/jquery.flot.navigate.min.js +96 -1
- data/vendor/assets/javascripts/jquery.flot.pie.js +561 -499
- data/vendor/assets/javascripts/jquery.flot.pie.min.js +56 -1
- data/vendor/assets/javascripts/jquery.flot.resize.js +60 -60
- data/vendor/assets/javascripts/jquery.flot.resize.min.js +21 -1
- data/vendor/assets/javascripts/jquery.flot.selection.js +360 -344
- data/vendor/assets/javascripts/jquery.flot.selection.min.js +79 -1
- data/vendor/assets/javascripts/jquery.flot.stack.js +188 -184
- data/vendor/assets/javascripts/jquery.flot.stack.min.js +36 -1
- data/vendor/assets/javascripts/jquery.flot.symbol.js +71 -70
- data/vendor/assets/javascripts/jquery.flot.symbol.min.js +14 -1
- data/vendor/assets/javascripts/jquery.flot.threshold.js +142 -103
- data/vendor/assets/javascripts/jquery.flot.threshold.min.js +43 -1
- data/vendor/assets/javascripts/jquery.flot.time.js +424 -0
- data/vendor/assets/javascripts/jquery.flot.time.min.js +9 -0
- metadata +25 -7
@@ -1 +1,96 @@
|
|
1
|
-
|
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
*
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
var
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
83
|
-
plot.hooks.
|
84
|
-
|
85
|
-
|
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
|
-
|
95
|
-
|
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
|
-
|
102
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
123
|
-
function bindEvents(plot, eventHolder)
|
124
|
-
{
|
117
|
+
});
|
118
|
+
|
119
|
+
plot.hooks.bindEvents.push(function(plot, eventHolder) {
|
125
120
|
var options = plot.getOptions();
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
174
|
-
|
175
|
-
if (
|
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
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if (
|
215
|
-
|
216
|
-
else
|
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
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|
-
|
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,
|
254
|
-
color: data[i].color,
|
222
|
+
data: [[1, value]],
|
223
|
+
color: data[i].color,
|
255
224
|
label: data[i].label,
|
256
|
-
angle:
|
257
|
-
percent: (
|
225
|
+
angle: value * Math.PI * 2 / total,
|
226
|
+
percent: value / (total / 100)
|
258
227
|
});
|
259
228
|
}
|
260
229
|
}
|
261
|
-
|
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:
|
267
|
-
percent:
|
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)
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
|
291
|
-
}
|
292
|
-
|
315
|
+
}
|
316
|
+
} while (!drawPie() && attempts < REDRAW_ATTEMPTS)
|
317
|
+
|
318
|
+
if (attempts >= REDRAW_ATTEMPTS) {
|
293
319
|
clear();
|
294
|
-
target.prepend(
|
320
|
+
target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
|
295
321
|
}
|
296
|
-
|
297
|
-
if (
|
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
|
-
|
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 =
|
314
|
-
var shadowTop =
|
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 =
|
317
|
-
|
318
|
-
|
319
|
-
if (options.series.pie.radius
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
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
|
-
|
393
|
-
|
394
|
-
|
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
|
-
|
419
|
+
}
|
420
|
+
|
421
|
+
if (fill) {
|
398
422
|
ctx.fillStyle = color;
|
399
|
-
else
|
400
|
-
{
|
423
|
+
} else {
|
401
424
|
ctx.strokeStyle = color;
|
402
|
-
ctx.lineJoin =
|
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
|
-
|
409
|
-
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
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
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
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
|
-
|
472
|
+
|
473
|
+
if (lf) {
|
447
474
|
text = lf(slice.label, slice);
|
448
|
-
else
|
475
|
+
} else {
|
449
476
|
text = slice.label;
|
450
|
-
|
477
|
+
}
|
478
|
+
|
479
|
+
if (plf) {
|
451
480
|
text = plf(text, slice);
|
452
|
-
|
453
|
-
|
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 =
|
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
|
-
|
460
|
-
var
|
461
|
-
var
|
462
|
-
label.
|
463
|
-
|
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
|
-
|
467
|
-
|
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
|
-
|
476
|
-
|
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
|
-
|
485
|
-
{
|
486
|
-
|
487
|
-
|
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 =
|
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
|
-
|
530
|
-
{
|
531
|
-
|
532
|
-
|
533
|
-
|
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
|
-
|
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 {
|
592
|
+
return {
|
593
|
+
datapoint: [s.percent, s.data],
|
594
|
+
dataIndex: 0,
|
595
|
+
series: s,
|
596
|
+
seriesIndex: i
|
597
|
+
};
|
550
598
|
}
|
551
|
-
}
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
p1X =
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
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
|
-
|
569
|
-
{
|
617
|
+
|
618
|
+
if (isPointInPoly(arrPoly, arrPoint)) {
|
570
619
|
ctx.restore();
|
571
|
-
return {
|
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
|
-
|
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
|
-
|
593
|
-
{
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
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
|
-
|
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
|
-
|
612
|
-
|
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, [
|
674
|
+
target.trigger(eventname, [pos, item]);
|
617
675
|
}
|
618
676
|
|
619
|
-
function highlight(s, auto)
|
620
|
-
|
621
|
-
|
622
|
-
|
677
|
+
function highlight(s, auto) {
|
678
|
+
//if (typeof s == "number") {
|
679
|
+
// s = series[s];
|
680
|
+
//}
|
623
681
|
|
624
682
|
var i = indexOfHighlight(s);
|
625
|
-
|
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
|
-
|
697
|
+
|
698
|
+
//if (typeof s == "number") {
|
699
|
+
// s = series[s];
|
700
|
+
//}
|
644
701
|
|
645
702
|
var i = indexOfHighlight(s);
|
646
|
-
|
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
|
-
|
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
|
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
|
-
|
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:
|
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:
|
774
|
+
left: "auto"
|
714
775
|
},
|
715
776
|
stroke: {
|
716
|
-
color:
|
777
|
+
color: "#fff",
|
717
778
|
width: 1
|
718
779
|
},
|
719
780
|
label: {
|
720
|
-
show:
|
721
|
-
formatter: function(label, slice){
|
722
|
-
return
|
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:
|
795
|
+
label: "Other" // label to give the new slice
|
735
796
|
},
|
736
797
|
highlight: {
|
737
|
-
//color:
|
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.
|
809
|
+
version: "1.1"
|
749
810
|
});
|
811
|
+
|
750
812
|
})(jQuery);
|