outfielding-jqplot-rails 1.0.8 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +8 -4
  4. data/changes-jqplot.txt +48 -37
  5. data/copyright-jqplot.txt +17 -17
  6. data/lib/outfielding-jqplot-rails/version.rb +1 -1
  7. data/vendor/assets/javascripts/excanvas.js +1438 -1438
  8. data/vendor/assets/javascripts/jqplot-plugins/jqplot.BezierCurveRenderer.js +313 -313
  9. data/vendor/assets/javascripts/jqplot-plugins/jqplot.barRenderer.js +801 -801
  10. data/vendor/assets/javascripts/jqplot-plugins/jqplot.blockRenderer.js +234 -234
  11. data/vendor/assets/javascripts/jqplot-plugins/jqplot.bubbleRenderer.js +758 -758
  12. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasAxisLabelRenderer.js +202 -202
  13. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasAxisTickRenderer.js +252 -252
  14. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasOverlay.js +1020 -1020
  15. data/vendor/assets/javascripts/jqplot-plugins/jqplot.canvasTextRenderer.js +448 -448
  16. data/vendor/assets/javascripts/jqplot-plugins/jqplot.categoryAxisRenderer.js +679 -679
  17. data/vendor/assets/javascripts/jqplot-plugins/jqplot.ciParser.js +115 -115
  18. data/vendor/assets/javascripts/jqplot-plugins/jqplot.cursor.js +1108 -1108
  19. data/vendor/assets/javascripts/jqplot-plugins/jqplot.dateAxisRenderer.js +741 -741
  20. data/vendor/assets/javascripts/jqplot-plugins/jqplot.donutRenderer.js +816 -805
  21. data/vendor/assets/javascripts/jqplot-plugins/jqplot.dragable.js +224 -224
  22. data/vendor/assets/javascripts/jqplot-plugins/jqplot.enhancedLegendRenderer.js +305 -305
  23. data/vendor/assets/javascripts/jqplot-plugins/jqplot.enhancedPieLegendRenderer.js +261 -0
  24. data/vendor/assets/javascripts/jqplot-plugins/jqplot.funnelRenderer.js +942 -942
  25. data/vendor/assets/javascripts/jqplot-plugins/jqplot.highlighter.js +464 -464
  26. data/vendor/assets/javascripts/jqplot-plugins/jqplot.json2.js +475 -475
  27. data/vendor/assets/javascripts/jqplot-plugins/jqplot.logAxisRenderer.js +533 -533
  28. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mekkoAxisRenderer.js +611 -611
  29. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mekkoRenderer.js +437 -437
  30. data/vendor/assets/javascripts/jqplot-plugins/jqplot.meterGaugeRenderer.js +1029 -1029
  31. data/vendor/assets/javascripts/jqplot-plugins/jqplot.mobile.js +2 -2
  32. data/vendor/assets/javascripts/jqplot-plugins/jqplot.ohlcRenderer.js +373 -373
  33. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pieRenderer.js +945 -903
  34. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pointLabels.js +379 -377
  35. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidAxisRenderer.js +728 -728
  36. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidGridRenderer.js +428 -428
  37. data/vendor/assets/javascripts/jqplot-plugins/jqplot.pyramidRenderer.js +513 -513
  38. data/vendor/assets/javascripts/jqplot-plugins/jqplot.trendline.js +222 -222
  39. data/vendor/assets/javascripts/jquery.jqplot.js +11477 -11411
  40. data/vendor/assets/stylesheets/jquery.jqplot.css +259 -259
  41. metadata +9 -10
