fix #8056 - more refinements to checking data
[g.raphael] / g.bar.horizontal.js
1 /*!
2  * g.Raphael 0.5 - Charting library, based on RaphaĆ«l
3  *
4  * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 imports = typeof(imports) == 'undefined' ? false : imports;
8 Raphael = typeof(Raphael) != 'undefined' ? Raphael :  (imports ? imports.seed.Raphael.Raphael : {});
9 Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
10
11 (function () {
12
13     /**
14      * Create a horizontal chart
15      * support left axis with labels and bottom axis with interval
16      * 
17      * @param {Raphael} paper to draw on
18      * @param {int} width - width of the chart
19      * @param {int} height - height of the chart
20      * @param {Array} values
21      * @param {Object} opts options
22      *   background (string) : background color
23      *   colors (array) : colors of the bars
24      *   labelfont (string) : font family of bar labels
25      *   labelfontsize (number) : font size of bar labels
26      *   labelfontweight (string) (number) : font weight of bar labels
27      *   labelfontcolors (array) : font colors of bar labels
28      *   gutter (string) (number) : height of gutters in terms of percentage of bar height (e.g. '20%' : height raito of a bar to a gutter is 10 to 2)
29      *   axis (string) : composed of 4 '0' / '1' separated by space (indicated whether top / right / bottom / left axis should be shown) 
30      *   (e.g. '0 0 1 1' indicates that the bottom and the left axis should be shown)
31      *   leftaxiswidth (number) : width of left axis area
32      *   leftaxislabels (array) : labels in left axis area
33      *   leftaxisfontsize (number) : font size of labels in left axis
34      *   bottomaxisheight (number) : height of bottom axis area
35      *   bottomaxisstep (number) : number of steps in bottom axis
36      *   bottomaxisfontsize (number) : font size of labels in bottom axis
37      *   axisfont (string) : font family of labels in axes
38      *   axisfontweight (string) (number) : font weight of labels in axes
39      *   axisfontcolor (string) : font color of labels in axes
40      */
41
42     function MHBarchart(paper, width, height, values, opts) {
43         opts = opts || {};
44
45         var chartinst = this,
46             chart = paper.set(),
47             len = values.length,
48             max = values.length == 0 ? 0 : Math.max(...values),
49             background = opts.background || '#FFFFFF',
50             colors = opts.colors || chartinst.colors,
51             labelFont = opts.labelfont || 'Work Sans',
52             labelFontSize = opts.labelfontsize || 12,
53             labelFontWeight = opts.labelfontweight || 'bold',
54             labelFontColors = opts.labelfontcolors || [],
55             gutter = parseFloat(opts.gutter || "40%"),
56             axis = opts.axis || "0 0 0 0",
57             ax = (axis + "").split(/[,\s]+/),
58             leftAxisWidth = 0,
59             leftAxisLabels = opts.leftaxislabels || [];
60             leftAxisFontSize = opts.leftaxisfontsize || 16,
61             rightPadding = 50;
62             bottomAxisHeight = 0,
63             bottomAxisStep = opts.bottomaxisstep || 10;
64             bottomAxisFontSize = opts.bottomaxisfontsize || 16,
65             axisFont = opts.axisfont || 'Work Sans',
66             axisFontWeight = opts.axisfontweight || 'normal',
67             axisFontColor = opts.axisfontcolor || '#323232';
68
69         // bottom axis
70         if(+ax[2]) {
71             bottomAxisHeight = opts.bottomaxisheight || 30;
72             max = Math.ceil(max / bottomAxisStep) * bottomAxisStep;
73         }
74         // left axis
75         if(+ax[3]) {
76             leftAxisWidth = opts.leftaxiswidth || 240;
77         }
78
79         if(labelFontColors.length < len) {
80             for(var i = labelFontColors.length; i < len; i++) {
81                 labelFontColors.push('#FFFFFF');
82             }
83         }
84
85         var step = max / bottomAxisStep;
86         var factor = 1;
87
88         if(step > 10) {
89             if(step % 10 != 0) {
90                 factor = 10;
91             }
92         }
93
94         max = Math.ceil(step / factor) * factor * bottomAxisStep;
95
96         // background
97         paper.rect(0, 0, width, height).attr({ stroke: "none", fill: background });
98
99         var barWidth = (width - leftAxisWidth - rightPadding); // maximum width of bar
100         var barHeight = (height - bottomAxisHeight) / (len * (100 + gutter) + gutter) * 100;
101         var barGutter = barHeight * (gutter / 100);
102
103         // bottom axis
104         if(+ax[2]) {
105             chartinst.bottomAxis(
106                 paper,
107                 leftAxisWidth,
108                 height - bottomAxisHeight,
109                 barWidth,
110                 max,
111                 bottomAxisStep,
112                 bottomAxisHeight,
113                 axisFont,
114                 bottomAxisFontSize,
115                 axisFontWeight,
116                 axisFontColor
117             )
118         }
119
120         // left axis
121         if(+ax[3]) {
122             chartinst.leftAxis(
123                 paper,
124                 leftAxisWidth,
125                 0,
126                 height - bottomAxisHeight,
127                 leftAxisLabels,
128                 barHeight,
129                 barGutter,
130                 axisFont,
131                 leftAxisFontSize,
132                 axisFontWeight,
133                 axisFontColor
134             )
135         }
136
137         // bars
138         var unit = barWidth / max;
139         
140         values.forEach(function(v,k) {
141             if(v == 0) {
142                 return;
143             }
144             
145             paper.rect(leftAxisWidth, barGutter + (k * (barHeight + barGutter)), v * unit, barHeight).attr({ stroke: "none", fill: colors[k] });
146
147             // bar label
148             // 16 pixels away from right of left axis
149             // align bar label in the center
150             paper.text(leftAxisWidth + 16, barGutter + (k * (barHeight + barGutter)) + barHeight / 2, Roo.util.Format.number(v, 0)).attr({ 
151                 "font-size": labelFontSize,
152                 "font-family": labelFont,
153                 "font-weight": labelFontWeight,
154                 "text-anchor": 'start',
155                 fill : labelFontColors[k]
156             });
157
158         });
159
160         return chart;
161         
162     }
163
164     //inheritance
165     var F = function() {};
166     F.prototype = Raphael.g;
167     MHBarchart.prototype = new F;
168     
169     Raphael.fn.mhbarchart = function(width, height, values, opts) {
170         return new MHBarchart(this, width, height, values, opts);
171     };
172
173     MHBarchart.prototype.bottomAxis = function (paper, x, y, length, max, steps, axisHeight, axisFont, axisFontSize, axisFontWeight, axisFontColor)
174     {
175
176         var path = ["M", x, y, "l", length, 0],
177             text = paper.set(),
178             d = Math.ceil(max / steps),
179             dl = length / steps;
180
181         for(var i = 0; i <= steps; i++) {
182             paper.text(x + i * dl, y + axisHeight / 2, i * d).attr({ 
183                 "font-size": axisFontSize,
184                 "font-family": axisFont,
185                 "font-weight": axisFontWeight,
186                 "text-anchor": 'middle',
187                 fill : axisFontColor
188             });
189
190             // bottom axis interval
191             paper.path(["M", x + i * dl , y, "l", 0, -1 * y]).attr({ stroke: '#9E9E9E', "stroke-width": 1 });
192         }
193         
194         var res = paper.path(path).attr({ stroke: '#000', "stroke-width": 0 }); // default no axis
195
196         res.text = text;
197         res.all = paper.set([res, text]);
198         res.remove = function () {
199             this.text.remove();
200             this.constructor.prototype.remove.call(this);
201         };
202
203         return res;
204     }
205
206     MHBarchart.prototype.leftAxis = function (paper, x, y, length, labels, barHeight, barGutter, axisFont, axisFontSize, axisFontWeight, axisFontColor)
207     {
208         var path = ["M", x, y, "l", 0, length],
209             text = paper.set();
210         
211         labels.forEach(function(v,k)  {
212             paper.text(0, barGutter + (k * (barHeight + barGutter)) + barHeight / 2, v).attr({ 
213                 "font-size": axisFontSize,
214                 "font-family": axisFont,
215                 "font-weight": axisFontWeight,
216                 "text-anchor": 'start',
217                 fill : axisFontColor
218             });
219         });
220         
221         var res = paper.path(path).attr({ stroke: '#000', "stroke-width": 0 }); // default no axis
222
223         res.text = text;
224         res.all = paper.set([res, text]);
225         res.remove = function () {
226             this.text.remove();
227             this.constructor.prototype.remove.call(this);
228         };
229
230         return res;
231     }
232 })();