Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
9 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.MekkoRenderer
34
     * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
35
     * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts.  The mekko renderer
36
     * overrides the default legend renderer with its own $.jqplot.MekkoLegendRenderer
37
     * which allows more flexibility to specify number of rows and columns in the legend.
38
     *
39
     * Data is specified per bar in the chart.  You can specify data as an array of y values, or as
40
     * an array of [label, value] pairs.  Note that labels are used only on the first series.
41
     * Labels on subsequent series are ignored:
42
     *
43
     * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
44
     * > bar2 = [15,6,9,13,6];
45
     * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
46
     *
47
     * If you want to place labels for each bar under the axis, you use the barLabels option on
48
     * the axes.  The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
49
     *
50
     * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
51
     * > axes:{xaxis:{barLabels:barLabels}}
52
     *
53
     */
54
 
55
 
56
    $.jqplot.MekkoRenderer = function(){
57
        this.shapeRenderer = new $.jqplot.ShapeRenderer();
58
        // prop: borderColor
59
        // color of the borders between areas on the chart
60
        this.borderColor = null;
61
        // prop: showBorders
62
        // True to draw borders lines between areas on the chart.
63
        // False will draw borders lines with the same color as the area.
64
        this.showBorders = true;
65
    };
66
 
67
    // called with scope of series.
68
    $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
69
        this.fill = false;
70
        this.fillRect = true;
71
        this.strokeRect = true;
72
        this.shadow = false;
73
        // width of bar on x axis.
74
        this._xwidth = 0;
75
        this._xstart = 0;
76
        $.extend(true, this.renderer, options);
77
        // set the shape renderer options
78
        var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
79
        this.renderer.shapeRenderer.init(opts);
80
        plot.axes.x2axis._series.push(this);
81
        this._type = 'mekko';
82
    };
83
 
84
    // Method: setGridData
85
    // converts the user data values to grid coordinates and stores them
86
    // in the gridData array.  Will convert user data into appropriate
87
    // rectangles.
88
    // Called with scope of a series.
89
    $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
90
        // recalculate the grid data
91
        var xp = this._xaxis.series_u2p;
92
        var yp = this._yaxis.series_u2p;
93
        var data = this._plotData;
94
        this.gridData = [];
95
        // figure out width on x axis.
96
        // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
97
        this._xwidth = xp(this._sumy) - xp(0);
98
        if (this.index>0) {
99
            this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
100
        }
101
        var totheight = this.canvas.getHeight();
102
        var sumy = 0;
103
        var cury;
104
        var curheight;
105
        for (var i=0; i<data.length; i++) {
106
            if (data[i] != null) {
107
                sumy += data[i][1];
108
                cury = totheight - (sumy / this._sumy * totheight);
109
                curheight = data[i][1] / this._sumy * totheight;
110
                this.gridData.push([this._xstart, cury, this._xwidth, curheight]);
111
            }
112
        }
113
    };
114
 
115
    // Method: makeGridData
116
    // converts any arbitrary data values to grid coordinates and
117
    // returns them.  This method exists so that plugins can use a series'
118
    // linerenderer to generate grid data points without overwriting the
119
    // grid data associated with that series.
120
    // Called with scope of a series.
121
    $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) {
122
        // recalculate the grid data
123
        // figure out width on x axis.
124
        var xp = this._xaxis.series_u2p;
125
        var totheight = this.canvas.getHeight();
126
        var sumy = 0;
127
        var cury;
128
        var curheight;
129
        var gd = [];
130
        for (var i=0; i<data.length; i++) {
131
            if (data[i] != null) {
132
                sumy += data[i][1];
133
                cury = totheight - (sumy / this._sumy * totheight);
134
                curheight = data[i][1] / this._sumy * totheight;
135
                gd.push([this._xstart, cury, this._xwidth, curheight]);
136
            }
137
        }
138
        return gd;
139
    };
140
 
141
 
142
    // called within scope of series.
143
    $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) {
144
        var i;
145
        var opts = (options != undefined) ? options : {};
146
        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
147
        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
148
        ctx.save();
149
        if (gd.length) {
150
            if (showLine) {
151
                for (i=0; i<gd.length; i++){
152
                    opts.fillStyle = colorGenerator.next();
153
                    if (this.renderer.showBorders) {
154
                        opts.strokeStyle = this.renderer.borderColor;
155
                    }
156
                    else {
157
                        opts.strokeStyle = opts.fillStyle;
158
                    }
159
                    this.renderer.shapeRenderer.draw(ctx, gd[i], opts);
160
                }
161
            }
162
        }
163
 
164
        ctx.restore();
165
    };