@@ -1,805 +1,816 @@
1
- /**
2
- * jqPlot
3
- * Pure JavaScript plotting plugin using jQuery
4
- *
5
- * Version: 1.0.8
6
- * Revision: 1250
7
- *
8
- * Copyright (c) 2009-2013 Chris Leonello
9
- * jqPlot is currently available for use in all personal or commercial projects
10
- * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11
- * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12
- * choose the license that best suits your project and use it accordingly.
13
- *
14
- * Although not required, the author would appreciate an email letting him
15
- * know of any substantial use of jqPlot. You can reach the author at:
16
- * chris at jqplot dot com or see http://www.jqplot.com/info.php .
17
- *
18
- * If you are feeling kind and generous, consider supporting the project by
19
- * making a donation at: http://www.jqplot.com/donate.php .
20
- *
21
- * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22
- *
23
- * version 2007.04.27
24
- * author Ash Searle
25
- * http://hexmen.com/blog/2007/03/printf-sprintf/
26
- * http://hexmen.com/js/sprintf.js
27
- * The author (Ash Searle) has placed this code in the public domain:
28
- * "This code is unrestricted: you are free to use it however you like."
29
- *
30
- */
31
- (function($) {
32
- /**
33
- * Class: $.jqplot.DonutRenderer
34
- * Plugin renderer to draw a donut chart.
35
- * x values, if present, will be used as slice labels.
36
- * y values give slice size.
37
- *
38
- * To use this renderer, you need to include the
39
- * donut renderer plugin, for example:
40
- *
41
- * > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script>
42
- *
43
- * Properties described here are passed into the $.jqplot function
44
- * as options on the series renderer. For example:
45
- *
46
- * > plot2 = $.jqplot('chart2', [s1, s2], {
47
- * > seriesDefaults: {
48
- * > renderer:$.jqplot.DonutRenderer,
49
- * > rendererOptions:{
50
- * > sliceMargin: 2,
51
- * > innerDiameter: 110,
52
- * > startAngle: -90
53
- * > }
54
- * > }
55
- * > });
56
- *
57
- * A donut plot will trigger events on the plot target
58
- * according to user interaction. All events return the event object,
59
- * the series index, the point (slice) index, and the point data for
60
- * the appropriate slice.
61
- *
62
- * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
63
- * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
64
- * if highlighting is enabled.
65
- * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
66
- * a highlighted slice.
67
- * 'jqplotDataClick' - triggered when the user clicks on a slice.
68
- * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
69
- * the "captureRightClick" option is set to true on the plot.
70
- */
71
- $.jqplot.DonutRenderer = function(){
72
- $.jqplot.LineRenderer.call(this);
73
- };
74
-
75
- $.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer();
76
- $.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer;
77
-
78
- // called with scope of a series
79
- $.jqplot.DonutRenderer.prototype.init = function(options, plot) {
80
- // Group: Properties
81
- //
82
- // prop: diameter
83
- // Outer diameter of the donut, auto computed by default
84
- this.diameter = null;
85
- // prop: innerDiameter
86
- // Inner diameter of the donut, auto calculated by default.
87
- // If specified will override thickness value.
88
- this.innerDiameter = null;
89
- // prop: thickness
90
- // thickness of the donut, auto computed by default
91
- // Overridden by if innerDiameter is specified.
92
- this.thickness = null;
93
- // prop: padding
94
- // padding between the donut and plot edges, legend, etc.
95
- this.padding = 20;
96
- // prop: sliceMargin
97
- // angular spacing between donut slices in degrees.
98
- this.sliceMargin = 0;
99
- // prop: ringMargin
100
- // pixel distance between rings, or multiple series in a donut plot.
101
- // null will compute ringMargin based on sliceMargin.
102
- this.ringMargin = null;
103
- // prop: fill
104
- // true or false, whether to fil the slices.
105
- this.fill = true;
106
- // prop: shadowOffset
107
- // offset of the shadow from the slice and offset of
108
- // each succesive stroke of the shadow from the last.
109
- this.shadowOffset = 2;
110
- // prop: shadowAlpha
111
- // transparency of the shadow (0 = transparent, 1 = opaque)
112
- this.shadowAlpha = 0.07;
113
- // prop: shadowDepth
114
- // number of strokes to apply to the shadow,
115
- // each stroke offset shadowOffset from the last.
116
- this.shadowDepth = 5;
117
- // prop: highlightMouseOver
118
- // True to highlight slice when moused over.
119
- // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
120
- this.highlightMouseOver = true;
121
- // prop: highlightMouseDown
122
- // True to highlight when a mouse button is pressed over a slice.
123
- // This will be disabled if highlightMouseOver is true.
124
- this.highlightMouseDown = false;
125
- // prop: highlightColors
126
- // an array of colors to use when highlighting a slice.
127
- this.highlightColors = [];
128
- // prop: dataLabels
129
- // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
130
- // Defaults to percentage of each pie slice.
131
- this.dataLabels = 'percent';
132
- // prop: showDataLabels
133
- // true to show data labels on slices.
134
- this.showDataLabels = false;
135
- // prop: dataLabelFormatString
136
- // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
137
- this.dataLabelFormatString = null;
138
- // prop: dataLabelThreshold
139
- // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
140
- // This applies to all label types, not just to percentage labels.
141
- this.dataLabelThreshold = 3;
142
- // prop: dataLabelPositionFactor
143
- // A Multiplier (0-1) of the pie radius which controls position of label on slice.
144
- // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
145
- this.dataLabelPositionFactor = 0.4;
146
- // prop: dataLabelNudge
147
- // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
148
- this.dataLabelNudge = 0;
149
- // prop: startAngle
150
- // Angle to start drawing donut in degrees.
151
- // According to orientation of canvas coordinate system:
152
- // 0 = on the positive x axis
153
- // -90 = on the positive y axis.
154
- // 90 = on the negaive y axis.
155
- // 180 or - 180 = on the negative x axis.
156
- this.startAngle = 0;
157
- this.tickRenderer = $.jqplot.DonutTickRenderer;
158
- // Used as check for conditions where donut shouldn't be drawn.
159
- this._drawData = true;
160
- this._type = 'donut';
161
-
162
- // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
163
- if (options.highlightMouseDown && options.highlightMouseOver == null) {
164
- options.highlightMouseOver = false;
165
- }
166
-
167
- $.extend(true, this, options);
168
- if (this.diameter != null) {
169
- this.diameter = this.diameter - this.sliceMargin;
170
- }
171
- this._diameter = null;
172
- this._innerDiameter = null;
173
- this._radius = null;
174
- this._innerRadius = null;
175
- this._thickness = null;
176
- // references to the previous series in the plot to properly calculate diameters
177
- // and thicknesses of nested rings.
178
- this._previousSeries = [];
179
- this._numberSeries = 1;
180
- // array of [start,end] angles arrays, one for each slice. In radians.
181
- this._sliceAngles = [];
182
- // index of the currenty highlighted point, if any
183
- this._highlightedPoint = null;
184
-
185
- // set highlight colors if none provided
186
- if (this.highlightColors.length == 0) {
187
- for (var i=0; i<this.seriesColors.length; i++){
188
- var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
189
- var newrgb = [rgba[0], rgba[1], rgba[2]];
190
- var sum = newrgb[0] + newrgb[1] + newrgb[2];
191
- for (var j=0; j<3; j++) {
192
- // when darkening, lowest color component can be is 60.
193
- newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
194
- newrgb[j] = parseInt(newrgb[j], 10);
195
- }
196
- this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
197
- }
198
- }
199
-
200
- plot.postParseOptionsHooks.addOnce(postParseOptions);
201
- plot.postInitHooks.addOnce(postInit);
202
- plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
203
- plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
204
- plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
205
- plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
206
- plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
207
- plot.postDrawHooks.addOnce(postPlotDraw);
208
-
209
-
210
- };
211
-
212
- $.jqplot.DonutRenderer.prototype.setGridData = function(plot) {
213
- // set gridData property. This will hold angle in radians of each data point.
214
- var stack = [];
215
- var td = [];
216
- var sa = this.startAngle/180*Math.PI;
217
- var tot = 0;
218
- // don't know if we have any valid data yet, so set plot to not draw.
219
- this._drawData = false;
220
- for (var i=0; i<this.data.length; i++){
221
- if (this.data[i][1] != 0) {
222
- // we have data, O.K. to draw.
223
- this._drawData = true;
224
- }
225
- stack.push(this.data[i][1]);
226
- td.push([this.data[i][0]]);
227
- if (i>0) {
228
- stack[i] += stack[i-1];
229
- }
230
- tot += this.data[i][1];
231
- }
232
- var fact = Math.PI*2/stack[stack.length - 1];
233
-
234
- for (var i=0; i<stack.length; i++) {
235
- td[i][1] = stack[i] * fact;
236
- td[i][2] = this.data[i][1]/tot;
237
- }
238
- this.gridData = td;
239
- };
240
-
241
- $.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) {
242
- var stack = [];
243
- var td = [];
244
- var tot = 0;
245
- var sa = this.startAngle/180*Math.PI;
246
- // don't know if we have any valid data yet, so set plot to not draw.
247
- this._drawData = false;
248
- for (var i=0; i<data.length; i++){
249
- if (this.data[i][1] != 0) {
250
- // we have data, O.K. to draw.
251
- this._drawData = true;
252
- }
253
- stack.push(data[i][1]);
254
- td.push([data[i][0]]);
255
- if (i>0) {
256
- stack[i] += stack[i-1];
257
- }
258
- tot += data[i][1];
259
- }
260
- var fact = Math.PI*2/stack[stack.length - 1];
261
-
262
- for (var i=0; i<stack.length; i++) {
263
- td[i][1] = stack[i] * fact;
264
- td[i][2] = data[i][1]/tot;
265
- }
266
- return td;
267
- };
268
-
269
- $.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
270
- var r = this._diameter / 2;
271
- var ri = r - this._thickness;
272
- var fill = this.fill;
273
- // var lineWidth = this.lineWidth;
274
- ctx.save();
275
- ctx.translate(this._center[0], this._center[1]);
276
- // ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2));
277
-
278
- if (isShadow) {
279
- for (var i=0; i<this.shadowDepth; i++) {
280
- ctx.save();
281
- ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
282
- doDraw();
283
- }
284
- }
285
-
286
- else {
287
- doDraw();
288
- }
289
-
290
- function doDraw () {
291
- // Fix for IE and Chrome that can't seem to draw circles correctly.
292
- // ang2 should always be <= 2 pi since that is the way the data is converted.
293
- if (ang2 > 6.282 + this.startAngle) {
294
- ang2 = 6.282 + this.startAngle;
295
- if (ang1 > ang2) {
296
- ang1 = 6.281 + this.startAngle;
297
- }
298
- }
299
- // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids
300
- // ugly line on unfilled donuts.
301
- if (ang1 >= ang2) {
302
- return;
303
- }
304
- ctx.beginPath();
305
- ctx.fillStyle = color;
306
- ctx.strokeStyle = color;
307
- // ctx.lineWidth = lineWidth;
308
- ctx.arc(0, 0, r, ang1, ang2, false);
309
- ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2));
310
- ctx.arc(0,0, ri, ang2, ang1, true);
311
- ctx.closePath();
312
- if (fill) {
313
- ctx.fill();
314
- }
315
- else {
316
- ctx.stroke();
317
- }
318
- }
319
-
320
- if (isShadow) {
321
- for (var i=0; i<this.shadowDepth; i++) {
322
- ctx.restore();
323
- }
324
- }
325
-
326
- ctx.restore();
327
- };
328
-
329
- // called with scope of series
330
- $.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) {
331
- var i;
332
- var opts = (options != undefined) ? options : {};
333
- // offset and direction of offset due to legend placement
334
- var offx = 0;
335
- var offy = 0;
336
- var trans = 1;
337
- // var colorGenerator = new this.colorGenerator(this.seriesColors);
338
- if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
339
- var li = options.legendInfo;
340
- switch (li.location) {
341
- case 'nw':
342
- offx = li.width + li.xoffset;
343
- break;
344
- case 'w':
345
- offx = li.width + li.xoffset;
346
- break;
347
- case 'sw':
348
- offx = li.width + li.xoffset;
349
- break;
350
- case 'ne':
351
- offx = li.width + li.xoffset;
352
- trans = -1;
353
- break;
354
- case 'e':
355
- offx = li.width + li.xoffset;
356
- trans = -1;
357
- break;
358
- case 'se':
359
- offx = li.width + li.xoffset;
360
- trans = -1;
361
- break;
362
- case 'n':
363
- offy = li.height + li.yoffset;
364
- break;
365
- case 's':
366
- offy = li.height + li.yoffset;
367
- trans = -1;
368
- break;
369
- default:
370
- break;
371
- }
372
- }
373
-
374
- var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
375
- var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
376
- var fill = (opts.fill != undefined) ? opts.fill : this.fill;
377
- var cw = ctx.canvas.width;
378
- var ch = ctx.canvas.height;
379
- var w = cw - offx - 2 * this.padding;
380
- var h = ch - offy - 2 * this.padding;
381
- var mindim = Math.min(w,h);
382
- var d = mindim;
383
- var ringmargin = (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin;
384
-
385
- for (var i=0; i<this._previousSeries.length; i++) {
386
- d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin;
387
- }
388
- this._diameter = this.diameter || d;
389
- if (this.innerDiameter != null) {
390
- var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter;
391
- this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0;
392
- }
393
- else {
394
- this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85;
395
- }
396
-
397
- var r = this._radius = this._diameter/2;
398
- this._innerRadius = this._radius - this._thickness;
399
- var sa = this.startAngle / 180 * Math.PI;
400
- this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
401
-
402
- if (this.shadow) {
403
- var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
404
- for (var i=0; i<gd.length; i++) {
405
- var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
406
- // Adjust ang1 and ang2 for sliceMargin
407
- ang1 += this.sliceMargin/180*Math.PI;
408
- this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true);
409
- }
410
-
411
- }
412
- for (var i=0; i<gd.length; i++) {
413
- var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
414
- // Adjust ang1 and ang2 for sliceMargin
415
- ang1 += this.sliceMargin/180*Math.PI;
416
- var ang2 = gd[i][1] + sa;
417
- this._sliceAngles.push([ang1, ang2]);
418
- this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false);
419
-
420
- if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
421
- var fstr, avgang = (ang1+ang2)/2, label;
422
-
423
- if (this.dataLabels == 'label') {
424
- fstr = this.dataLabelFormatString || '%s';
425
- label = $.jqplot.sprintf(fstr, gd[i][0]);
426
- }
427
- else if (this.dataLabels == 'value') {
428
- fstr = this.dataLabelFormatString || '%d';
429
- label = $.jqplot.sprintf(fstr, this.data[i][1]);
430
- }
431
- else if (this.dataLabels == 'percent') {
432
- fstr = this.dataLabelFormatString || '%d%%';
433
- label = $.jqplot.sprintf(fstr, gd[i][2]*100);
434
- }
435
- else if (this.dataLabels.constructor == Array) {
436
- fstr = this.dataLabelFormatString || '%s';
437
- label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
438
- }
439
-
440
- var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
441
-
442
- var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
443
- var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
444
-
445
- var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
446
- x -= labelelem.width()/2;
447
- y -= labelelem.height()/2;
448
- x = Math.round(x);
449
- y = Math.round(y);
450
- labelelem.css({left: x, top: y});
451
- }
452
- }
453
-
454
- };
455
-
456
- $.jqplot.DonutAxisRenderer = function() {
457
- $.jqplot.LinearAxisRenderer.call(this);
458
- };
459
-
460
- $.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
461
- $.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer;
462
-
463
-
464
- // There are no traditional axes on a donut chart. We just need to provide
465
- // dummy objects with properties so the plot will render.
466
- // called with scope of axis object.
467
- $.jqplot.DonutAxisRenderer.prototype.init = function(options){
468
- //
469
- this.tickRenderer = $.jqplot.DonutTickRenderer;
470
- $.extend(true, this, options);
471
- // I don't think I'm going to need _dataBounds here.
472
- // have to go Axis scaling in a way to fit chart onto plot area
473
- // and provide u2p and p2u functionality for mouse cursor, etc.
474
- // for convienence set _dataBounds to 0 and 100 and
475
- // set min/max to 0 and 100.
476
- this._dataBounds = {min:0, max:100};
477
- this.min = 0;
478
- this.max = 100;
479
- this.showTicks = false;
480
- this.ticks = [];
481
- this.showMark = false;
482
- this.show = false;
483
- };
484
-
485
-
486
-
487
-
488
- $.jqplot.DonutLegendRenderer = function(){
489
- $.jqplot.TableLegendRenderer.call(this);
490
- };
491
-
492
- $.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
493
- $.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer;
494
-
495
- /**
496
- * Class: $.jqplot.DonutLegendRenderer
497
- * Legend Renderer specific to donut plots. Set by default
498
- * when user creates a donut plot.
499
- */
500
- $.jqplot.DonutLegendRenderer.prototype.init = function(options) {
501
- // Group: Properties
502
- //
503
- // prop: numberRows
504
- // Maximum number of rows in the legend. 0 or null for unlimited.
505
- this.numberRows = null;
506
- // prop: numberColumns
507
- // Maximum number of columns in the legend. 0 or null for unlimited.
508
- this.numberColumns = null;
509
- $.extend(true, this, options);
510
- };
511
-
512
- // called with context of legend
513
- $.jqplot.DonutLegendRenderer.prototype.draw = function() {
514
- var legend = this;
515
- if (this.show) {
516
- var series = this._series;
517
- var ss = 'position:absolute;';
518
- ss += (this.background) ? 'background:'+this.background+';' : '';
519
- ss += (this.border) ? 'border:'+this.border+';' : '';
520
- ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
521
- ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
522
- ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
523
- ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
524
- ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
525
- ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
526
- ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
527
- this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
528
- // Donut charts legends don't go by number of series, but by number of data points
529
- // in the series. Refactor things here for that.
530
-
531
- var pad = false,
532
- reverse = false,
533
- nr, nc;
534
- var s = series[0];
535
- var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
536
-
537
- if (s.show) {
538
- var pd = s.data;
539
- if (this.numberRows) {
540
- nr = this.numberRows;
541
- if (!this.numberColumns){
542
- nc = Math.ceil(pd.length/nr);
543
- }
544
- else{
545
- nc = this.numberColumns;
546
- }
547
- }
548
- else if (this.numberColumns) {
549
- nc = this.numberColumns;
550
- nr = Math.ceil(pd.length/this.numberColumns);
551
- }
552
- else {
553
- nr = pd.length;
554
- nc = 1;
555
- }
556
-
557
- var i, j, tr, td1, td2, lt, rs, color;
558
- var idx = 0;
559
-
560
- for (i=0; i<nr; i++) {
561
- if (reverse){
562
- tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
563
- }
564
- else{
565
- tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
566
- }
567
- for (j=0; j<nc; j++) {
568
- if (idx < pd.length){
569
- lt = this.labels[idx] || pd[idx][0].toString();
570
- color = colorGenerator.next();
571
- if (!reverse){
572
- if (i>0){
573
- pad = true;
574
- }
575
- else{
576
- pad = false;
577
- }
578
- }
579
- else{
580
- if (i == nr -1){
581
- pad = false;
582
- }
583
- else{
584
- pad = true;
585
- }
586
- }
587
- rs = (pad) ? this.rowSpacing : '0';
588
-
589
- td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
590
- '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
591
- '</div></td>');
592
- td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
593
- if (this.escapeHtml){
594
- td2.text(lt);
595
- }
596
- else {
597
- td2.html(lt);
598
- }
599
- if (reverse) {
600
- td2.prependTo(tr);
601
- td1.prependTo(tr);
602
- }
603
- else {
604
- td1.appendTo(tr);
605
- td2.appendTo(tr);
606
- }
607
- pad = true;
608
- }
609
- idx++;
610
- }
611
- }
612
- }
613
- }
614
- return this._elem;
615
- };
616
-
617
- // setup default renderers for axes and legend so user doesn't have to
618
- // called with scope of plot
619
- function preInit(target, data, options) {
620
- options = options || {};
621
- options.axesDefaults = options.axesDefaults || {};
622
- options.legend = options.legend || {};
623
- options.seriesDefaults = options.seriesDefaults || {};
624
- // only set these if there is a donut series
625
- var setopts = false;
626
- if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) {
627
- setopts = true;
628
- }
629
- else if (options.series) {
630
- for (var i=0; i < options.series.length; i++) {
631
- if (options.series[i].renderer == $.jqplot.DonutRenderer) {
632
- setopts = true;
633
- }
634
- }
635
- }
636
-
637
- if (setopts) {
638
- options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer;
639
- options.legend.renderer = $.jqplot.DonutLegendRenderer;
640
- options.legend.preDraw = true;
641
- options.seriesDefaults.pointLabels = {show: false};
642
- }
643
- }
644
-
645
- // called with scope of plot.
646
- function postInit(target, data, options) {
647
- // if multiple series, add a reference to the previous one so that
648
- // donut rings can nest.
649
- for (var i=1; i<this.series.length; i++) {
650
- if (!this.series[i]._previousSeries.length){
651
- for (var j=0; j<i; j++) {
652
- if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) {
653
- this.series[i]._previousSeries.push(this.series[j]);
654
- }
655
- }
656
- }
657
- }
658
- for (i=0; i<this.series.length; i++) {
659
- if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) {
660
- this.series[i]._numberSeries = this.series.length;
661
- // don't allow mouseover and mousedown at same time.
662
- if (this.series[i].highlightMouseOver) {
663
- this.series[i].highlightMouseDown = false;
664
- }
665
- }
666
- }
667
- }
668
-
669
- var postParseOptionsRun = false;
670
- // called with scope of plot
671
- function postParseOptions(options) {
672
- for (var i=0; i<this.series.length; i++) {
673
- this.series[i].seriesColors = this.seriesColors;
674
- this.series[i].colorGenerator = $.jqplot.colorGenerator;
675
- }
676
- }
677
-
678
- function highlight (plot, sidx, pidx) {
679
- var s = plot.series[sidx];
680
- var canvas = plot.plugins.donutRenderer.highlightCanvas;
681
- canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
682
- s._highlightedPoint = pidx;
683
- plot.plugins.donutRenderer.highlightedSeriesIndex = sidx;
684
- s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false);
685
- }
686
-
687
- function unhighlight (plot) {
688
- var canvas = plot.plugins.donutRenderer.highlightCanvas;
689
- canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
690
- for (var i=0; i<plot.series.length; i++) {
691
- plot.series[i]._highlightedPoint = null;
692
- }
693
- plot.plugins.donutRenderer.highlightedSeriesIndex = null;
694
- plot.target.trigger('jqplotDataUnhighlight');
695
- }
696
-
697
- function handleMove(ev, gridpos, datapos, neighbor, plot) {
698
- if (neighbor) {
699
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
700
- var evt1 = jQuery.Event('jqplotDataMouseOver');
701
- evt1.pageX = ev.pageX;
702
- evt1.pageY = ev.pageY;
703
- plot.target.trigger(evt1, ins);
704
- if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
705
- var evt = jQuery.Event('jqplotDataHighlight');
706
- evt.which = ev.which;
707
- evt.pageX = ev.pageX;
708
- evt.pageY = ev.pageY;
709
- plot.target.trigger(evt, ins);
710
- highlight (plot, ins[0], ins[1]);
711
- }
712
- }
713
- else if (neighbor == null) {
714
- unhighlight (plot);
715
- }
716
- }
717
-
718
- function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
719
- if (neighbor) {
720
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
721
- if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
722
- var evt = jQuery.Event('jqplotDataHighlight');
723
- evt.which = ev.which;
724
- evt.pageX = ev.pageX;
725
- evt.pageY = ev.pageY;
726
- plot.target.trigger(evt, ins);
727
- highlight (plot, ins[0], ins[1]);
728
- }
729
- }
730
- else if (neighbor == null) {
731
- unhighlight (plot);
732
- }
733
- }
734
-
735
- function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
736
- var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
737
- if (idx != null && plot.series[idx].highlightMouseDown) {
738
- unhighlight(plot);
739
- }
740
- }
741
-
742
- function handleClick(ev, gridpos, datapos, neighbor, plot) {
743
- if (neighbor) {
744
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
745
- var evt = jQuery.Event('jqplotDataClick');
746
- evt.which = ev.which;
747
- evt.pageX = ev.pageX;
748
- evt.pageY = ev.pageY;
749
- plot.target.trigger(evt, ins);
750
- }
751
- }
752
-
753
- function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
754
- if (neighbor) {
755
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
756
- var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
757
- if (idx != null && plot.series[idx].highlightMouseDown) {
758
- unhighlight(plot);
759
- }
760
- var evt = jQuery.Event('jqplotDataRightClick');
761
- evt.which = ev.which;
762
- evt.pageX = ev.pageX;
763
- evt.pageY = ev.pageY;
764
- plot.target.trigger(evt, ins);
765
- }
766
- }
767
-
768
- // called within context of plot
769
- // create a canvas which we can draw on.
770
- // insert it before the eventCanvas, so eventCanvas will still capture events.
771
- function postPlotDraw() {
772
- // Memory Leaks patch
773
- if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) {
774
- this.plugins.donutRenderer.highlightCanvas.resetCanvas();
775
- this.plugins.donutRenderer.highlightCanvas = null;
776
- }
777
-
778
- this.plugins.donutRenderer = {highlightedSeriesIndex:null};
779
- this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
780
- // do we have any data labels? if so, put highlight canvas before those
781
- // Fix for broken jquery :first selector with canvas (VML) elements.
782
- var labels = $(this.targetId+' .jqplot-data-label');
783
- if (labels.length) {
784
- $(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this));
785
- }
786
- // else put highlight canvas before event canvas.
787
- else {
788
- this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this));
789
- }
790
- var hctx = this.plugins.donutRenderer.highlightCanvas.setContext();
791
- this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
792
- }
793
-
794
- $.jqplot.preInitHooks.push(preInit);
795
-
796
- $.jqplot.DonutTickRenderer = function() {
797
- $.jqplot.AxisTickRenderer.call(this);
798
- };
799
-
800
- $.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
801
- $.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer;
802
-
803
- })(jQuery);
804
-
805
-
1
+ /**
2
+ * jqPlot
3
+ * Pure JavaScript plotting plugin using jQuery
4
+ *
5
+ * Version: 1.0.9
6
+ * Revision: d96a669
7
+ *
8
+ * Copyright (c) 2009-2016 Chris Leonello
9
+ * jqPlot is currently available for use in all personal or commercial projects
10
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12
+ * choose the license that best suits your project and use it accordingly.
13
+ *
14
+ * Although not required, the author would appreciate an email letting him
15
+ * know of any substantial use of jqPlot. You can reach the author at:
16
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
17
+ *
18
+ * If you are feeling kind and generous, consider supporting the project by
19
+ * making a donation at: http://www.jqplot.com/donate.php .
20
+ *
21
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22
+ *
23
+ * version 2007.04.27
24
+ * author Ash Searle
25
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
26
+ * http://hexmen.com/js/sprintf.js
27
+ * The author (Ash Searle) has placed this code in the public domain:
28
+ * "This code is unrestricted: you are free to use it however you like."
29
+ *
30
+ */
31
+ (function($) {
32
+ /**
33
+ * Class: $.jqplot.DonutRenderer
34
+ * Plugin renderer to draw a donut chart.
35
+ * x values, if present, will be used as slice labels.
36
+ * y values give slice size.
37
+ *
38
+ * To use this renderer, you need to include the
39
+ * donut renderer plugin, for example:
40
+ *
41
+ * > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script>
42
+ *
43
+ * Properties described here are passed into the $.jqplot function
44
+ * as options on the series renderer. For example:
45
+ *
46
+ * > plot2 = $.jqplot('chart2', [s1, s2], {
47
+ * > seriesDefaults: {
48
+ * > renderer:$.jqplot.DonutRenderer,
49
+ * > rendererOptions:{
50
+ * > sliceMargin: 2,
51
+ * > innerDiameter: 110,
52
+ * > startAngle: -90
53
+ * > }
54
+ * > }
55
+ * > });
56
+ *
57
+ * A donut plot will trigger events on the plot target
58
+ * according to user interaction. All events return the event object,
59
+ * the series index, the point (slice) index, and the point data for
60
+ * the appropriate slice.
61
+ *
62
+ * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
63
+ * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
64
+ * if highlighting is enabled.
65
+ * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
66
+ * a highlighted slice.
67
+ * 'jqplotDataClick' - triggered when the user clicks on a slice.
68
+ * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
69
+ * the "captureRightClick" option is set to true on the plot.
70
+ */
71
+ $.jqplot.DonutRenderer = function(){
72
+ $.jqplot.LineRenderer.call(this);
73
+ };
74
+
75
+ $.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer();
76
+ $.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer;
77
+
78
+ // called with scope of a series
79
+ $.jqplot.DonutRenderer.prototype.init = function(options, plot) {
80
+ // Group: Properties
81
+ //
82
+ // prop: diameter
83
+ // Outer diameter of the donut, auto computed by default
84
+ this.diameter = null;
85
+ // prop: innerDiameter
86
+ // Inner diameter of the donut, auto calculated by default.
87
+ // If specified will override thickness value.
88
+ this.innerDiameter = null;
89
+ // prop: thickness
90
+ // thickness of the donut, auto computed by default
91
+ // Overridden by if innerDiameter is specified.
92
+ this.thickness = null;
93
+ // prop: padding
94
+ // padding between the donut and plot edges, legend, etc.
95
+ this.padding = 20;
96
+ // prop: sliceMargin
97
+ // angular spacing between donut slices in degrees.
98
+ this.sliceMargin = 0;
99
+ // prop: ringMargin
100
+ // pixel distance between rings, or multiple series in a donut plot.
101
+ // null will compute ringMargin based on sliceMargin.
102
+ this.ringMargin = null;
103
+ // prop: fill
104
+ // true or false, whether to fil the slices.
105
+ this.fill = true;
106
+ // prop: shadowOffset
107
+ // offset of the shadow from the slice and offset of
108
+ // each succesive stroke of the shadow from the last.
109
+ this.shadowOffset = 2;
110
+ // prop: shadowAlpha
111
+ // transparency of the shadow (0 = transparent, 1 = opaque)
112
+ this.shadowAlpha = 0.07;
113
+ // prop: shadowDepth
114
+ // number of strokes to apply to the shadow,
115
+ // each stroke offset shadowOffset from the last.
116
+ this.shadowDepth = 5;
117
+ // prop: highlightMouseOver
118
+ // True to highlight slice when moused over.
119
+ // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
120
+ this.highlightMouseOver = true;
121
+ // prop: highlightMouseDown
122
+ // True to highlight when a mouse button is pressed over a slice.
123
+ // This will be disabled if highlightMouseOver is true.
124
+ this.highlightMouseDown = false;
125
+ // prop: highlightColors
126
+ // an array of colors to use when highlighting a slice.
127
+ this.highlightColors = [];
128
+ // prop: dataLabels
129
+ // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
130
+ // Defaults to percentage of each pie slice.
131
+ this.dataLabels = 'percent';
132
+ // prop: showDataLabels
133
+ // true to show data labels on slices.
134
+ this.showDataLabels = false;
135
+ // prop: totalLabel
136
+ // true to show total label in the centre
137
+ this.totalLabel = false;
138
+ // prop: dataLabelFormatString
139
+ // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
140
+ this.dataLabelFormatString = null;
141
+ // prop: dataLabelThreshold
142
+ // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
143
+ // This applies to all label types, not just to percentage labels.
144
+ this.dataLabelThreshold = 3;
145
+ // prop: dataLabelPositionFactor
146
+ // A Multiplier (0-1) of the pie radius which controls position of label on slice.
147
+ // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
148
+ this.dataLabelPositionFactor = 0.4;
149
+ // prop: dataLabelNudge
150
+ // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
151
+ this.dataLabelNudge = 0;
152
+ // prop: startAngle
153
+ // Angle to start drawing donut in degrees.
154
+ // According to orientation of canvas coordinate system:
155
+ // 0 = on the positive x axis
156
+ // -90 = on the positive y axis.
157
+ // 90 = on the negaive y axis.
158
+ // 180 or - 180 = on the negative x axis.
159
+ this.startAngle = 0;
160
+ this.tickRenderer = $.jqplot.DonutTickRenderer;
161
+ // Used as check for conditions where donut shouldn't be drawn.
162
+ this._drawData = true;
163
+ this._type = 'donut';
164
+
165
+ // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
166
+ if (options.highlightMouseDown && options.highlightMouseOver == null) {
167
+ options.highlightMouseOver = false;
168
+ }
169
+
170
+ $.extend(true, this, options);
171
+ if (this.diameter != null) {
172
+ this.diameter = this.diameter - this.sliceMargin;
173
+ }
174
+ this._diameter = null;
175
+ this._innerDiameter = null;
176
+ this._radius = null;
177
+ this._innerRadius = null;
178
+ this._thickness = null;
179
+ // references to the previous series in the plot to properly calculate diameters
180
+ // and thicknesses of nested rings.
181
+ this._previousSeries = [];
182
+ this._numberSeries = 1;
183
+ // array of [start,end] angles arrays, one for each slice. In radians.
184
+ this._sliceAngles = [];
185
+ // index of the currenty highlighted point, if any
186
+ this._highlightedPoint = null;
187
+
188
+ // set highlight colors if none provided
189
+ if (this.highlightColors.length == 0) {
190
+ for (var i=0; i<this.seriesColors.length; i++){
191
+ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
192
+ var newrgb = [rgba[0], rgba[1], rgba[2]];
193
+ var sum = newrgb[0] + newrgb[1] + newrgb[2];
194
+ for (var j=0; j<3; j++) {
195
+ // when darkening, lowest color component can be is 60.
196
+ newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
197
+ newrgb[j] = parseInt(newrgb[j], 10);
198
+ }
199
+ this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
200
+ }
201
+ }
202
+
203
+ plot.postParseOptionsHooks.addOnce(postParseOptions);
204
+ plot.postInitHooks.addOnce(postInit);
205
+ plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
206
+ plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
207
+ plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
208
+ plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
209
+ plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
210
+ plot.postDrawHooks.addOnce(postPlotDraw);
211
+
212
+
213
+ };
214
+
215
+ $.jqplot.DonutRenderer.prototype.setGridData = function(plot) {
216
+ // set gridData property. This will hold angle in radians of each data point.
217
+ var stack = [];
218
+ var td = [];
219
+ var sa = this.startAngle/180*Math.PI;
220
+ var tot = 0;
221
+ // don't know if we have any valid data yet, so set plot to not draw.
222
+ this._drawData = false;
223
+ for (var i=0; i<this.data.length; i++){
224
+ if (this.data[i][1] != 0) {
225
+ // we have data, O.K. to draw.
226
+ this._drawData = true;
227
+ }
228
+ stack.push(this.data[i][1]);
229
+ td.push([this.data[i][0]]);
230
+ if (i>0) {
231
+ stack[i] += stack[i-1];
232
+ }
233
+ tot += this.data[i][1];
234
+ }
235
+ var fact = Math.PI*2/stack[stack.length - 1];
236
+
237
+ for (var i=0; i<stack.length; i++) {
238
+ td[i][1] = stack[i] * fact;
239
+ td[i][2] = this.data[i][1]/tot;
240
+ }
241
+ this.gridData = td;
242
+ };
243
+
244
+ $.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) {
245
+ var stack = [];
246
+ var td = [];
247
+ var tot = 0;
248
+ var sa = this.startAngle/180*Math.PI;
249
+ // don't know if we have any valid data yet, so set plot to not draw.
250
+ this._drawData = false;
251
+ for (var i=0; i<data.length; i++){
252
+ if (this.data[i][1] != 0) {
253
+ // we have data, O.K. to draw.
254
+ this._drawData = true;
255
+ }
256
+ stack.push(data[i][1]);
257
+ td.push([data[i][0]]);
258
+ if (i>0) {
259
+ stack[i] += stack[i-1];
260
+ }
261
+ tot += data[i][1];
262
+ }
263
+ var fact = Math.PI*2/stack[stack.length - 1];
264
+
265
+ for (var i=0; i<stack.length; i++) {
266
+ td[i][1] = stack[i] * fact;
267
+ td[i][2] = data[i][1]/tot;
268
+ }
269
+ this._totalAmount = tot;
270
+ return td;
271
+ };
272
+
273
+ $.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
274
+ var r = this._diameter / 2;
275
+ var ri = r - this._thickness;
276
+ var fill = this.fill;
277
+ // var lineWidth = this.lineWidth;
278
+ ctx.save();
279
+ ctx.translate(this._center[0], this._center[1]);
280
+ // ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2));
281
+
282
+ if (isShadow) {
283
+ for (var i=0; i<this.shadowDepth; i++) {
284
+ ctx.save();
285
+ ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
286
+ doDraw();
287
+ }
288
+ }
289
+
290
+ else {
291
+ doDraw();
292
+ }
293
+
294
+ function doDraw () {
295
+ // Fix for IE and Chrome that can't seem to draw circles correctly.
296
+ // ang2 should always be <= 2 pi since that is the way the data is converted.
297
+ if (ang2 > 6.282 + this.startAngle) {
298
+ ang2 = 6.282 + this.startAngle;
299
+ if (ang1 > ang2) {
300
+ ang1 = 6.281 + this.startAngle;
301
+ }
302
+ }
303
+ // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids
304
+ // ugly line on unfilled donuts.
305
+ if (ang1 >= ang2) {
306
+ return;
307
+ }
308
+ ctx.beginPath();
309
+ ctx.fillStyle = color;
310
+ ctx.strokeStyle = color;
311
+ // ctx.lineWidth = lineWidth;
312
+ ctx.arc(0, 0, r, ang1, ang2, false);
313
+ ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2));
314
+ ctx.arc(0,0, ri, ang2, ang1, true);
315
+ ctx.closePath();
316
+ if (fill) {
317
+ ctx.fill();
318
+ }
319
+ else {
320
+ ctx.stroke();
321
+ }
322
+ }
323
+
324
+ if (isShadow) {
325
+ for (var i=0; i<this.shadowDepth; i++) {
326
+ ctx.restore();
327
+ }
328
+ }
329
+
330
+ ctx.restore();
331
+ };
332
+
333
+ // called with scope of series
334
+ $.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) {
335
+ var i;
336
+ var opts = (options != undefined) ? options : {};
337
+ // offset and direction of offset due to legend placement
338
+ var offx = 0;
339
+ var offy = 0;
340
+ var trans = 1;
341
+ // var colorGenerator = new this.colorGenerator(this.seriesColors);
342
+ if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
343
+ var li = options.legendInfo;
344
+ switch (li.location) {
345
+ case 'nw':
346
+ offx = li.width + li.xoffset;
347
+ break;
348
+ case 'w':
349
+ offx = li.width + li.xoffset;
350
+ break;
351
+ case 'sw':
352
+ offx = li.width + li.xoffset;
353
+ break;
354
+ case 'ne':
355
+ offx = li.width + li.xoffset;
356
+ trans = -1;
357
+ break;
358
+ case 'e':
359
+ offx = li.width + li.xoffset;
360
+ trans = -1;
361
+ break;
362
+ case 'se':
363
+ offx = li.width + li.xoffset;
364
+ trans = -1;
365
+ break;
366
+ case 'n':
367
+ offy = li.height + li.yoffset;
368
+ break;
369
+ case 's':
370
+ offy = li.height + li.yoffset;
371
+ trans = -1;
372
+ break;
373
+ default:
374
+ break;
375
+ }
376
+ }
377
+
378
+ var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
379
+ var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
380
+ var fill = (opts.fill != undefined) ? opts.fill : this.fill;
381
+ //see http://stackoverflow.com/questions/20221461/hidpi-retina-plot-drawing
382
+ var cw = parseInt(ctx.canvas.style.width);
383
+ var ch = parseInt(ctx.canvas.style.height);
384
+ var w = cw - offx - 2 * this.padding;
385
+ var h = ch - offy - 2 * this.padding;
386
+ var mindim = Math.min(w,h);
387
+ var d = mindim;
388
+ var ringmargin = (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin;
389
+
390
+ for (var i=0; i<this._previousSeries.length; i++) {
391
+ d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin;
392
+ }
393
+ this._diameter = this.diameter || d;
394
+ if (this.innerDiameter != null) {
395
+ var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter;
396
+ this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0;
397
+ }
398
+ else {
399
+ this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85;
400
+ }
401
+ if (this._diameter < 6) {
402
+ $.jqplot.log("Diameter of donut too small, not rendering.");
403
+ return;
404
+ }
405
+ var r = this._radius = this._diameter/2;
406
+ this._innerRadius = this._radius - this._thickness;
407
+ var sa = this.startAngle / 180 * Math.PI;
408
+ this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
409
+
410
+ if (this.shadow) {
411
+ var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
412
+ for (var i=0; i<gd.length; i++) {
413
+ var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
414
+ // Adjust ang1 and ang2 for sliceMargin
415
+ ang1 += this.sliceMargin/180*Math.PI;
416
+ this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true);
417
+ }
418
+
419
+ }
420
+ for (var i=0; i<gd.length; i++) {
421
+ var ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
422
+ // Adjust ang1 and ang2 for sliceMargin
423
+ ang1 += this.sliceMargin/180*Math.PI;
424
+ var ang2 = gd[i][1] + sa;
425
+ this._sliceAngles.push([ang1, ang2]);
426
+ this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false);
427
+
428
+ if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
429
+ var fstr, avgang = (ang1+ang2)/2, label;
430
+
431
+ if (this.dataLabels == 'label') {
432
+ fstr = this.dataLabelFormatString || '%s';
433
+ label = $.jqplot.sprintf(fstr, gd[i][0]);
434
+ }
435
+ else if (this.dataLabels == 'value') {
436
+ fstr = this.dataLabelFormatString || '%d';
437
+ label = $.jqplot.sprintf(fstr, this.data[i][1]);
438
+ }
439
+ else if (this.dataLabels == 'percent') {
440
+ fstr = this.dataLabelFormatString || '%d%%';
441
+ label = $.jqplot.sprintf(fstr, gd[i][2]*100);
442
+ }
443
+ else if (this.dataLabels.constructor == Array) {
444
+ fstr = this.dataLabelFormatString || '%s';
445
+ label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
446
+ }
447
+
448
+ var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
449
+
450
+ var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
451
+ var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
452
+
453
+ var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
454
+ x -= labelelem.width()/2;
455
+ y -= labelelem.height()/2;
456
+ x = Math.round(x);
457
+ y = Math.round(y);
458
+ labelelem.css({left: x, top: y});
459
+ }
460
+ }
461
+ if (this.totalLabel) {
462
+ var totalLabel = $('<div class="jqplot-data-label" style="position:absolute">' + this._totalAmount + '</div>').insertAfter(plot.eventCanvas._elem);
463
+ totalLabel.css({left: this._center[0], top: this._center[1]});
464
+ }
465
+ };
466
+
467
+ $.jqplot.DonutAxisRenderer = function() {
468
+ $.jqplot.LinearAxisRenderer.call(this);
469
+ };
470
+
471
+ $.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
472
+ $.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer;
473
+
474
+
475
+ // There are no traditional axes on a donut chart. We just need to provide
476
+ // dummy objects with properties so the plot will render.
477
+ // called with scope of axis object.
478
+ $.jqplot.DonutAxisRenderer.prototype.init = function(options){
479
+ //
480
+ this.tickRenderer = $.jqplot.DonutTickRenderer;
481
+ $.extend(true, this, options);
482
+ // I don't think I'm going to need _dataBounds here.
483
+ // have to go Axis scaling in a way to fit chart onto plot area
484
+ // and provide u2p and p2u functionality for mouse cursor, etc.
485
+ // for convienence set _dataBounds to 0 and 100 and
486
+ // set min/max to 0 and 100.
487
+ this._dataBounds = {min:0, max:100};
488
+ this.min = 0;
489
+ this.max = 100;
490
+ this.showTicks = false;
491
+ this.ticks = [];
492
+ this.showMark = false;
493
+ this.show = false;
494
+ };
495
+
496
+
497
+
498
+
499
+ $.jqplot.DonutLegendRenderer = function(){
500
+ $.jqplot.TableLegendRenderer.call(this);
501
+ };
502
+
503
+ $.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
504
+ $.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer;
505
+
506
+ /**
507
+ * Class: $.jqplot.DonutLegendRenderer
508
+ * Legend Renderer specific to donut plots. Set by default
509
+ * when user creates a donut plot.
510
+ */
511
+ $.jqplot.DonutLegendRenderer.prototype.init = function(options) {
512
+ // Group: Properties
513
+ //
514
+ // prop: numberRows
515
+ // Maximum number of rows in the legend. 0 or null for unlimited.
516
+ this.numberRows = null;
517
+ // prop: numberColumns
518
+ // Maximum number of columns in the legend. 0 or null for unlimited.
519
+ this.numberColumns = null;
520
+ $.extend(true, this, options);
521
+ };
522
+
523
+ // called with context of legend
524
+ $.jqplot.DonutLegendRenderer.prototype.draw = function() {
525
+ var legend = this;
526
+ if (this.show) {
527
+ var series = this._series;
528
+ var ss = 'position:absolute;';
529
+ ss += (this.background) ? 'background:'+this.background+';' : '';
530
+ ss += (this.border) ? 'border:'+this.border+';' : '';
531
+ ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
532
+ ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
533
+ ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
534
+ ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
535
+ ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
536
+ ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
537
+ ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
538
+ this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
539
+ // Donut charts legends don't go by number of series, but by number of data points
540
+ // in the series. Refactor things here for that.
541
+
542
+ var pad = false,
543
+ reverse = false,
544
+ nr, nc;
545
+ var s = series[0];
546
+ var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
547
+
548
+ if (s.show) {
549
+ var pd = s.data;
550
+ if (this.numberRows) {
551
+ nr = this.numberRows;
552
+ if (!this.numberColumns){
553
+ nc = Math.ceil(pd.length/nr);
554
+ }
555
+ else{
556
+ nc = this.numberColumns;
557
+ }
558
+ }
559
+ else if (this.numberColumns) {
560
+ nc = this.numberColumns;
561
+ nr = Math.ceil(pd.length/this.numberColumns);
562
+ }
563
+ else {
564
+ nr = pd.length;
565
+ nc = 1;
566
+ }
567
+
568
+ var i, j, tr, td1, td2, lt, rs, color;
569
+ var idx = 0;
570
+
571
+ for (i=0; i<nr; i++) {
572
+ if (reverse){
573
+ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
574
+ }
575
+ else{
576
+ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
577
+ }
578
+ for (j=0; j<nc; j++) {
579
+ if (idx < pd.length){
580
+ lt = this.labels[idx] || pd[idx][0].toString();
581
+ color = colorGenerator.next();
582
+ if (!reverse){
583
+ if (i>0){
584
+ pad = true;
585
+ }
586
+ else{
587
+ pad = false;
588
+ }
589
+ }
590
+ else{
591
+ if (i == nr -1){
592
+ pad = false;
593
+ }
594
+ else{
595
+ pad = true;
596
+ }
597
+ }
598
+ rs = (pad) ? this.rowSpacing : '0';
599
+
600
+ td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
601
+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
602
+ '</div></td>');
603
+ td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
604
+ if (this.escapeHtml){
605
+ td2.text(lt);
606
+ }
607
+ else {
608
+ td2.html(lt);
609
+ }
610
+ if (reverse) {
611
+ td2.prependTo(tr);
612
+ td1.prependTo(tr);
613
+ }
614
+ else {
615
+ td1.appendTo(tr);
616
+ td2.appendTo(tr);
617
+ }
618
+ pad = true;
619
+ }
620
+ idx++;
621
+ }
622
+ }
623
+ }
624
+ }
625
+ return this._elem;
626
+ };
627
+
628
+ // setup default renderers for axes and legend so user doesn't have to
629
+ // called with scope of plot
630
+ function preInit(target, data, options) {
631
+ options = options || {};
632
+ options.axesDefaults = options.axesDefaults || {};
633
+ options.legend = options.legend || {};
634
+ options.seriesDefaults = options.seriesDefaults || {};
635
+ // only set these if there is a donut series
636
+ var setopts = false;
637
+ if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) {
638
+ setopts = true;
639
+ }
640
+ else if (options.series) {
641
+ for (var i=0; i < options.series.length; i++) {
642
+ if (options.series[i].renderer == $.jqplot.DonutRenderer) {
643
+ setopts = true;
644
+ }
645
+ }
646
+ }
647
+
648
+ if (setopts) {
649
+ options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer;
650
+ options.legend.renderer = $.jqplot.DonutLegendRenderer;
651
+ options.legend.preDraw = true;
652
+ options.seriesDefaults.pointLabels = {show: false};
653
+ }
654
+ }
655
+
656
+ // called with scope of plot.
657
+ function postInit(target, data, options) {
658
+ // if multiple series, add a reference to the previous one so that
659
+ // donut rings can nest.
660
+ for (var i=1; i<this.series.length; i++) {
661
+ if (!this.series[i]._previousSeries.length){
662
+ for (var j=0; j<i; j++) {
663
+ if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) {
664
+ this.series[i]._previousSeries.push(this.series[j]);
665
+ }
666
+ }
667
+ }
668
+ }
669
+ for (i=0; i<this.series.length; i++) {
670
+ if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) {
671
+ this.series[i]._numberSeries = this.series.length;
672
+ // don't allow mouseover and mousedown at same time.
673
+ if (this.series[i].highlightMouseOver) {
674
+ this.series[i].highlightMouseDown = false;
675
+ }
676
+ }
677
+ }
678
+ }
679
+
680
+ var postParseOptionsRun = false;
681
+ // called with scope of plot
682
+ function postParseOptions(options) {
683
+ for (var i=0; i<this.series.length; i++) {
684
+ this.series[i].seriesColors = this.seriesColors;
685
+ this.series[i].colorGenerator = $.jqplot.colorGenerator;
686
+ }
687
+ }
688
+
689
+ function highlight (plot, sidx, pidx) {
690
+ var s = plot.series[sidx];
691
+ var canvas = plot.plugins.donutRenderer.highlightCanvas;
692
+ canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
693
+ s._highlightedPoint = pidx;
694
+ plot.plugins.donutRenderer.highlightedSeriesIndex = sidx;
695
+ s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false);
696
+ }
697
+
698
+ function unhighlight (plot) {
699
+ var canvas = plot.plugins.donutRenderer.highlightCanvas;
700
+ canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
701
+ for (var i=0; i<plot.series.length; i++) {
702
+ plot.series[i]._highlightedPoint = null;
703
+ }
704
+ plot.plugins.donutRenderer.highlightedSeriesIndex = null;
705
+ plot.target.trigger('jqplotDataUnhighlight');
706
+ }
707
+
708
+ function handleMove(ev, gridpos, datapos, neighbor, plot) {
709
+ if (neighbor) {
710
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
711
+ var evt1 = jQuery.Event('jqplotDataMouseOver');
712
+ evt1.pageX = ev.pageX;
713
+ evt1.pageY = ev.pageY;
714
+ plot.target.trigger(evt1, ins);
715
+ if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
716
+ var evt = jQuery.Event('jqplotDataHighlight');
717
+ evt.which = ev.which;
718
+ evt.pageX = ev.pageX;
719
+ evt.pageY = ev.pageY;
720
+ plot.target.trigger(evt, ins);
721
+ highlight (plot, ins[0], ins[1]);
722
+ }
723
+ }
724
+ else if (neighbor == null) {
725
+ unhighlight (plot);
726
+ }
727
+ }
728
+
729
+ function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
730
+ if (neighbor) {
731
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
732
+ if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
733
+ var evt = jQuery.Event('jqplotDataHighlight');
734
+ evt.which = ev.which;
735
+ evt.pageX = ev.pageX;
736
+ evt.pageY = ev.pageY;
737
+ plot.target.trigger(evt, ins);
738
+ highlight (plot, ins[0], ins[1]);
739
+ }
740
+ }
741
+ else if (neighbor == null) {
742
+ unhighlight (plot);
743
+ }
744
+ }
745
+
746
+ function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
747
+ var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
748
+ if (idx != null && plot.series[idx].highlightMouseDown) {
749
+ unhighlight(plot);
750
+ }
751
+ }
752
+
753
+ function handleClick(ev, gridpos, datapos, neighbor, plot) {
754
+ if (neighbor) {
755
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
756
+ var evt = jQuery.Event('jqplotDataClick');
757
+ evt.which = ev.which;
758
+ evt.pageX = ev.pageX;
759
+ evt.pageY = ev.pageY;
760
+ plot.target.trigger(evt, ins);
761
+ }
762
+ }
763
+
764
+ function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
765
+ if (neighbor) {
766
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
767
+ var idx = plot.plugins.donutRenderer.highlightedSeriesIndex;
768
+ if (idx != null && plot.series[idx].highlightMouseDown) {
769
+ unhighlight(plot);
770
+ }
771
+ var evt = jQuery.Event('jqplotDataRightClick');
772
+ evt.which = ev.which;
773
+ evt.pageX = ev.pageX;
774
+ evt.pageY = ev.pageY;
775
+ plot.target.trigger(evt, ins);
776
+ }
777
+ }
778
+
779
+ // called within context of plot
780
+ // create a canvas which we can draw on.
781
+ // insert it before the eventCanvas, so eventCanvas will still capture events.
782
+ function postPlotDraw() {
783
+ // Memory Leaks patch
784
+ if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) {
785
+ this.plugins.donutRenderer.highlightCanvas.resetCanvas();
786
+ this.plugins.donutRenderer.highlightCanvas = null;
787
+ }
788
+
789
+ this.plugins.donutRenderer = {highlightedSeriesIndex:null};
790
+ this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
791
+ // do we have any data labels? if so, put highlight canvas before those
792
+ // Fix for broken jquery :first selector with canvas (VML) elements.
793
+ var labels = $(this.targetId+' .jqplot-data-label');
794
+ if (labels.length) {
795
+ $(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this));
796
+ }
797
+ // else put highlight canvas before event canvas.
798
+ else {
799
+ this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this));
800
+ }
801
+ var hctx = this.plugins.donutRenderer.highlightCanvas.setContext();
802
+ this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
803
+ }
804
+
805
+ $.jqplot.preInitHooks.push(preInit);
806
+
807
+ $.jqplot.DonutTickRenderer = function() {
808
+ $.jqplot.AxisTickRenderer.call(this);
809
+ };
810
+
811
+ $.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
812
+ $.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer;
813
+
814
+ })(jQuery);
815
+
816
+