Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 lars 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.PieRenderer
34
     * Plugin renderer to draw a pie 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
     * pie renderer plugin, for example:
40
     *
41
     * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.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.PieRenderer,
49
     * >         rendererOptions:{
50
     * >              sliceMargin: 2,
51
     * >              startAngle: -90
52
     * >          }
53
     * >      }
54
     * > });
55
     *
56
     * A pie plot will trigger events on the plot target
57
     * according to user interaction.  All events return the event object,
58
     * the series index, the point (slice) index, and the point data for
59
     * the appropriate slice.
60
     *
61
     * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
62
     * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
63
     * if highlighting is enabled.
64
     * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
65
     * a highlighted slice.
66
     * 'jqplotDataClick' - triggered when the user clicks on a slice.
67
     * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
68
     * the "captureRightClick" option is set to true on the plot.
69
     */
70
    $.jqplot.PieRenderer = function(){
71
        $.jqplot.LineRenderer.call(this);
72
    };
73
 
74
    $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
75
    $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
76
 
77
    // called with scope of a series
78
    $.jqplot.PieRenderer.prototype.init = function(options, plot) {
79
        // Group: Properties
80
        //
81
        // prop: diameter
82
        // Outer diameter of the pie, auto computed by default
83
        this.diameter = null;
84
        // prop: padding
85
        // padding between the pie and plot edges, legend, etc.
86
        this.padding = 20;
87
        // prop: sliceMargin
88
        // angular spacing between pie slices in degrees.
89
        this.sliceMargin = 0;
90
        // prop: fill
91
        // true or false, whether to fil the slices.
92
        this.fill = true;
93
        // prop: shadowOffset
94
        // offset of the shadow from the slice and offset of
95
        // each succesive stroke of the shadow from the last.
96
        this.shadowOffset = 2;
97
        // prop: shadowAlpha
98
        // transparency of the shadow (0 = transparent, 1 = opaque)
99
        this.shadowAlpha = 0.07;
100
        // prop: shadowDepth
101
        // number of strokes to apply to the shadow,
102
        // each stroke offset shadowOffset from the last.
103
        this.shadowDepth = 5;
104
        // prop: highlightMouseOver
105
        // True to highlight slice when moused over.
106
        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
107
        this.highlightMouseOver = true;
108
        // prop: highlightMouseDown
109
        // True to highlight when a mouse button is pressed over a slice.
110
        // This will be disabled if highlightMouseOver is true.
111
        this.highlightMouseDown = false;
112
        // prop: highlightColors
113
        // an array of colors to use when highlighting a slice.
114
        this.highlightColors = [];
115
        // prop: dataLabels
116
        // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
117
        // Defaults to percentage of each pie slice.
118
        this.dataLabels = 'percent';
119
        // prop: showDataLabels
120
        // true to show data labels on slices.
121
        this.showDataLabels = false;
122
        // prop: dataLabelFormatString
123
        // Format string for data labels.  If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
124
        this.dataLabelFormatString = null;
125
        // prop: dataLabelThreshold
126
        // Threshhold in percentage (0-100) of pie area, below which no label will be displayed.
127
        // This applies to all label types, not just to percentage labels.
128
        this.dataLabelThreshold = 3;
129
        // prop: dataLabelPositionFactor
130
        // A Multiplier (0-1) of the pie radius which controls position of label on slice.
131
        // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
132
        this.dataLabelPositionFactor = 0.52;
133
        // prop: dataLabelNudge
134
        // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
135
        this.dataLabelNudge = 2;
136
        // prop: dataLabelCenterOn
137
        // True to center the data label at its position.
138
        // False to set the inside facing edge of the label at its position.
139
        this.dataLabelCenterOn = true;
140
        // prop: startAngle
141
        // Angle to start drawing pie in degrees.
142
        // According to orientation of canvas coordinate system:
143
        // 0 = on the positive x axis
144
        // -90 = on the positive y axis.
145
        // 90 = on the negaive y axis.
146
        // 180 or - 180 = on the negative x axis.
147
        this.startAngle = 0;
148
        this.tickRenderer = $.jqplot.PieTickRenderer;
149
        // Used as check for conditions where pie shouldn't be drawn.
150
        this._drawData = true;
151
        this._type = 'pie';
152
 
153
        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
154
        if (options.highlightMouseDown && options.highlightMouseOver == null) {
155
            options.highlightMouseOver = false;
156
        }
157
 
158
        $.extend(true, this, options);
159
 
160
        if (this.sliceMargin < 0) {
161
            this.sliceMargin = 0;
162
        }
163
 
164
        this._diameter = null;
165
        this._radius = null;
166
        // array of [start,end] angles arrays, one for each slice.  In radians.
167
        this._sliceAngles = [];
168
        // index of the currenty highlighted point, if any
169
        this._highlightedPoint = null;
170
 
171
        // set highlight colors if none provided
172
        if (this.highlightColors.length == 0) {
173
            for (var i=0; i<this.seriesColors.length; i++){
174
                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
175
                var newrgb = [rgba[0], rgba[1], rgba[2]];
176
                var sum = newrgb[0] + newrgb[1] + newrgb[2];
177
                for (var j=0; j<3; j++) {
178
                    // when darkening, lowest color component can be is 60.
179
                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
180
                    newrgb[j] = parseInt(newrgb[j], 10);
181
                }
182
                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
183
            }
184
        }
185
 
186
        this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
187
 
188
        plot.postParseOptionsHooks.addOnce(postParseOptions);
189
        plot.postInitHooks.addOnce(postInit);
190
        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
191
        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
192
        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
193
        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
194
        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
195
        plot.postDrawHooks.addOnce(postPlotDraw);
196
    };
