Added more updates
[soho-sigint.git] / client-freqwatch / Chart.js / src / Chart.Bar.js
1 (function(){
2         "use strict";
3
4         var root = this,
5                 Chart = root.Chart,
6                 helpers = Chart.helpers;
7
8
9         var defaultConfig = {
10                 //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
11                 scaleBeginAtZero : true,
12
13                 //Boolean - Whether grid lines are shown across the chart
14                 scaleShowGridLines : true,
15
16                 //String - Colour of the grid lines
17                 scaleGridLineColor : "rgba(0,0,0,.05)",
18
19                 //Number - Width of the grid lines
20                 scaleGridLineWidth : 1,
21
22                 //Boolean - Whether to show horizontal lines (except X axis)
23                 scaleShowHorizontalLines: true,
24
25                 //Boolean - Whether to show vertical lines (except Y axis)
26                 scaleShowVerticalLines: true,
27
28                 //Boolean - If there is a stroke on each bar
29                 barShowStroke : true,
30
31                 //Number - Pixel width of the bar stroke
32                 barStrokeWidth : 2,
33
34                 //Number - Spacing between each of the X value sets
35                 barValueSpacing : 5,
36
37                 //Number - Spacing between data sets within X values
38                 barDatasetSpacing : 1,
39
40                 //String - A legend template
41                 legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
42
43         };
44
45
46         Chart.Type.extend({
47                 name: "Bar",
48                 defaults : defaultConfig,
49                 initialize:  function(data){
50
51                         //Expose options as a scope variable here so we can access it in the ScaleClass
52                         var options = this.options;
53
54                         this.ScaleClass = Chart.Scale.extend({
55                                 offsetGridLines : true,
56                                 calculateBarX : function(datasetCount, datasetIndex, barIndex){
57                                         //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
58                                         var xWidth = this.calculateBaseWidth(),
59                                                 xAbsolute = this.calculateX(barIndex) - (xWidth/2),
60                                                 barWidth = this.calculateBarWidth(datasetCount);
61
62                                         return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
63                                 },
64                                 calculateBaseWidth : function(){
65                                         return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
66                                 },
67                                 calculateBarWidth : function(datasetCount){
68                                         //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
69                                         var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
70
71                                         return (baseWidth / datasetCount);
72                                 }
73                         });
74
75                         this.datasets = [];
76
77                         //Set up tooltip events on the chart
78                         if (this.options.showTooltips){
79                                 helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
80                                         var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
81
82                                         this.eachBars(function(bar){
83                                                 bar.restore(['fillColor', 'strokeColor']);
84                                         });
85                                         helpers.each(activeBars, function(activeBar){
86                                                 activeBar.fillColor = activeBar.highlightFill;
87                                                 activeBar.strokeColor = activeBar.highlightStroke;
88                                         });
89                                         this.showTooltip(activeBars);
90                                 });
91                         }
92
93                         //Declare the extension of the default point, to cater for the options passed in to the constructor
94                         this.BarClass = Chart.Rectangle.extend({
95                                 strokeWidth : this.options.barStrokeWidth,
96                                 showStroke : this.options.barShowStroke,
97                                 ctx : this.chart.ctx
98                         });
99
100                         //Iterate through each of the datasets, and build this into a property of the chart
101                         helpers.each(data.datasets,function(dataset,datasetIndex){
102
103                                 var datasetObject = {
104                                         label : dataset.label || null,
105                                         fillColor : dataset.fillColor,
106                                         strokeColor : dataset.strokeColor,
107                                         bars : []
108                                 };
109
110                                 this.datasets.push(datasetObject);
111
112                                 helpers.each(dataset.data,function(dataPoint,index){
113                                         //Add a new point for each piece of data, passing any required data to draw.
114                                         datasetObject.bars.push(new this.BarClass({
115                                                 value : dataPoint,
116                                                 label : data.labels[index],
117                                                 datasetLabel: dataset.label,
118                                                 strokeColor : dataset.strokeColor,
119                                                 fillColor : dataset.fillColor,
120                                                 highlightFill : dataset.highlightFill || dataset.fillColor,
121                                                 highlightStroke : dataset.highlightStroke || dataset.strokeColor
122                                         }));
123                                 },this);
124
125                         },this);
126
127                         this.buildScale(data.labels);
128
129                         this.BarClass.prototype.base = this.scale.endPoint;
130
131                         this.eachBars(function(bar, index, datasetIndex){
132                                 helpers.extend(bar, {
133                                         width : this.scale.calculateBarWidth(this.datasets.length),
134                                         x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
135                                         y: this.scale.endPoint
136                                 });
137                                 bar.save();
138                         }, this);
139
140                         this.render();
141                 },
142                 update : function(){
143                         this.scale.update();
144                         // Reset any highlight colours before updating.
145                         helpers.each(this.activeElements, function(activeElement){
146                                 activeElement.restore(['fillColor', 'strokeColor']);
147                         });
148
149                         this.eachBars(function(bar){
150                                 bar.save();
151                         });
152                         this.render();
153                 },
154                 eachBars : function(callback){
155                         helpers.each(this.datasets,function(dataset, datasetIndex){
156                                 helpers.each(dataset.bars, callback, this, datasetIndex);
157                         },this);
158                 },
159                 getBarsAtEvent : function(e){
160                         var barsArray = [],
161                                 eventPosition = helpers.getRelativePosition(e),
162                                 datasetIterator = function(dataset){
163                                         barsArray.push(dataset.bars[barIndex]);
164                                 },
165                                 barIndex;
166
167                         for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
168                                 for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
169                                         if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
170                                                 helpers.each(this.datasets, datasetIterator);
171                                                 return barsArray;
172                                         }
173                                 }
174                         }
175
176                         return barsArray;
177                 },
178                 buildScale : function(labels){
179                         var self = this;
180
181                         var dataTotal = function(){
182                                 var values = [];
183                                 self.eachBars(function(bar){
184                                         values.push(bar.value);
185                                 });
186                                 return values;
187                         };
188
189                         var scaleOptions = {
190                                 templateString : this.options.scaleLabel,
191                                 height : this.chart.height,
192                                 width : this.chart.width,
193                                 ctx : this.chart.ctx,
194                                 textColor : this.options.scaleFontColor,
195                                 fontSize : this.options.scaleFontSize,
196                                 fontStyle : this.options.scaleFontStyle,
197                                 fontFamily : this.options.scaleFontFamily,
198                                 valuesCount : labels.length,
199                                 beginAtZero : this.options.scaleBeginAtZero,
200                                 integersOnly : this.options.scaleIntegersOnly,
201                                 calculateYRange: function(currentHeight){
202                                         var updatedRanges = helpers.calculateScaleRange(
203                                                 dataTotal(),
204                                                 currentHeight,
205                                                 this.fontSize,
206                                                 this.beginAtZero,
207                                                 this.integersOnly
208                                         );
209                                         helpers.extend(this, updatedRanges);
210                                 },
211                                 xLabels : labels,
212                                 font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
213                                 lineWidth : this.options.scaleLineWidth,
214                                 lineColor : this.options.scaleLineColor,
215                                 showHorizontalLines : this.options.scaleShowHorizontalLines,
216                                 showVerticalLines : this.options.scaleShowVerticalLines,
217                                 gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
218                                 gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
219                                 padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
220                                 showLabels : this.options.scaleShowLabels,
221                                 display : this.options.showScale
222                         };
223
224                         if (this.options.scaleOverride){
225                                 helpers.extend(scaleOptions, {
226                                         calculateYRange: helpers.noop,
227                                         steps: this.options.scaleSteps,
228                                         stepValue: this.options.scaleStepWidth,
229                                         min: this.options.scaleStartValue,
230                                         max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
231                                 });
232                         }
233
234                         this.scale = new this.ScaleClass(scaleOptions);
235                 },
236                 addData : function(valuesArray,label){
237                         //Map the values array for each of the datasets
238                         helpers.each(valuesArray,function(value,datasetIndex){
239                                 //Add a new point for each piece of data, passing any required data to draw.
240                                 this.datasets[datasetIndex].bars.push(new this.BarClass({
241                                         value : value,
242                                         label : label,
243                                         datasetLabel: this.datasets[datasetIndex].label,
244                                         x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
245                                         y: this.scale.endPoint,
246                                         width : this.scale.calculateBarWidth(this.datasets.length),
247                                         base : this.scale.endPoint,
248                                         strokeColor : this.datasets[datasetIndex].strokeColor,
249                                         fillColor : this.datasets[datasetIndex].fillColor
250                                 }));
251                         },this);
252
253                         this.scale.addXLabel(label);
254                         //Then re-render the chart.
255                         this.update();
256                 },
257                 removeData : function(){
258                         this.scale.removeXLabel();
259                         //Then re-render the chart.
260                         helpers.each(this.datasets,function(dataset){
261                                 dataset.bars.shift();
262                         },this);
263                         this.update();
264                 },
265                 reflow : function(){
266                         helpers.extend(this.BarClass.prototype,{
267                                 y: this.scale.endPoint,
268                                 base : this.scale.endPoint
269                         });
270                         var newScaleProps = helpers.extend({
271                                 height : this.chart.height,
272                                 width : this.chart.width
273                         });
274                         this.scale.update(newScaleProps);
275                 },
276                 draw : function(ease){
277                         var easingDecimal = ease || 1;
278                         this.clear();
279
280                         var ctx = this.chart.ctx;
281
282                         this.scale.draw(easingDecimal);
283
284                         //Draw all the bars for each dataset
285                         helpers.each(this.datasets,function(dataset,datasetIndex){
286                                 helpers.each(dataset.bars,function(bar,index){
287                                         if (bar.hasValue()){
288                                                 bar.base = this.scale.endPoint;
289                                                 //Transition then draw
290                                                 bar.transition({
291                                                         x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
292                                                         y : this.scale.calculateY(bar.value),
293                                                         width : this.scale.calculateBarWidth(this.datasets.length)
294                                                 }, easingDecimal).draw();
295                                         }
296                                 },this);
297
298                         },this);
299                 }
300         });
301
302
303 }).call(this);