166
 
167
    $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) {
168
        // This is a no-op, no shadows on mekko charts.
169
    };
170
 
171
    /**
172
     * Class: $.jqplot.MekkoLegendRenderer
173
     * Legend renderer used by mekko charts with options for
174
     * controlling number or rows and columns as well as placement
175
     * outside of plot area.
176
     *
177
     */
178
    $.jqplot.MekkoLegendRenderer = function(){
179
        //
180
    };
181
 
182
    $.jqplot.MekkoLegendRenderer.prototype.init = function(options) {
183
        // prop: numberRows
184
        // Maximum number of rows in the legend.  0 or null for unlimited.
185
        this.numberRows = null;
186
        // prop: numberColumns
187
        // Maximum number of columns in the legend.  0 or null for unlimited.
188
        this.numberColumns = null;
189
        // this will override the placement option on the Legend object
190
        this.placement = "outside";
191
        $.extend(true, this, options);
192
    };
193
 
194
    // called with scope of legend
195
    $.jqplot.MekkoLegendRenderer.prototype.draw = function() {
196
        var legend = this;
197
        if (this.show) {
198
            var series = this._series;
199
            var ss = 'position:absolute;';
200
            ss += (this.background) ? 'background:'+this.background+';' : '';
201
            ss += (this.border) ? 'border:'+this.border+';' : '';
202
            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
203
            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
204
            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
205
            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
206
            // Mekko charts  legends don't go by number of series, but by number of data points
207
            // in the series.  Refactor things here for that.
208
 
209
            var pad = false,
210
                reverse = true,    // mekko charts are always stacked, so reverse
211
                nr, nc;
212
            var s = series[0];
213
            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
214
 
215
            if (s.show) {
216
                var pd = s.data;
217
                if (this.numberRows) {
218
                    nr = this.numberRows;
219
                    if (!this.numberColumns){
220
                        nc = Math.ceil(pd.length/nr);
221
                    }
222
                    else{
223
                        nc = this.numberColumns;
224
                    }
225
                }
226
                else if (this.numberColumns) {
227
                    nc = this.numberColumns;
228
                    nr = Math.ceil(pd.length/this.numberColumns);
229
                }
230
                else {
231
                    nr = pd.length;
232
                    nc = 1;
233
                }
234
 
235
                var i, j, tr, td1, td2, lt, rs, color;
236
                var idx = 0;
237
 
238
                for (i=0; i<nr; i++) {
239
                    if (reverse){
240
                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
241
                    }
242
                    else{
243
                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
244
                    }
245
                    for (j=0; j<nc; j++) {
246
                        if (idx < pd.length) {
247
                            lt = this.labels[idx] || pd[idx][0].toString();
248
                            color = colorGenerator.next();
249
                            if (!reverse){
250
                                if (i>0){
251
                                    pad = true;
252
                                }
253
                                else{
254
                                    pad = false;
255
                                }
256
                            }
257
                            else{
258
                                if (i == nr -1){
259
                                    pad = false;
260
                                }
261
                                else{
262
                                    pad = true;
263
                                }
264
                            }
265
                            rs = (pad) ? this.rowSpacing : '0';
266
 
267
                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
268
                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
269
                                '</div></td>');
270
                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
271
                            if (this.escapeHtml){
272
                                td2.text(lt);
273
                            }
274
                            else {
275
                                td2.html(lt);
276
                            }
277
                            if (reverse) {
278
                                td2.prependTo(tr);
279
                                td1.prependTo(tr);
280
                            }
281
                            else {
282
                                td1.appendTo(tr);
283
                                td2.appendTo(tr);
284
                            }
285
                            pad = true;
286
                        }
287
                        idx++;
288
                    }
289
                }
290
 
291
                tr = null;
292
                td1 = null;
293
                td2 = null;
294
            }
295
        }
296
        return this._elem;
297
    };
298
 
299
    $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