197
 
198
    $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
199
        // set gridData property.  This will hold angle in radians of each data point.
200
        var stack = [];
201
        var td = [];
202
        var sa = this.startAngle/180*Math.PI;
203
        var tot = 0;
204
        // don't know if we have any valid data yet, so set plot to not draw.
205
        this._drawData = false;
206
        for (var i=0; i<this.data.length; i++){
207
            if (this.data[i][1] != 0) {
208
                // we have data, O.K. to draw.
209
                this._drawData = true;
210
            }
211
            stack.push(this.data[i][1]);
212
            td.push([this.data[i][0]]);
213
            if (i>0) {
214
                stack[i] += stack[i-1];
215
            }
216
            tot += this.data[i][1];
217
        }
218
        var fact = Math.PI*2/stack[stack.length - 1];
219
 
220
        for (var i=0; i<stack.length; i++) {
221
            td[i][1] = stack[i] * fact;
222
            td[i][2] = this.data[i][1]/tot;
223
        }
224
        this.gridData = td;
225
    };
226
 
227
    $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
228
        var stack = [];
229
        var td = [];
230
        var tot = 0;
231
        var sa = this.startAngle/180*Math.PI;
232
        // don't know if we have any valid data yet, so set plot to not draw.
233
        this._drawData = false;
234
        for (var i=0; i<data.length; i++){
235
            if (this.data[i][1] != 0) {
236
                // we have data, O.K. to draw.
237
                this._drawData = true;
238
            }
239
            stack.push(data[i][1]);
240
            td.push([data[i][0]]);
241
            if (i>0) {
242
                stack[i] += stack[i-1];
243
            }
244
            tot += data[i][1];
245
        }
246
        var fact = Math.PI*2/stack[stack.length - 1];
247
 
248
        for (var i=0; i<stack.length; i++) {
249
            td[i][1] = stack[i] * fact;
250
            td[i][2] = data[i][1]/tot;
251
        }
252
        return td;
253
    };
254
 
255
    function calcRadiusAdjustment(ang) {
256
        return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0);
257
    }
258
 
259
    function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) {
260
        var rprime = 0;
261
        var ang = ang2 - ang1;
262
        var absang = Math.abs(ang);
263
        var sm = sliceMargin;
264
        if (fill == false) {
265
            sm += lineWidth;
266
        }
267
 
268
        if (sm > 0 && absang > 0.01 && absang < 6.282) {
269
            rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang);
270
        }
