Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
776 lars 1
/*
2
Axis Labels Plugin for flot.
3
http://github.com/markrcote/flot-axislabels
4
Original code is Copyright (c) 2010 Xuan Luo.
5
Original code was released under the GPLv3 license by Xuan Luo, September 2010.
6
Original code was rereleased under the MIT license by Xuan Luo, April 2012.
7
Permission is hereby granted, free of charge, to any person obtaining
8
a copy of this software and associated documentation files (the
9
"Software"), to deal in the Software without restriction, including
10
without limitation the rights to use, copy, modify, merge, publish,
11
distribute, sublicense, and/or sell copies of the Software, and to
12
permit persons to whom the Software is furnished to do so, subject to
13
the following conditions:
14
The above copyright notice and this permission notice shall be
15
included in all copies or substantial portions of the Software.
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 */
24
 
25
(function ($) {
26
    var options = {
27
      axisLabels: {
28
        show: true
29
      }
30
    };
31
 
32
    function canvasSupported() {
33
        return !!document.createElement('canvas').getContext;
34
    }
35
 
36
    function canvasTextSupported() {
37
        if (!canvasSupported()) {
38
            return false;
39
        }
40
        var dummy_canvas = document.createElement('canvas');
41
        var context = dummy_canvas.getContext('2d');
42
        return typeof context.fillText == 'function';
43
    }
44
 
45
    function css3TransitionSupported() {
46
        var div = document.createElement('div');
47
        return typeof div.style.MozTransition != 'undefined'    // Gecko
48
            || typeof div.style.OTransition != 'undefined'      // Opera
49
            || typeof div.style.webkitTransition != 'undefined' // WebKit
50
            || typeof div.style.transition != 'undefined';
51
    }
52
 
53
 
54
    function AxisLabel(axisName, position, padding, plot, opts) {
55
        this.axisName = axisName;
56
        this.position = position;
57
        this.padding = padding;
58
        this.plot = plot;
59
        this.opts = opts;
60
        this.width = 0;
61
        this.height = 0;
62
    }
63
 
64
    AxisLabel.prototype.cleanup = function() {
65
    };
66
 
67
 
68
    CanvasAxisLabel.prototype = new AxisLabel();
69
    CanvasAxisLabel.prototype.constructor = CanvasAxisLabel;
70
    function CanvasAxisLabel(axisName, position, padding, plot, opts) {
71
        AxisLabel.prototype.constructor.call(this, axisName, position, padding,
72
                                             plot, opts);
73
    }
74
 
75
    CanvasAxisLabel.prototype.calculateSize = function() {
76
        if (!this.opts.axisLabelFontSizePixels)
77
            this.opts.axisLabelFontSizePixels = 14;
78
        if (!this.opts.axisLabelFontFamily)
79
            this.opts.axisLabelFontFamily = 'sans-serif';
80
 
81
        var textWidth = this.opts.axisLabelFontSizePixels + this.padding;
82
        var textHeight = this.opts.axisLabelFontSizePixels + this.padding;
83
        if (this.position == 'left' || this.position == 'right') {
84
            this.width = this.opts.axisLabelFontSizePixels + this.padding;
85
            this.height = 0;
86
        } else {
87
            this.width = 0;
88
            this.height = this.opts.axisLabelFontSizePixels + this.padding;
89
        }
90
    };
91
 
92
    CanvasAxisLabel.prototype.draw = function(box) {
93
        if (!this.opts.axisLabelColour)
94
            this.opts.axisLabelColour = 'black';
95
        var ctx = this.plot.getCanvas().getContext('2d');
96
        ctx.save();
97
        ctx.font = this.opts.axisLabelFontSizePixels + 'px ' +
98
            this.opts.axisLabelFontFamily;
99
        ctx.fillStyle = this.opts.axisLabelColour;
100
        var width = ctx.measureText(this.opts.axisLabel).width;
101
        var height = this.opts.axisLabelFontSizePixels;
102
        var x, y, angle = 0;
103
        if (this.position == 'top') {
104
            x = box.left + box.width/2 - width/2;
105
            y = box.top + height*0.72;
106
        } else if (this.position == 'bottom') {
107
            x = box.left + box.width/2 - width/2;
108
            y = box.top + box.height - height*0.72;
109
        } else if (this.position == 'left') {
110
            x = box.left + height*0.72;
111
            y = box.height/2 + box.top + width/2;
112
            angle = -Math.PI/2;
113
        } else if (this.position == 'right') {
114
            x = box.left + box.width - height*0.72;
115
            y = box.height/2 + box.top - width/2;
116
            angle = Math.PI/2;
117
        }
118
        ctx.translate(x, y);
119
        ctx.rotate(angle);
120
        ctx.fillText(this.opts.axisLabel, 0, 0);
121
        ctx.restore();
122
    };
123
 
124
 
125
    HtmlAxisLabel.prototype = new AxisLabel();
126
    HtmlAxisLabel.prototype.constructor = HtmlAxisLabel;
127
    function HtmlAxisLabel(axisName, position, padding, plot, opts) {
128
        AxisLabel.prototype.constructor.call(this, axisName, position,
129
                                             padding, plot, opts);
130
        this.elem = null;
131
    }
132
 
133
    HtmlAxisLabel.prototype.calculateSize = function() {
134
        var elem = $('<div class="axisLabels" style="position:absolute;">' +
135
                     this.opts.axisLabel + '</div>');
136
        this.plot.getPlaceholder().append(elem);
137
        // store height and width of label itself, for use in draw()
138
        this.labelWidth = elem.outerWidth(true);
139
        this.labelHeight = elem.outerHeight(true);
140
        elem.remove();
141
 
142
        this.width = this.height = 0;
143
        if (this.position == 'left' || this.position == 'right') {
144
            this.width = this.labelWidth + this.padding;
145
        } else {
146
            this.height = this.labelHeight + this.padding;
147
        }
148
    };
149
 
150
    HtmlAxisLabel.prototype.cleanup = function() {
151
        if (this.elem) {
152
            this.elem.remove();
153
        }
154
    };
155
 
156
    HtmlAxisLabel.prototype.draw = function(box) {
157
        this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove();
158
        this.elem = $('<div id="' + this.axisName +
159
                      'Label" " class="axisLabels" style="position:absolute;">'
160
                      + this.opts.axisLabel + '</div>');
161
        this.plot.getPlaceholder().append(this.elem);
162
        if (this.position == 'top') {
163
            this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 +
164
                          'px');
165
            this.elem.css('top', box.top + 'px');
166
        } else if (this.position == 'bottom') {
167
            this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 +
168
                          'px');
169
            this.elem.css('top', box.top + box.height - this.labelHeight +
170
                          'px');
171
        } else if (this.position == 'left') {
172
            this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 +
173
                          'px');
174
            this.elem.css('left', box.left + 'px');
175
        } else if (this.position == 'right') {
176
            this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 +
177
                          'px');
