Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/Scripts/jqPlot/plugins/jqplot.categoryAxisRenderer.js @ 9617

Last change on this file since 9617 was 9617, checked in by pfleck, 11 years ago

#2063:
Started integrating Hive statistics into statistics web project.
Added jqPlot library for charting.

File size: 26.1 KB
Line 
1/**
2 * jqPlot
3 * Pure JavaScript plotting plugin using jQuery
4 *
5 * Version: 1.0.0b2_r1012
6 *
7 * Copyright (c) 2009-2011 Chris Leonello
8 * jqPlot is currently available for use in all personal or commercial projects
9 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11 * choose the license that best suits your project and use it accordingly.
12 *
13 * Although not required, the author would appreciate an email letting him
14 * know of any substantial use of jqPlot.  You can reach the author at:
15 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
16 *
17 * If you are feeling kind and generous, consider supporting the project by
18 * making a donation at: http://www.jqplot.com/donate.php .
19 *
20 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
21 *
22 *     version 2007.04.27
23 *     author Ash Searle
24 *     http://hexmen.com/blog/2007/03/printf-sprintf/
25 *     http://hexmen.com/js/sprintf.js
26 *     The author (Ash Searle) has placed this code in the public domain:
27 *     "This code is unrestricted: you are free to use it however you like."
28 *
29 */
30(function($) {   
31    /**
32    *  class: $.jqplot.CategoryAxisRenderer
33    *  A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series.
34    * 
35    *  To use this renderer, include the plugin in your source
36    *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script>
37    * 
38    *  and supply the appropriate options to your plot
39    * 
40    *  > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}}
41    **/
42    $.jqplot.CategoryAxisRenderer = function(options) {
43        $.jqplot.LinearAxisRenderer.call(this);
44        // prop: sortMergedLabels
45        // True to sort tick labels when labels are created by merging
46        // x axis values from multiple series.  That is, say you have
47        // two series like:
48        // > line1 = [[2006, 4],            [2008, 9], [2009, 16]];
49        // > line2 = [[2006, 3], [2007, 7], [2008, 6]];
50        // If no label array is specified, tick labels will be collected
51        // from the x values of the series.  With sortMergedLabels
52        // set to true, tick labels will be:
53        // > [2006, 2007, 2008, 2009]
54        // With sortMergedLabels set to false, tick labels will be:
55        // > [2006, 2008, 2009, 2007]
56        //
57        // Note, this property is specified on the renderOptions for the
58        // axes when creating a plot:
59        // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
60        this.sortMergedLabels = false;
61    };
62   
63    $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
64    $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer;
65   
66    $.jqplot.CategoryAxisRenderer.prototype.init = function(options){
67        this.groups = 1;
68        this.groupLabels = [];
69        this._groupLabels = [];
70        this._grouped = false;
71        this._barsPerGroup = null;
72        // prop: tickRenderer
73        // A class of a rendering engine for creating the ticks labels displayed on the plot,
74        // See <$.jqplot.AxisTickRenderer>.
75        // this.tickRenderer = $.jqplot.AxisTickRenderer;
76        // this.labelRenderer = $.jqplot.AxisLabelRenderer;
77        $.extend(true, this, {tickOptions:{formatString:'%d'}}, options);
78        var db = this._dataBounds;
79        // Go through all the series attached to this axis and find
80        // the min/max bounds for this axis.
81        for (var i=0; i<this._series.length; i++) {
82            var s = this._series[i];
83            if (s.groups) {
84                this.groups = s.groups;
85            }
86            var d = s.data;
87           
88            for (var j=0; j<d.length; j++) {
89                if (this.name == 'xaxis' || this.name == 'x2axis') {
90                    if (d[j][0] < db.min || db.min == null) {
91                        db.min = d[j][0];
92                    }
93                    if (d[j][0] > db.max || db.max == null) {
94                        db.max = d[j][0];
95                    }
96                }             
97                else {
98                    if (d[j][1] < db.min || db.min == null) {
99                        db.min = d[j][1];
100                    }
101                    if (d[j][1] > db.max || db.max == null) {
102                        db.max = d[j][1];
103                    }
104                }             
105            }
106        }
107       
108        if (this.groupLabels.length) {
109            this.groups = this.groupLabels.length;
110        }
111    };
112 
113
114    $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() {
115        // we're are operating on an axis here
116        var ticks = this._ticks;
117        var userTicks = this.ticks;
118        var name = this.name;
119        // databounds were set on axis initialization.
120        var db = this._dataBounds;
121        var dim, interval;
122        var min, max;
123        var pos1, pos2;
124        var tt, i;
125
126        // if we already have ticks, use them.
127        if (userTicks.length) {
128            // adjust with blanks if we have groups
129            if (this.groups > 1 && !this._grouped) {
130                var l = userTicks.length;
131                var skip = parseInt(l/this.groups, 10);
132                var count = 0;
133                for (var i=skip; i<l; i+=skip) {
134                    userTicks.splice(i+count, 0, ' ');
135                    count++;
136                }
137                this._grouped = true;
138            }
139            this.min = 0.5;
140            this.max = userTicks.length + 0.5;
141            var range = this.max - this.min;
142            this.numberTicks = 2*userTicks.length + 1;
143            for (i=0; i<userTicks.length; i++){
144                tt = this.min + 2 * i * range / (this.numberTicks-1);
145                // need a marker before and after the tick
146                var t = new this.tickRenderer(this.tickOptions);
147                t.showLabel = false;
148                // t.showMark = true;
149                t.setTick(tt, this.name);
150                this._ticks.push(t);
151                var t = new this.tickRenderer(this.tickOptions);
152                t.label = userTicks[i];
153                // t.showLabel = true;
154                t.showMark = false;
155                t.showGridline = false;
156                t.setTick(tt+0.5, this.name);
157                this._ticks.push(t);
158            }
159            // now add the last tick at the end
160            var t = new this.tickRenderer(this.tickOptions);
161            t.showLabel = false;
162            // t.showMark = true;
163            t.setTick(tt+1, this.name);
164            this._ticks.push(t);
165        }
166
167        // we don't have any ticks yet, let's make some!
168        else {
169            if (name == 'xaxis' || name == 'x2axis') {
170                dim = this._plotDimensions.width;
171            }
172            else {
173                dim = this._plotDimensions.height;
174            }
175           
176            // if min, max and number of ticks specified, user can't specify interval.
177            if (this.min != null && this.max != null && this.numberTicks != null) {
178                this.tickInterval = null;
179            }
180           
181            // if max, min, and interval specified and interval won't fit, ignore interval.
182            if (this.min != null && this.max != null && this.tickInterval != null) {
183                if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
184                    this.tickInterval = null;
185                }
186            }
187       
188            // find out how many categories are in the lines and collect labels
189            var labels = [];
190            var numcats = 0;
191            var min = 0.5;
192            var max, val;
193            var isMerged = false;
194            for (var i=0; i<this._series.length; i++) {
195                var s = this._series[i];
196                for (var j=0; j<s.data.length; j++) {
197                    if (this.name == 'xaxis' || this.name == 'x2axis') {
198                        val = s.data[j][0];
199                    }
200                    else {
201                        val = s.data[j][1];
202                    }
203                    if ($.inArray(val, labels) == -1) {
204                        isMerged = true;
205                        numcats += 1;     
206                        labels.push(val);
207                    }
208                }
209            }
210           
211            if (isMerged && this.sortMergedLabels) {
212                labels.sort(function(a,b) { return a - b; });
213            }
214           
215            // keep a reference to these tick labels to use for redrawing plot (see bug #57)
216            this.ticks = labels;
217           
218            // now bin the data values to the right lables.
219            for (var i=0; i<this._series.length; i++) {
220                var s = this._series[i];
221                for (var j=0; j<s.data.length; j++) {
222                    if (this.name == 'xaxis' || this.name == 'x2axis') {
223                        val = s.data[j][0];
224                    }
225                    else {
226                        val = s.data[j][1];
227                    }
228                    // for category axis, force the values into category bins.
229                    // we should have the value in the label array now.
230                    var idx = $.inArray(val, labels)+1;
231                    if (this.name == 'xaxis' || this.name == 'x2axis') {
232                        s.data[j][0] = idx;
233                    }
234                    else {
235                        s.data[j][1] = idx;
236                    }
237                }
238            }
239           
240            // adjust with blanks if we have groups
241            if (this.groups > 1 && !this._grouped) {
242                var l = labels.length;
243                var skip = parseInt(l/this.groups, 10);
244                var count = 0;
245                for (var i=skip; i<l; i+=skip+1) {
246                    labels[i] = ' ';
247                }
248                this._grouped = true;
249            }
250       
251            max = numcats + 0.5;
252            if (this.numberTicks == null) {
253                this.numberTicks = 2*numcats + 1;
254            }
255
256            var range = max - min;
257            this.min = min;
258            this.max = max;
259            var track = 0;
260           
261            // todo: adjust this so more ticks displayed.
262            var maxVisibleTicks = parseInt(3+dim/10, 10);
263            var skip = parseInt(numcats/maxVisibleTicks, 10);
264
265            if (this.tickInterval == null) {
266
267                this.tickInterval = range / (this.numberTicks-1);
268
269            }
270            // if tickInterval is specified, we will ignore any computed maximum.
271            for (var i=0; i<this.numberTicks; i++){
272                tt = this.min + i * this.tickInterval;
273                var t = new this.tickRenderer(this.tickOptions);
274                // if even tick, it isn't a category, it's a divider
275                if (i/2 == parseInt(i/2, 10)) {
276                    t.showLabel = false;
277                    t.showMark = true;
278                }
279                else {
280                    if (skip>0 && track<skip) {
281                        t.showLabel = false;
282                        track += 1;
283                    }
284                    else {
285                        t.showLabel = true;
286                        track = 0;
287                    }
288                    t.label = t.formatter(t.formatString, labels[(i-1)/2]);
289                    t.showMark = false;
290                    t.showGridline = false;
291                }
292                t.setTick(tt, this.name);
293                this._ticks.push(t);
294            }
295        }
296       
297    };
298   
299    // called with scope of axis
300    $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) {
301        if (this.show) {
302            // populate the axis label and value properties.
303            // createTicks is a method on the renderer, but
304            // call it within the scope of the axis.
305            this.renderer.createTicks.call(this);
306            // fill a div with axes labels in the right direction.
307            // Need to pregenerate each axis to get it's bounds and
308            // position it and the labels correctly on the plot.
309            var dim=0;
310            var temp;
311            // Added for theming.
312            if (this._elem) {
313                // this._elem.empty();
314                // Memory Leaks patch
315                this._elem.emptyForce();
316            }
317
318            this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
319           
320            if (this.name == 'xaxis' || this.name == 'x2axis') {
321                this._elem.width(this._plotDimensions.width);
322            }
323            else {
324                this._elem.height(this._plotDimensions.height);
325            }
326           
327            // create a _label object.
328            this.labelOptions.axis = this.name;
329            this._label = new this.labelRenderer(this.labelOptions);
330            if (this._label.show) {
331                var elem = this._label.draw(ctx, plot);
332                elem.appendTo(this._elem);
333            }
334   
335            var t = this._ticks;
336            for (var i=0; i<t.length; i++) {
337                var tick = t[i];
338                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
339                    var elem = tick.draw(ctx, plot);
340                    elem.appendTo(this._elem);
341                }
342            }
343       
344            this._groupLabels = [];
345            // now make group labels
346            for (var i=0; i<this.groupLabels.length; i++)
347            {
348                var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>');
349                elem.html(this.groupLabels[i]);
350                this._groupLabels.push(elem);
351                elem.appendTo(this._elem);
352            }
353        }
354        return this._elem;
355    };
356   
357    // called with scope of axis
358    $.jqplot.CategoryAxisRenderer.prototype.set = function() {
359        var dim = 0;
360        var temp;
361        var w = 0;
362        var h = 0;
363        var lshow = (this._label == null) ? false : this._label.show;
364        if (this.show) {
365            var t = this._ticks;
366            for (var i=0; i<t.length; i++) {
367                var tick = t[i];
368                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
369                    if (this.name == 'xaxis' || this.name == 'x2axis') {
370                        temp = tick._elem.outerHeight(true);
371                    }
372                    else {
373                        temp = tick._elem.outerWidth(true);
374                    }
375                    if (temp > dim) {
376                        dim = temp;
377                    }
378                }
379            }
380           
381            var dim2 = 0;
382            for (var i=0; i<this._groupLabels.length; i++) {
383                var l = this._groupLabels[i];
384                if (this.name == 'xaxis' || this.name == 'x2axis') {
385                    temp = l.outerHeight(true);
386                }
387                else {
388                    temp = l.outerWidth(true);
389                }
390                if (temp > dim2) {
391                    dim2 = temp;
392                }
393            }
394           
395            if (lshow) {
396                w = this._label._elem.outerWidth(true);
397                h = this._label._elem.outerHeight(true);
398            }
399            if (this.name == 'xaxis') {
400                dim += dim2 + h;
401                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
402            }
403            else if (this.name == 'x2axis') {
404                dim += dim2 + h;
405                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
406            }
407            else if (this.name == 'yaxis') {
408                dim += dim2 + w;
409                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
410                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
411                    this._label._elem.css('width', w+'px');
412                }
413            }
414            else {
415                dim += dim2 + w;
416                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
417                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
418                    this._label._elem.css('width', w+'px');
419                }
420            }
421        } 
422    };
423   
424    // called with scope of axis
425    $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) {
426        var ticks = this._ticks;
427        var max = this.max;
428        var min = this.min;
429        var offmax = offsets.max;
430        var offmin = offsets.min;
431        var lshow = (this._label == null) ? false : this._label.show;
432        var i;
433   
434        for (var p in pos) {
435            this._elem.css(p, pos[p]);
436        }
437       
438        this._offsets = offsets;
439        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
440        var pixellength = offmax - offmin;
441        var unitlength = max - min;
442       
443        // point to unit and unit to point conversions references to Plot DOM element top left corner.
444        this.p2u = function(p){
445            return (p - offmin) * unitlength / pixellength + min;
446        };
447       
448        this.u2p = function(u){
449            return (u - min) * pixellength / unitlength + offmin;
450        };
451               
452        if (this.name == 'xaxis' || this.name == 'x2axis'){
453            this.series_u2p = function(u){
454                return (u - min) * pixellength / unitlength;
455            };
456            this.series_p2u = function(p){
457                return p * unitlength / pixellength + min;
458            };
459        }
460       
461        else {
462            this.series_u2p = function(u){
463                return (u - max) * pixellength / unitlength;
464            };
465            this.series_p2u = function(p){
466                return p * unitlength / pixellength + max;
467            };
468        }
469       
470        if (this.show) {
471            if (this.name == 'xaxis' || this.name == 'x2axis') {
472                for (i=0; i<ticks.length; i++) {
473                    var t = ticks[i];
474                    if (t.show && t.showLabel) {
475                        var shim;
476                       
477                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
478                            // will need to adjust auto positioning based on which axis this is.
479                            var temp = (this.name == 'xaxis') ? 1 : -1;
480                            switch (t.labelPosition) {
481                                case 'auto':
482                                    // position at end
483                                    if (temp * t.angle < 0) {
484                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
485                                    }
486                                    // position at start
487                                    else {
488                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
489                                    }
490                                    break;
491                                case 'end':
492                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
493                                    break;
494                                case 'start':
495                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
496                                    break;
497                                case 'middle':
498                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
499                                    break;
500                                default:
501                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
502                                    break;
503                            }
504                        }
505                        else {
506                            shim = -t.getWidth()/2;
507                        }
508                        var val = this.u2p(t.value) + shim + 'px';
509                        t._elem.css('left', val);
510                        t.pack();
511                    }
512                }
513               
514                var labeledge=['bottom', 0];
515                if (lshow) {
516                    var w = this._label._elem.outerWidth(true);
517                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
518                    if (this.name == 'xaxis') {
519                        this._label._elem.css('bottom', '0px');
520                        labeledge = ['bottom', this._label._elem.outerHeight(true)];
521                    }
522                    else {
523                        this._label._elem.css('top', '0px');
524                        labeledge = ['top', this._label._elem.outerHeight(true)];
525                    }
526                    this._label.pack();
527                }
528               
529                // draw the group labels
530                var step = parseInt(this._ticks.length/this.groups, 10);
531                for (i=0; i<this._groupLabels.length; i++) {
532                    var mid = 0;
533                    var count = 0;
534                    for (var j=i*step; j<=(i+1)*step; j++) {
535                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
536                            var t = this._ticks[j]._elem;
537                            var p = t.position();
538                            mid += p.left + t.outerWidth(true)/2;
539                            count++;
540                        }
541                    }
542                    mid = mid/count;
543                    this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)});
544                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
545                }
546            }
547            else {
548                for (i=0; i<ticks.length; i++) {
549                    var t = ticks[i];
550                    if (t.show && t.showLabel) {                       
551                        var shim;
552                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
553                            // will need to adjust auto positioning based on which axis this is.
554                            var temp = (this.name == 'yaxis') ? 1 : -1;
555                            switch (t.labelPosition) {
556                                case 'auto':
557                                    // position at end
558                                case 'end':
559                                    if (temp * t.angle < 0) {
560                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
561                                    }
562                                    else {
563                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
564                                    }
565                                    break;
566                                case 'start':
567                                    if (t.angle > 0) {
568                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
569                                    }
570                                    else {
571                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
572                                    }
573                                    break;
574                                case 'middle':
575                                    // if (t.angle > 0) {
576                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
577                                    // }
578                                    // else {
579                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
580                                    // }
581                                    shim = -t.getHeight()/2;
582                                    break;
583                                default:
584                                    shim = -t.getHeight()/2;
585                                    break;
586                            }
587                        }
588                        else {
589                            shim = -t.getHeight()/2;
590                        }
591                       
592                        var val = this.u2p(t.value) + shim + 'px';
593                        t._elem.css('top', val);
594                        t.pack();
595                    }
596                }
597               
598                var labeledge=['left', 0];
599                if (lshow) {
600                    var h = this._label._elem.outerHeight(true);
601                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
602                    if (this.name == 'yaxis') {
603                        this._label._elem.css('left', '0px');
604                        labeledge = ['left', this._label._elem.outerWidth(true)];
605                    }
606                    else {
607                        this._label._elem.css('right', '0px');
608                        labeledge = ['right', this._label._elem.outerWidth(true)];
609                    }   
610                    this._label.pack();
611                }
612               
613                // draw the group labels, position top here, do left after label position.
614                var step = parseInt(this._ticks.length/this.groups, 10);
615                for (i=0; i<this._groupLabels.length; i++) {
616                    var mid = 0;
617                    var count = 0;
618                    for (var j=i*step; j<=(i+1)*step; j++) {
619                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
620                            var t = this._ticks[j]._elem;
621                            var p = t.position();
622                            mid += p.top + t.outerHeight()/2;
623                            count++;
624                        }
625                    }
626                    mid = mid/count;
627                    this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2});
628                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
629                   
630                }
631            }
632        }
633    };   
634   
635   
636})(jQuery);
Note: See TracBrowser for help on using the repository browser.