271
 
272
        return rprime;
273
    }
274
 
275
    $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
276
        if (this._drawData) {
277
            var r = this._radius;
278
            var fill = this.fill;
279
            var lineWidth = this.lineWidth;
280
            var sm = this.sliceMargin;
281
            if (this.fill == false) {
282
                sm += this.lineWidth;
283
            }
284
            ctx.save();
285
            ctx.translate(this._center[0], this._center[1]);
286
 
287
            var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
288
 
289
            var transx = rprime * Math.cos((ang1 + ang2) / 2.0);
290
            var transy = rprime * Math.sin((ang1 + ang2) / 2.0);
291
 
292
            if ((ang2 - ang1) <= Math.PI) {
293
                r -= rprime;
294
            }
295
            else {
296
                r += rprime;
297
            }
298
 
299
            ctx.translate(transx, transy);
300
 
301
            if (isShadow) {
302
                for (var i=0, l=this.shadowDepth; i<l; i++) {
303
                    ctx.save();
304
                    ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
305
                    doDraw(r);
306
                }
307
                for (var i=0, l=this.shadowDepth; i<l; i++) {
308
                    ctx.restore();
309
                }
310
            }
311
 
312
            else {
313
                doDraw(r);
314
            }
315
            ctx.restore();
316
        }
317
 
318
        function doDraw (rad) {
319
            // Fix for IE and Chrome that can't seem to draw circles correctly.
320
            // ang2 should always be <= 2 pi since that is the way the data is converted.
321
            // 2Pi = 6.2831853, Pi = 3.1415927
322
             if (ang2 > 6.282 + this.startAngle) {
323
                ang2 = 6.282 + this.startAngle;
324
                if (ang1 > ang2) {
325
                    ang1 = 6.281 + this.startAngle;
326
                }
327
            }
328
            // Fix for IE, where it can't seem to handle 0 degree angles.  Also avoids
329
            // ugly line on unfilled pies.
330
            if (ang1 >= ang2) {
331
                return;
332
            }
333
 
334
            ctx.beginPath();
335
            ctx.fillStyle = color;
336
            ctx.strokeStyle = color;
337
            ctx.lineWidth = lineWidth;
338
            ctx.arc(0, 0, rad, ang1, ang2, false);
339
            ctx.lineTo(0,0);
340
            ctx.closePath();
341
 
342
            if (fill) {
343
                ctx.fill();
344
            }
345
            else {
346
                ctx.stroke();
347
            }
348
        }
349
    };
350
 
351
    // called with scope of series
352
    $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) {
353
        var i;
354
        var opts = (options != undefined) ? options : {};
355
        // offset and direction of offset due to legend placement
356
        var offx = 0;
357
        var offy = 0;
358
        var trans = 1;
359
        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
360
        if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
361
            var li = options.legendInfo;
362
            switch (li.location) {
363
                case 'nw':
364
                    offx = li.width + li.xoffset;
365
                    break;
366
                case 'w':
367
                    offx = li.width + li.xoffset;
368
                    break;
369
                case 'sw':
370
                    offx = li.width + li.xoffset;
371
                    break;
372
                case 'ne':
373
                    offx = li.width + li.xoffset;
374
                    trans = -1;
375
                    break;
376
                case 'e':
377
                    offx = li.width + li.xoffset;
378
                    trans = -1;
379
                    break;
380
                case 'se':
381
                    offx = li.width + li.xoffset;
382
                    trans = -1;
383
                    break;
384
                case 'n':
385
                    offy = li.height + li.yoffset;
386
                    break;
387
                case 's':
388
                    offy = li.height + li.yoffset;
389
                    trans = -1;
390
                    break;
391
                default:
392
                    break;
393
            }
394
        }
395
 
396
        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
397
        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
398
        var cw = ctx.canvas.width;
399
        var ch = ctx.canvas.height;
400
        var w = cw - offx - 2 * this.padding;
401
        var h = ch - offy - 2 * this.padding;