178
            this.elem.css('left', box.left + box.width - this.labelWidth +
179
                          'px');
180
        }
181
    };
182
 
183
 
184
    CssTransformAxisLabel.prototype = new HtmlAxisLabel();
185
    CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel;
186
    function CssTransformAxisLabel(axisName, position, padding, plot, opts) {
187
        HtmlAxisLabel.prototype.constructor.call(this, axisName, position,
188
                                                 padding, plot, opts);
189
    }
190
 
191
    CssTransformAxisLabel.prototype.calculateSize = function() {
192
        HtmlAxisLabel.prototype.calculateSize.call(this);
193
        this.width = this.height = 0;
194
        if (this.position == 'left' || this.position == 'right') {
195
            this.width = this.labelHeight + this.padding;
196
        } else {
197
            this.height = this.labelHeight + this.padding;
198
        }
199
    };
200
 
201
    CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
202
        var stransforms = {
203
            '-moz-transform': '',
204
            '-webkit-transform': '',
205
            '-o-transform': '',
206
            '-ms-transform': ''
207
        };
208
        if (x != 0 || y != 0) {
209
            var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)';
210
            stransforms['-moz-transform'] += stdTranslate;
211
            stransforms['-webkit-transform'] += stdTranslate;
212
            stransforms['-o-transform'] += stdTranslate;
213
            stransforms['-ms-transform'] += stdTranslate;
214
        }
215
        if (degrees != 0) {
216
            var rotation = degrees / 90;
217
            var stdRotate = ' rotate(' + degrees + 'deg)';
218
            stransforms['-moz-transform'] += stdRotate;
219
            stransforms['-webkit-transform'] += stdRotate;
220
            stransforms['-o-transform'] += stdRotate;
221
            stransforms['-ms-transform'] += stdRotate;
222
        }
223
        var s = 'top: 0; left: 0; ';
224
        for (var prop in stransforms) {
225
            if (stransforms[prop]) {
226
                s += prop + ':' + stransforms[prop] + ';';
227
            }
228
        }