300
        if (this.show) {
301
            // fake a grid for positioning
302
            var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
303
            if (this.placement == 'insideGrid') {
304
                switch (this.location) {
305
                    case 'nw':
306
                        var a = grid._left + this.xoffset;
307
                        var b = grid._top + this.yoffset;
308
                        this._elem.css('left', a);
309
                        this._elem.css('top', b);
310
                        break;
311
                    case 'n':
312
                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
313
                        var b = grid._top + this.yoffset;
314
                        this._elem.css('left', a);
315
                        this._elem.css('top', b);
316
                        break;
317
                    case 'ne':
318
                        var a = offsets.right + this.xoffset;
319
                        var b = grid._top + this.yoffset;
320
                        this._elem.css({right:a, top:b});
321
                        break;
322
                    case 'e':
323
                        var a = offsets.right + this.xoffset;
324
                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
325
                        this._elem.css({right:a, top:b});
326
                        break;
327
                    case 'se':
328
                        var a = offsets.right + this.xoffset;
329
                        var b = offsets.bottom + this.yoffset;
330
                        this._elem.css({right:a, bottom:b});
331
                        break;
332
                    case 's':
333
                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
334
                        var b = offsets.bottom + this.yoffset;
335
                        this._elem.css({left:a, bottom:b});
336
                        break;
337
                    case 'sw':
338
                        var a = grid._left + this.xoffset;
339
                        var b = offsets.bottom + this.yoffset;
340
                        this._elem.css({left:a, bottom:b});
341
                        break;
342
                    case 'w':
343
                        var a = grid._left + this.xoffset;
344
                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
345
                        this._elem.css({left:a, top:b});
346
                        break;
347
                    default:  // same as 'se'
348
                        var a = grid._right - this.xoffset;
349
                        var b = grid._bottom + this.yoffset;
350
                        this._elem.css({right:a, bottom:b});
351
                        break;
352
                }
353
 
354
            }
355
            else {
356
                switch (this.location) {
357
                    case 'nw':
358
                        var a = this._plotDimensions.width - grid._left + this.xoffset;
359
                        var b = grid._top + this.yoffset;
360
                        this._elem.css('right', a);
361
                        this._elem.css('top', b);
362
                        break;
363
                    case 'n':
364
                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
365
                        var b = this._plotDimensions.height - grid._top + this.yoffset;
366
                        this._elem.css('left', a);
367
                        this._elem.css('bottom', b);
368
                        break;
369
                    case 'ne':
370
                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
371
                        var b = grid._top + this.yoffset;
372
                        this._elem.css({left:a, top:b});
373
                        break;
374
                    case 'e':
375
                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
376
                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
377
                        this._elem.css({left:a, top:b});
378
                        break;
379
                    case 'se':
380
                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
381
                        var b = offsets.bottom + this.yoffset;
382
                        this._elem.css({left:a, bottom:b});
383
                        break;
384
                    case 's':
385
                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
386
                        var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
387
                        this._elem.css({left:a, top:b});
388
                        break;
389
                    case 'sw':
390
                        var a = this._plotDimensions.width - grid._left + this.xoffset;
391
                        var b = offsets.bottom + this.yoffset;
392
                        this._elem.css({right:a, bottom:b});
393
                        break;
394
                    case 'w':
395
                        var a = this._plotDimensions.width - grid._left + this.xoffset;
396
                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
397
                        this._elem.css({right:a, top:b});
398
                        break;
399
                    default:  // same as 'se'
400
                        var a = grid._right - this.xoffset;
401
                        var b = grid._bottom + this.yoffset;
402
                        this._elem.css({right:a, bottom:b});
403
                        break;
404
                }
405
            }
406
        }
407
    };
408
 
409
    // setup default renderers for axes and legend so user doesn't have to
410
    // called with scope of plot
411
    function preInit(target, data, options) {
412
        options = options || {};
413
        options.axesDefaults = options.axesDefaults || {};
414
        options.legend = options.legend || {};
415
        options.seriesDefaults = options.seriesDefaults || {};
416
        var setopts = false;
417
        if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
418
            setopts = true;
419
        }
420
        else if (options.series) {
421
            for (var i=0; i < options.series.length; i++) {
422
                if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
423
                    setopts = true;
424
                }
425
            }
426
        }
427
 
428
        if (setopts) {
429
            options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
430
            options.legend.renderer = $.jqplot.MekkoLegendRenderer;
431
            options.legend.preDraw = true;
432
        }
433
    }
434
 
435
    $.jqplot.preInitHooks.push(preInit);
436
 
437
})(jQuery);