402
        var mindim = Math.min(w,h);
403
        var d = mindim;
404
 
405
        // Fixes issue #272.  Thanks hugwijst!
406
        // reset slice angles array.
407
        this._sliceAngles = [];
408
 
409
        var sm = this.sliceMargin;
410
        if (this.fill == false) {
411
            sm += this.lineWidth;
412
        }
413
 
414
        var rprime;
415
        var maxrprime = 0;
416
 
417
        var ang, ang1, ang2, shadowColor;
418
        var sa = this.startAngle / 180 * Math.PI;
419
 
420
        // have to pre-draw shadows, so loop throgh here and calculate some values also.
421
        for (var i=0, l=gd.length; i<l; i++) {
422
            ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
423
            ang2 = gd[i][1] + sa;
424
 
425
            this._sliceAngles.push([ang1, ang2]);
426
 
427
            rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
428
 
429
            if (Math.abs(ang2-ang1) > Math.PI) {
430
                maxrprime = Math.max(rprime, maxrprime);
431
            }
432
        }
433
 
434
        if (this.diameter != null && this.diameter > 0) {
435
            this._diameter = this.diameter - 2*maxrprime;
436
        }
437
        else {
438
            this._diameter = d - 2*maxrprime;
439
        }
440
 
441
        // Need to check for undersized pie.  This can happen if
442
        // plot area too small and legend is too big.
443
        if (this._diameter < 6) {
444
            $.jqplot.log('Diameter of pie too small, not rendering.');
445
            return;
446
        }
447
 
448
        var r = this._radius = this._diameter/2;
449
 
450
        this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)];
451
 
452
        if (this.shadow) {
453
            for (var i=0, l=gd.length; i<l; i++) {
454
                shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
455
                this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true);
456
            }
457
        }
458
 
459
        for (var i=0; i<gd.length; i++) {
460
 
461
            this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false);
462
 
463
            if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
464
                var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label;
465
 
466
                if (this.dataLabels == 'label') {
467
                    fstr = this.dataLabelFormatString || '%s';
468
                    label = $.jqplot.sprintf(fstr, gd[i][0]);
469
                }
470
                else if (this.dataLabels == 'value') {
471
                    fstr = this.dataLabelFormatString || '%d';
472
                    label = $.jqplot.sprintf(fstr, this.data[i][1]);
473
                }
474
                else if (this.dataLabels == 'percent') {
475
                    fstr = this.dataLabelFormatString || '%d%%';
476
                    label = $.jqplot.sprintf(fstr, gd[i][2]*100);
477
                }
478
                else if (this.dataLabels.constructor == Array) {
479
                    fstr = this.dataLabelFormatString || '%s';
480
                    label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
481
                }
482
 
483
                var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
484
 
485
                var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
486
                var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
487
 
488
                var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem);
489
                if (this.dataLabelCenterOn) {
490
                    x -= labelelem.width()/2;
491
                    y -= labelelem.height()/2;
492
                }
493
                else {
494
                    x -= labelelem.width() * Math.sin(avgang/2);
495
                    y -= labelelem.height()/2;
496
                }
497
                x = Math.round(x);
498
                y = Math.round(y);
499
                labelelem.css({left: x, top: y});
500
            }
501
        }
502
    };
503
 
504
    $.jqplot.PieAxisRenderer = function() {
505
        $.jqplot.LinearAxisRenderer.call(this);
506
    };
507
 
508
    $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
509
    $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
510
 
511
 
512
    // There are no traditional axes on a pie chart.  We just need to provide
513
    // dummy objects with properties so the plot will render.
514
    // called with scope of axis object.
515
    $.jqplot.PieAxisRenderer.prototype.init = function(options){
516
        //
517
        this.tickRenderer = $.jqplot.PieTickRenderer;
518
        $.extend(true, this, options);
519
        // I don't think I'm going to need _dataBounds here.
520
        // have to go Axis scaling in a way to fit chart onto plot area
521
        // and provide u2p and p2u functionality for mouse cursor, etc.
522
        // for convienence set _dataBounds to 0 and 100 and
523
        // set min/max to 0 and 100.
524
        this._dataBounds = {min:0, max:100};
525
        this.min = 0;
526
        this.max = 100;
527
        this.showTicks = false;
528
        this.ticks = [];
529
        this.showMark = false;
530
        this.show = false;
531
    };
