fix #8056 - more refinements to checking data
[g.raphael] / g.bar.vertical.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 vertical chart
15      * support left 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      *   leftaxisstep (number) : number of steps in left axis
33      *   leftaxisfontsize (number) : font size of labels in left axis
34      *   axisfont (string) : font family of labels in axes
35      *   axisfontweight (string) (number) : font weight of labels in axes
36      *   axisfontcolor (string) : font color of labels in axes
37      *   legend (array) : legend
38      *   legendkeyshape (string) : shape of the legend keys ('circle' / 'rect')
39      *   legendkeysize (number) : size of the legend keys (diameter for 'circle' and width for 'rect')
40      *   legendfont (string) : font family of the legend labels
41      *   legendfontsize (number) : font size of the legend labels
42      *   legendfontcolor (string) : font color of the legend colors
43      *   lineheight (number) : distance between two legend labels
44      */
45
46      function MVBarchart(paper, width, height, values, opts) {
47         opts = opts || {};
48
49         var chartinst = this,
50             chart = paper.set(),
51             len = values.length,
52             max = opts.max ? opts.max : (values.length == 0 ? 0 : Math.max(...values)),
53             background = opts.background || '#FFFFFF',
54             colors = opts.colors || chartinst.colors,
55             labelFont = opts.labelfont || 'Work Sans',
56             labelFontSize = opts.labelfontsize || 16,
57             labelFontWeight = opts.labelfontweight || 'bold',
58             labelFontColors = opts.labelfontcolors || [],
59             gutter = parseFloat(opts.gutter || "20%"),
60             axis = opts.axis || "0 0 0 0",
61             ax = (axis + "").split(/[,\s]+/),
62             leftAxisWidth = 0,
63             leftAxisStep = opts.leftaxisstep || 10;
64             leftAxisFontSize = opts.leftaxisfontsize || 12,
65             axisFont = opts.axisfont || 'Work Sans',
66             axisFontWeight = opts.axisfontweight || 'normal',
67             axisFontColor = opts.axisfontcolor || '#323232';
68             topPadding = 20;
69             bottomPadding = 120; // for legends
70
71         // left axis
72         if(+ax[3]) {
73             leftAxisWidth = opts.leftaxiswidth || 50;
74             max = Math.ceil(max / leftAxisStep) * leftAxisStep;
75         }
76
77         var step = max / leftAxisStep;
78         var factor = 1;
79
80         if(step > 10) {
81             if(step % 10 != 0) {
82                 factor = 10;
83             }
84         }
85
86         max = Math.ceil(step / factor) * factor * leftAxisStep;
87
88         if(labelFontColors.length < len) {
89             for(var i = labelFontColors.length; i < len; i++) {
90                 labelFontColors.push('#323232');
91             }
92         }
93
94         // background
95         paper.rect(0, 0, width, height).attr({ stroke: "none", fill: background });
96
97         var barWidth = (width - leftAxisWidth) / (len * (100 + gutter) + gutter) * 100;
98         var barHeight = (height - topPadding - bottomPadding); // maximum height of bar
99         var barGutter = barWidth * (gutter / 100);
100
101         // bars
102         var unit = barHeight / max;
103
104         // left axis
105         if(+ax[3]) {
106             chartinst.leftAxis(
107                 paper,
108                 leftAxisWidth,
109                 height - bottomPadding,
110                 width,
111                 barHeight,
112                 max,
113                 leftAxisStep,
114                 leftAxisWidth,
115                 axisFont,
116                 leftAxisFontSize,
117                 axisFontWeight,
118                 axisFontColor
119             )
120         }
121
122
123         values.forEach(function(v,k) {
124             if(v == 0) {
125                 return;
126             }
127
128             paper.rect(leftAxisWidth + barGutter + (k * (barWidth + barGutter)), topPadding + barHeight - v * unit, barWidth, v * unit).attr({stroke: "none", fill: colors[k]});
129
130             // bar label
131             // 4 pixels away from top of the bar
132             // align bar label in the center
133             paper.text(leftAxisWidth + barGutter + (k * (barWidth + barGutter)) + barWidth / 2, topPadding + barHeight - v * unit - 4 - labelFontSize / 2, Roo.util.Format.number(v, 0)).attr({ 
134                 "font-size": labelFontSize,
135                 "font-family": labelFont,
136                 "font-weight": labelFontWeight,
137                 "text-anchor": 'middle',
138                 fill : labelFontColors[k]
139             });
140
141         });
142
143         chartinst.legend(paper, opts, values, len, height, bottomPadding);
144
145         return chart;
146         
147      }
148
149     //inheritance
150     var F = function() {};
151     F.prototype = Raphael.g;
152     MVBarchart.prototype = new F;
153     
154     Raphael.fn.mvbarchart = function(width, height, values, opts) {
155         return new MVBarchart(this, width, height, values, opts);
156     };
157
158
159     MVBarchart.prototype.leftAxis = function (paper, x, y, width, length,  max, steps, axisWidth, axisFont, axisFontSize, axisFontWeight, axisFontColor)
160     {
161         var path = ["M", x, y, "l", 0, length],
162             d = Math.ceil(max / steps),
163             dl = length / steps;
164
165
166         for(var i = 0; i <= steps; i++) {
167             // 0 pixels away from the left of the graph
168             paper.text(0, y - i * dl, Roo.util.Format.number(Math.round(i * d), 0)).attr({ 
169                 "font-size": axisFontSize,
170                 "font-family": axisFont,
171                 "font-weight": axisFontWeight,
172                 "text-anchor": 'start',
173                 fill : axisFontColor
174             });
175
176             // left axis interval
177             paper.path(["M", x, y - i * dl, "l", width - x, 0]).attr({ stroke: '#9E9E9E', "stroke-width": 1 });
178         }
179         
180         paper.path(path).attr({ stroke: '#000', "stroke-width": 0 }); // default no axis
181     }
182
183     // draw legend
184     MVBarchart.prototype.legend = function (paper, opts, values, len, height, bottomPadding) 
185     {
186         var chartinst = this,
187             legendKeyShape = opts.legendkeyshape || 'rect',
188             legendKeySize = opts.legendkeysize || 14,
189             legendFont = opts.legendfont || "Work Sans",
190             legendFontSize = opts.legendfontsize || 14,
191             legendFontColor = opts.legendfontcolor || '#323232',
192             lineHeight = opts.lineheight || 23;
193
194
195         for (var i = 0; i < len; i++) {
196             // 0 pixels away from left of the graph
197             // 30 pixels away from bottom of the chart
198             if(legendKeyShape == 'rect') {
199                 // pass top left position for 'rect'
200                 paper.rect(
201                     0, 
202                     height - bottomPadding + 30 + i * lineHeight - legendKeySize / 2, 
203                     legendKeySize, 
204                     legendKeySize, 
205                     0
206                 ).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
207             }
208             else {
209                 // pass center position for 'circle'
210                 paper.circle(
211                     legendKeySize / 2, 
212                     height - bottomPadding + 30 + i * lineHeight, 
213                     legendKeySize / 2
214                 ).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
215             }
216
217             var text = (opts.legend) ? opts.legend[i] : values[i].toString();
218             
219             if(text.indexOf('#qty#') !== -1) {
220                 text = text.replace('#qty#', Roo.util.Format.number(Math.round(values[i]), 0));
221             }
222
223             // 12 pixels away from th right of the legend key
224             paper.text(legendKeySize + 12, height - bottomPadding + 30 + i * lineHeight - legendKeySize / 10, text).attr({ 
225                 "font-size": legendFontSize,
226                 "font-family": legendFont,
227                 "text-anchor": "start",
228                 fill : legendFontColor
229             });
230         }
231     }
232  })();