229
        s += ';';
230
        return s;
231
    };
232
 
233
    CssTransformAxisLabel.prototype.calculateOffsets = function(box) {
234
        var offsets = { x: 0, y: 0, degrees: 0 };
235
        if (this.position == 'bottom') {
236
            offsets.x = box.left + box.width/2 - this.labelWidth/2;
237
            offsets.y = box.top + box.height - this.labelHeight;
238
        } else if (this.position == 'top') {
239
            offsets.x = box.left + box.width/2 - this.labelWidth/2;
240
            offsets.y = box.top;
241
        } else if (this.position == 'left') {
242
            offsets.degrees = -90;
243
            offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2;
244
            offsets.y = box.height/2 + box.top;
245
        } else if (this.position == 'right') {
246
            offsets.degrees = 90;
247
            offsets.x = box.left + box.width - this.labelWidth/2
248
                        - this.labelHeight/2;
249
            offsets.y = box.height/2 + box.top;
250
        }
251
        offsets.x = Math.round(offsets.x);
252
        offsets.y = Math.round(offsets.y);
253
 
254
        return offsets;
255
    };
256
 
257
    CssTransformAxisLabel.prototype.draw = function(box) {
258
        this.plot.getPlaceholder().find("." + this.axisName + "Label").remove();
259
        var offsets = this.calculateOffsets(box);
260
        this.elem = $('<div class="axisLabels ' + this.axisName +
261
                      'Label" style="position:absolute; ' +
262
                      this.transforms(offsets.degrees, offsets.x, offsets.y) +
263
                      '">' + this.opts.axisLabel + '</div>');
264
        this.plot.getPlaceholder().append(this.elem);
265
    };
266
 
267
 
268
    IeTransformAxisLabel.prototype = new CssTransformAxisLabel();
269
    IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel;
270
    function IeTransformAxisLabel(axisName, position, padding, plot, opts) {
271
        CssTransformAxisLabel.prototype.constructor.call(this, axisName,
272
                                                         position, padding,
273
                                                         plot, opts);
274
        this.requiresResize = false;
275
    }
276
 
277
    IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
278
        // I didn't feel like learning the crazy Matrix stuff, so this uses
279
        // a combination of the rotation transform and CSS positioning.
280
        var s = '';
281
        if (degrees != 0) {
282
            var rotation = degrees/90;
283
            while (rotation < 0) {
284
                rotation += 4;
285
            }
286
            s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); ';
287
            // see below
288
            this.requiresResize = (this.position == 'right');
289
        }
290
        if (x != 0) {
291
            s += 'left: ' + x + 'px; ';
292
        }
293
        if (y != 0) {
294
            s += 'top: ' + y + 'px; ';
295
        }
296
        return s;
297
    };
298
 
299
    IeTransformAxisLabel.prototype.calculateOffsets = function(box) {
300
        var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call(
301
                          this, box);
302
        // adjust some values to take into account differences between
303
        // CSS and IE rotations.
304
        if (this.position == 'top') {
305
            // FIXME: not sure why, but placing this exactly at the top causes
306
            // the top axis label to flip to the bottom...
307
            offsets.y = box.top + 1;
308
        } else if (this.position == 'left') {
309
            offsets.x = box.left;
310
            offsets.y = box.height/2 + box.top - this.labelWidth/2;
311
        } else if (this.position == 'right') {
312
            offsets.x = box.left + box.width - this.labelHeight;
313
            offsets.y = box.height/2 + box.top - this.labelWidth/2;
314
        }
315
        return offsets;
316
    };
317
 
318
    IeTransformAxisLabel.prototype.draw = function(box) {
319
        CssTransformAxisLabel.prototype.draw.call(this, box);
320
        if (this.requiresResize) {
321
            this.elem = this.plot.getPlaceholder().find("." + this.axisName +
322
                                                        "Label");
323
            // Since we used CSS positioning instead of transforms for
324
            // translating the element, and since the positioning is done
325
            // before any rotations, we have to reset the width and height
326
            // in case the browser wrapped the text (specifically for the
327
            // y2axis).
328
            this.elem.css('width', this.labelWidth);
329
            this.elem.css('height', this.labelHeight);
330
        }
331
    };
332
 
333
 