532
 
533
 
534
 
535
 
536
    $.jqplot.PieLegendRenderer = function(){
537
        $.jqplot.TableLegendRenderer.call(this);
538
    };
539
 
540
    $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
541
    $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
542
 
543
    /**
544
     * Class: $.jqplot.PieLegendRenderer
545
     * Legend Renderer specific to pie plots.  Set by default
546
     * when user creates a pie plot.
547
     */
548
    $.jqplot.PieLegendRenderer.prototype.init = function(options) {
549
        // Group: Properties
550
        //
551
        // prop: numberRows
552
        // Maximum number of rows in the legend.  0 or null for unlimited.
553
        this.numberRows = null;
554
        // prop: numberColumns
555
        // Maximum number of columns in the legend.  0 or null for unlimited.
556
        this.numberColumns = null;
557
        $.extend(true, this, options);
558
    };
559
 
560
    // called with context of legend
561
    $.jqplot.PieLegendRenderer.prototype.draw = function() {
562
        var legend = this;
563
        if (this.show) {
564
            var series = this._series;
565
 
566
 
567
            this._elem = $(document.createElement('table'));
568
            this._elem.addClass('jqplot-table-legend');
569
 
570
            var ss = {position:'absolute'};
571
            if (this.background) {
572
                ss['background'] = this.background;
573
            }
574
            if (this.border) {
575
                ss['border'] = this.border;
576
            }
577
            if (this.fontSize) {
578
                ss['fontSize'] = this.fontSize;
579
            }
580
            if (this.fontFamily) {
581
                ss['fontFamily'] = this.fontFamily;
582
            }
583
            if (this.textColor) {
584
                ss['textColor'] = this.textColor;
585
            }
586
            if (this.marginTop != null) {
587
                ss['marginTop'] = this.marginTop;
588
            }
589
            if (this.marginBottom != null) {
590
                ss['marginBottom'] = this.marginBottom;
591
            }
592
            if (this.marginLeft != null) {
593
                ss['marginLeft'] = this.marginLeft;
594
            }
595
            if (this.marginRight != null) {
596
                ss['marginRight'] = this.marginRight;
597
            }
598
 
599
            this._elem.css(ss);
600
 
601
            // Pie charts legends don't go by number of series, but by number of data points
602
            // in the series.  Refactor things here for that.
603
 
604
            var pad = false,
605
                reverse = false,
606
                nr,
607
                nc;
608
            var s = series[0];
609
            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
610
 
611
            if (s.show) {
612
                var pd = s.data;
613
                if (this.numberRows) {
614
                    nr = this.numberRows;
615
                    if (!this.numberColumns){
616
                        nc = Math.ceil(pd.length/nr);
617
                    }
618
                    else{
619
                        nc = this.numberColumns;
620
                    }
621
                }
622
                else if (this.numberColumns) {
623
                    nc = this.numberColumns;
624
                    nr = Math.ceil(pd.length/this.numberColumns);
625
                }
626
                else {
627
                    nr = pd.length;
628
                    nc = 1;
629
                }
630
 
631
                var i, j;
632
                var tr, td1, td2;
633
                var lt, rs, color;
634
                var idx = 0;
635
                var div0, div1;
636
 
637
                for (i=0; i<nr; i++) {
638
                    tr = $(document.createElement('tr'));
639
                    tr.addClass('jqplot-table-legend');
640
 
641
                    if (reverse){
642
                        tr.prependTo(this._elem);
643
                    }
644
 
645
                    else{
646
                        tr.appendTo(this._elem);
647
                    }
648
 
649
                    for (j=0; j<nc; j++) {
650
                        if (idx < pd.length){
651
                            lt = this.labels[idx] || pd[idx][0].toString();
652
                            color = colorGenerator.next();
653
                            if (!reverse){
654
                                if (i>0){
655
                                    pad = true;
656
                                }
657
                                else{
658
                                    pad = false;
659
                                }
660
                            }
661
                            else{
662
                                if (i == nr -1){
663
                                    pad = false;
664
                                }
665
                                else{
666
                                    pad = true;
667
                                }
668
                            }
669
                            rs = (pad) ? this.rowSpacing : '0';
670
 
671
 
672
 
673
                            td1 = $(document.createElement('td'));
674
                            td1.addClass('jqplot-table-legend jqplot-table-legend-swatch');
675
                            td1.css({textAlign: 'center', paddingTop: rs});
676
 
677
                            div0 = $(document.createElement('div'));
678
                            div0.addClass('jqplot-table-legend-swatch-outline');
679
                            div1 = $(document.createElement('div'));
680
                            div1.addClass('jqplot-table-legend-swatch');
681
                            div1.css({backgroundColor: color, borderColor: color});
682
                            td1.append(div0.append(div1));
683
 
684
                            td2 = $(document.createElement('td'));
685
                            td2.addClass('jqplot-table-legend jqplot-table-legend-label');
686
                            td2.css('paddingTop', rs);
687
 
688
                            if (this.escapeHtml){
689
                                td2.text(lt);
690
                            }
691
                            else {
692
                                td2.html(lt);
693
                            }
694
                            if (reverse) {
695
                                td2.prependTo(tr);
696
                                td1.prependTo(tr);
697
                            }
698
                            else {
699
                                td1.appendTo(tr);
700
                                td2.appendTo(tr);
701
                            }
702
                            pad = true;
703
                        }
704
                        idx++;
705
                    }
706
                }
707
            }
708
        }
709
        return this._elem;
710
    };
711
 
712
    $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) {
713
        if (neighbor) {
714
            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
715
            plot.target.trigger('jqplotDataMouseOver', ins);
716
            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
717
                plot.target.trigger('jqplotDataHighlight', ins);
718
                highlight (plot, ins[0], ins[1]);
719
            }
720
        }
721
        else if (neighbor == null) {
722
            unhighlight (plot);
723
        }
724
    };
725
 
726
 
727
    // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
728
 
729
    // setup default renderers for axes and legend so user doesn't have to
730
    // called with scope of plot
731
    function preInit(target, data, options) {
732
        options = options || {};
733
        options.axesDefaults = options.axesDefaults || {};
734
        options.legend = options.legend || {};
735
        options.seriesDefaults = options.seriesDefaults || {};
736
        // only set these if there is a pie series
737
        var setopts = false;
738
        if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
739
            setopts = true;
740
        }
741
        else if (options.series) {
742
            for (var i=0; i < options.series.length; i++) {
743
                if (options.series[i].renderer == $.jqplot.PieRenderer) {
744
                    setopts = true;
745
                }
746
            }
747
        }
748
 
749
        if (setopts) {
750
            options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
751
            options.legend.renderer = $.jqplot.PieLegendRenderer;
752
            options.legend.preDraw = true;
753
            options.seriesDefaults.pointLabels = {show: false};
754
        }
755
    }
756
 
757
    function postInit(target, data, options) {
758
        for (var i=0; i<this.series.length; i++) {
759
            if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) {
760
                // don't allow mouseover and mousedown at same time.
761
                if (this.series[i].highlightMouseOver) {
762
                    this.series[i].highlightMouseDown = false;
763
                }
764
            }
765
        }
766
    }
767
 
768
    // called with scope of plot
769
    function postParseOptions(options) {
770
        for (var i=0; i<this.series.length; i++) {
771
            this.series[i].seriesColors = this.seriesColors;
772
            this.series[i].colorGenerator = $.jqplot.colorGenerator;
773
        }
774
    }
775
 
776
    function highlight (plot, sidx, pidx) {
777
        var s = plot.series[sidx];
778
        var canvas = plot.plugins.pieRenderer.highlightCanvas;
779
        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
780
        s._highlightedPoint = pidx;
781
        plot.plugins.pieRenderer.highlightedSeriesIndex = sidx;
782
        s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false);