334
    function init(plot) {
335
        plot.hooks.processOptions.push(function (plot, options) {
336
 
337
            if (!options.axisLabels.show)
338
                return;
339
 
340
            // This is kind of a hack. There are no hooks in Flot between
341
            // the creation and measuring of the ticks (setTicks, measureTickLabels
342
            // in setupGrid() ) and the drawing of the ticks and plot box
343
            // (insertAxisLabels in setupGrid() ).
344
            //
345
            // Therefore, we use a trick where we run the draw routine twice:
346
            // the first time to get the tick measurements, so that we can change
347
            // them, and then have it draw it again.
348
            var secondPass = false;
349
 
350
            var axisLabels = {};
351
            var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 };
352
 
353
            var defaultPadding = 2;  // padding between axis and tick labels
354
            plot.hooks.draw.push(function (plot, ctx) {
355
                var hasAxisLabels = false;
356
                if (!secondPass) {
357
                    // MEASURE AND SET OPTIONS
358
                    $.each(plot.getAxes(), function(axisName, axis) {
359
                        var opts = axis.options // Flot 0.7
360
                            || plot.getOptions()[axisName]; // Flot 0.6
361
 
362
                        // Handle redraws initiated outside of this plug-in.
363
                        if (axisName in axisLabels) {
364
                            axis.labelHeight = axis.labelHeight -
365
                                axisLabels[axisName].height;
366
                            axis.labelWidth = axis.labelWidth -
367
                                axisLabels[axisName].width;
368
                            opts.labelHeight = axis.labelHeight;
369
                            opts.labelWidth = axis.labelWidth;
370
                            axisLabels[axisName].cleanup();
371
                            delete axisLabels[axisName];
372
                        }
373
 
374
                        if (!opts || !opts.axisLabel || !axis.show)
375
                            return;
376
 
377
                        hasAxisLabels = true;
378
                        var renderer = null;
379
 
380
                        if (!opts.axisLabelUseHtml &&
381
                            navigator.appName == 'Microsoft Internet Explorer') {
382
                            var ua = navigator.userAgent;
383
                            var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
384
                            if (re.exec(ua) != null) {
385
                                rv = parseFloat(RegExp.$1);
386
                            }
387
                            if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
388
                                renderer = CssTransformAxisLabel;
389
                            } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
390
                                renderer = IeTransformAxisLabel;
391
                            } else if (opts.axisLabelUseCanvas) {
392
                                renderer = CanvasAxisLabel;
393
                            } else {
394
                                renderer = HtmlAxisLabel;
395
                            }
396
                        } else {
397
                            if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) {
398
                                renderer = HtmlAxisLabel;
399
                            } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) {
400
                                renderer = CanvasAxisLabel;
401
                            } else {
402
                                renderer = CssTransformAxisLabel;
403
                            }
404
                        }
405
 
406
                        var padding = opts.axisLabelPadding === undefined ?
407
                                      defaultPadding : opts.axisLabelPadding;
408
 
409
                        axisLabels[axisName] = new renderer(axisName,
410
                                                            axis.position, padding,
411
                                                            plot, opts);
412
 
413
                        // flot interprets axis.labelHeight and .labelWidth as
414
                        // the height and width of the tick labels. We increase
415
                        // these values to make room for the axis label and
416
                        // padding.
417
 
418
                        axisLabels[axisName].calculateSize();
419
 
420
                        // AxisLabel.height and .width are the size of the
421
                        // axis label and padding.
422
                        // Just set opts here because axis will be sorted out on
423
                        // the redraw.
424
 
425
                        opts.labelHeight = axis.labelHeight +
426
                            axisLabels[axisName].height;
427
                        opts.labelWidth = axis.labelWidth +
428
                            axisLabels[axisName].width;
429
                    });
430
 
431
                    // If there are axis labels, re-draw with new label widths and
432
                    // heights.
433
 
434
                    if (hasAxisLabels) {
435
                        secondPass = true;
436
                        plot.setupGrid();
437
                        plot.draw();
438
                    }
439
                } else {
440
                    secondPass = false;
441
                    // DRAW
442
                    $.each(plot.getAxes(), function(axisName, axis) {
443
                        var opts = axis.options // Flot 0.7
444
                            || plot.getOptions()[axisName]; // Flot 0.6
445
                        if (!opts || !opts.axisLabel || !axis.show)
446
                            return;
447
 
448
                        axisLabels[axisName].draw(axis.box);
449
                    });
450
                }
451
            });
452
        });
453
    }
454
 
455
 
456
    $.plot.plugins.push({
457
        init: init,
458
        options: options,
459
        name: 'axisLabels',
460
        version: '2.0'
461
    });
462
})(jQuery);