783
    }
784
 
785
    function unhighlight (plot) {
786
        var canvas = plot.plugins.pieRenderer.highlightCanvas;
787
        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
788
        for (var i=0; i<plot.series.length; i++) {
789
            plot.series[i]._highlightedPoint = null;
790
        }
791
        plot.plugins.pieRenderer.highlightedSeriesIndex = null;
792
        plot.target.trigger('jqplotDataUnhighlight');
793
    }
794
 
795
    function handleMove(ev, gridpos, datapos, neighbor, plot) {
796
        if (neighbor) {
797
            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
798
            var evt1 = jQuery.Event('jqplotDataMouseOver');
799
            evt1.pageX = ev.pageX;
800
            evt1.pageY = ev.pageY;
801
            plot.target.trigger(evt1, ins);
802
            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
803
                var evt = jQuery.Event('jqplotDataHighlight');
804
                evt.which = ev.which;
805
                evt.pageX = ev.pageX;
806
                evt.pageY = ev.pageY;
807
                plot.target.trigger(evt, ins);
808
                highlight (plot, ins[0], ins[1]);
809
            }
810
        }
811
        else if (neighbor == null) {
812
            unhighlight (plot);
813
        }
814
    }
815
 
816
    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
817
        if (neighbor) {
818
            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
819
            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
820
                var evt = jQuery.Event('jqplotDataHighlight');
821
                evt.which = ev.which;
822
                evt.pageX = ev.pageX;
823
                evt.pageY = ev.pageY;
824
                plot.target.trigger(evt, ins);
825
                highlight (plot, ins[0], ins[1]);
826
            }
827
        }
828
        else if (neighbor == null) {
829
            unhighlight (plot);
830
        }
831
    }
832
 
833
    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
834
        var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
835
        if (idx != null && plot.series[idx].highlightMouseDown) {
836
            unhighlight(plot);
837
        }
838
    }
839
 
840
    function handleClick(ev, gridpos, datapos, neighbor, plot) {
841
        if (neighbor) {
842
            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
843
            var evt = jQuery.Event('jqplotDataClick');
844
            evt.which = ev.which;
845
            evt.pageX = ev.pageX;
846
            evt.pageY = ev.pageY;
847
            plot.target.trigger(evt, ins);
848
        }
849
    }
850
 
851
    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
852
        if (neighbor) {
853
            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
854
            var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
855
            if (idx != null && plot.series[idx].highlightMouseDown) {
856
                unhighlight(plot);
857
            }
858
            var evt = jQuery.Event('jqplotDataRightClick');
859
            evt.which = ev.which;
860
            evt.pageX = ev.pageX;
861
            evt.pageY = ev.pageY;
862
            plot.target.trigger(evt, ins);
863
        }
864
    }
865
 
866
    // called within context of plot
867
    // create a canvas which we can draw on.
868
    // insert it before the eventCanvas, so eventCanvas will still capture events.
869
    function postPlotDraw() {
870
        // Memory Leaks patch
871
        if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) {
872
            this.plugins.pieRenderer.highlightCanvas.resetCanvas();
873
            this.plugins.pieRenderer.highlightCanvas = null;
874
        }
875
 
876
        this.plugins.pieRenderer = {highlightedSeriesIndex:null};
877
        this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
878
 
879
        // do we have any data labels?  if so, put highlight canvas before those
880
        var labels = $(this.targetId+' .jqplot-data-label');
881
        if (labels.length) {
882
            $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
883
        }
884
        // else put highlight canvas before event canvas.
885
        else {
886
            this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
887
        }
888
 
889
        var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
890
        this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
891
    }
892
 
893
    $.jqplot.preInitHooks.push(preInit);
894
 
895
    $.jqplot.PieTickRenderer = function() {
896
        $.jqplot.AxisTickRenderer.call(this);
897
    };
898
 
899
    $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
900
    $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
901
 
902
})(jQuery);
903
 
904