Merge pull request #431 from xmujay/0609_monitor
[phpmyadmin/aamir.git] / js / chart.js
blob895d051d9bfaa2b828c0f08c19ac8ca173ced30f
1 /**
2  * Chart type enumerations
3  */
4 var ChartType = {
5     LINE : 'line',
6     SPLINE : 'spline',
7     AREA : 'area',
8     BAR : 'bar',
9     COLUMN : 'column',
10     PIE : 'pie',
11     TIMELINE: 'timeline'
14 /**
15  * Abstract chart factory which defines the contract for chart factories
16  */
17 var ChartFactory = function () {
19 ChartFactory.prototype = {
20     createChart : function (type, options) {
21         throw new Error("createChart must be implemented by a subclass");
22     }
25 /**
26  * Abstract chart which defines the contract for charts
27  *
28  * @param elementId
29  *            id of the div element the chart is drawn in
30  */
31 var Chart = function (elementId) {
32     this.elementId = elementId;
34 Chart.prototype = {
35     draw : function (data, options) {
36         throw new Error("draw must be implemented by a subclass");
37     },
38     redraw : function (options) {
39         throw new Error("redraw must be implemented by a subclass");
40     },
41     destroy : function () {
42         throw new Error("destroy must be implemented by a subclass");
43     }
46 /**
47  * Abstract representation of charts that operates on DataTable where,<br />
48  * <ul>
49  * <li>First column provides index to the data.</li>
50  * <li>Each subsequent columns are of type
51  * <code>ColumnType.NUMBER<code> and represents a data series.</li>
52  * </ul>
53  * Line chart, area chart, bar chart, column chart are typical examples.
54  *
55  * @param elementId
56  *            id of the div element the chart is drawn in
57  */
58 var BaseChart = function (elementId) {
59     Chart.call(this, elementId);
61 BaseChart.prototype = new Chart();
62 BaseChart.prototype.constructor = BaseChart;
63 BaseChart.prototype.validateColumns = function (dataTable) {
64     var columns = dataTable.getColumns();
65     if (columns.length < 2) {
66         throw new Error("Minimum of two columns are required for this chart");
67     }
68     for (var i = 1; i < columns.length; i++) {
69         if (columns[i].type != ColumnType.NUMBER) {
70             throw new Error("Column " + (i + 1) + " should be of type 'Number'");
71         }
72     }
73     return true;
76 /**
77  * Abstract pie chart
78  *
79  * @param elementId
80  *            id of the div element the chart is drawn in
81  */
82 var PieChart = function (elementId) {
83     BaseChart.call(this, elementId);
85 PieChart.prototype = new BaseChart();
86 PieChart.prototype.constructor = PieChart;
87 PieChart.prototype.validateColumns = function (dataTable) {
88     var columns = dataTable.getColumns();
89     if (columns.length > 2) {
90         throw new Error("Pie charts can draw only one series");
91     }
92     return BaseChart.prototype.validateColumns.call(this, dataTable);
95 /**
96  * Abstract timeline chart
97  *
98  * @param elementId
99  *            id of the div element the chart is drawn in
100  */
101 var TimelineChart = function (elementId) {
102     BaseChart.call(this, elementId);
104 TimelineChart.prototype = new BaseChart();
105 TimelineChart.prototype.constructor = TimelineChart;
106 TimelineChart.prototype.validateColumns = function (dataTable) {
107     var result = BaseChart.prototype.validateColumns.call(this, dataTable);
108     if (result) {
109         var columns = dataTable.getColumns();
110         if (columns[0].type != ColumnType.DATE) {
111             throw new Error("First column of timeline chart need to be a date column");
112         }
113     }
114     return result;
118  * The data table contains column information and data for the chart.
119  */
120 var DataTable = function () {
121     var columns = [];
122     var data;
124     this.addColumn = function (type, name) {
125         columns.push({
126             'type' : type,
127             'name' : name
128         });
129     };
131     this.getColumns = function () {
132         return columns;
133     };
135     this.setData = function (rows) {
136         data = rows;
137         fillMissingValues();
138     };
140     this.getData = function () {
141         return data;
142     };
144     var fillMissingValues = function () {
145         if (columns.length === 0) {
146             throw new Error("Set columns first");
147         }
148         var row, column;
149         for (var i = 0; i < data.length; i++) {
150             row = data[i];
151             if (row.length > columns.length) {
152                 row.splice(columns.length - 1, row.length - columns.length);
153             } else if (row.length < columns.length) {
154                 for (var j = row.length; j < columns.length; j++) {
155                     row.push(null);
156                 }
157             }
158         }
159     };
163  * Column type enumeration
164  */
165 var ColumnType = {
166     STRING : 'string',
167     NUMBER : 'number',
168     BOOLEAN : 'boolean',
169     DATE : 'date'
172 /*******************************************************************************
173  * JQPlot specifc code
174  ******************************************************************************/
177  * Chart factory that returns JQPlotCharts
178  */
179 var JQPlotChartFactory = function () {
181 JQPlotChartFactory.prototype = new ChartFactory();
182 JQPlotChartFactory.prototype.createChart = function (type, elementId) {
183     var chart;
184     switch (type) {
185     case ChartType.LINE:
186         chart = new JQPlotLineChart(elementId);
187         break;
188     case ChartType.SPLINE:
189         chart = new JQPlotSplineChart(elementId);
190         break;
191     case ChartType.TIMELINE:
192         chart = new JQPlotTimelineChart(elementId);
193         break;
194     case ChartType.AREA:
195         chart = new JQPlotAreaChart(elementId);
196         break;
197     case ChartType.BAR:
198         chart = new JQPlotBarChart(elementId);
199         break;
200     case ChartType.COLUMN:
201         chart = new JQPlotColumnChart(elementId);
202         break;
203     case ChartType.PIE:
204         chart = new JQPlotPieChart(elementId);
205         break;
206     }
208     return chart;
212  * Abstract JQplot chart
214  * @param elementId
215  *            id of the div element the chart is drawn in
216  */
217 var JQPlotChart = function (elementId) {
218     Chart.call(this, elementId);
219     this.plot = null;
220     this.validator;
222 JQPlotChart.prototype = new Chart();
223 JQPlotChart.prototype.constructor = JQPlotChart;
224 JQPlotChart.prototype.draw = function (data, options) {
225     if (this.validator.validateColumns(data)) {
226         this.plot = $.jqplot(this.elementId, this.prepareData(data), this
227                 .populateOptions(data, options));
228     }
230 JQPlotChart.prototype.destroy = function () {
231     if (this.plot !== null) {
232         this.plot.destroy();
233     }
235 JQPlotChart.prototype.redraw = function (options) {
236     if (this.plot !== null) {
237         this.plot.replot(options);
238     }
240 JQPlotChart.prototype.populateOptions = function (dataTable, options) {
241     throw new Error("populateOptions must be implemented by a subclass");
243 JQPlotChart.prototype.prepareData = function (dataTable) {
244     throw new Error("prepareData must be implemented by a subclass");
248  * JQPlot line chart
250  * @param elementId
251  *            id of the div element the chart is drawn in
252  */
253 var JQPlotLineChart = function (elementId) {
254     JQPlotChart.call(this, elementId);
255     this.validator = BaseChart.prototype;
257 JQPlotLineChart.prototype = new JQPlotChart();
258 JQPlotLineChart.prototype.constructor = JQPlotLineChart;
260 JQPlotLineChart.prototype.populateOptions = function (dataTable, options) {
261     var columns = dataTable.getColumns();
262     var optional = {
263         axes : {
264             xaxis : {
265                 label : columns[0].name,
266                 renderer : $.jqplot.CategoryAxisRenderer,
267                 ticks : []
268             },
269             yaxis : {
270                 label : (columns.length == 2 ? columns[1].name : 'Values'),
271                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer
272             }
273         },
274         series : []
275     };
276     $.extend(true, optional, options);
278     if (optional.series.length === 0) {
279         for (var i = 1; i < columns.length; i++) {
280             optional.series.push({
281                 label : columns[i].name.toString()
282             });
283         }
284     }
285     if (optional.axes.xaxis.ticks.length === 0) {
286         var data = dataTable.getData();
287         for (var i = 0; i < data.length; i++) {
288             optional.axes.xaxis.ticks.push(data[i][0].toString());
289         }
290     }
291     return optional;
294 JQPlotLineChart.prototype.prepareData = function (dataTable) {
295     var data = dataTable.getData(), row;
296     var retData = [], retRow;
297     for (var i = 0; i < data.length; i++) {
298         row = data[i];
299         for (var j = 1; j < row.length; j++) {
300             retRow = retData[j - 1];
301             if (retRow === undefined) {
302                 retRow = [];
303                 retData[j - 1] = retRow;
304             }
305             retRow.push(row[j]);
306         }
307     }
308     return retData;
312  * JQPlot spline chart
314  * @param elementId
315  *            id of the div element the chart is drawn in
316  */
317 var JQPlotSplineChart = function (elementId) {
318     JQPlotLineChart.call(this, elementId);
320 JQPlotSplineChart.prototype = new JQPlotLineChart();
321 JQPlotSplineChart.prototype.constructor = JQPlotSplineChart;
323 JQPlotSplineChart.prototype.populateOptions = function (dataTable, options) {
324     var optional = {};
325     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
326             options);
327     var compulsory = {
328         seriesDefaults : {
329             rendererOptions : {
330                 smooth : true
331             }
332         }
333     };
334     $.extend(true, optional, opt, compulsory);
335     return optional;
339  * JQPlot timeline chart
341  * @param elementId
342  *            id of the div element the chart is drawn in
343  */
344 var JQPlotTimelineChart = function (elementId) {
345     JQPlotLineChart.call(this, elementId);
346     this.validator = TimelineChart.prototype;
348 JQPlotTimelineChart.prototype = new JQPlotLineChart();
349 JQPlotTimelineChart.prototype.constructor = JQPlotAreaChart;
351 JQPlotTimelineChart.prototype.populateOptions = function (dataTable, options) {
352     var optional = {
353         axes : {
354             xaxis : {
355                 tickOptions : {
356                     formatString: '%b %#d, %y'
357                 }
358             }
359         }
360     };
361     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable, options);
362     var compulsory = {
363         axes : {
364             xaxis : {
365                 renderer : $.jqplot.DateAxisRenderer
366             }
367         }
368     };
369     $.extend(true, optional, opt, compulsory);
370     return optional;
373 JQPlotTimelineChart.prototype.prepareData = function (dataTable) {
374     var data = dataTable.getData(), row, d;
375     var retData = [], retRow;
376     for (var i = 0; i < data.length; i++) {
377         row = data[i];
378         d = row[0];
379         for (var j = 1; j < row.length; j++) {
380             retRow = retData[j - 1];
381             if (retRow === undefined) {
382                 retRow = [];
383                 retData[j - 1] = retRow;
384             }
385             if (d !== null) {
386                 retRow.push([d.getTime(), row[j]]);
387             }
388         }
389     }
390     return retData;
394  * JQPlot area chart
396  * @param elementId
397  *            id of the div element the chart is drawn in
398  */
399 var JQPlotAreaChart = function (elementId) {
400     JQPlotLineChart.call(this, elementId);
402 JQPlotAreaChart.prototype = new JQPlotLineChart();
403 JQPlotAreaChart.prototype.constructor = JQPlotAreaChart;
405 JQPlotAreaChart.prototype.populateOptions = function (dataTable, options) {
406     var optional = {
407         seriesDefaults : {
408             fillToZero : true
409         }
410     };
411     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
412             options);
413     var compulsory = {
414         seriesDefaults : {
415             fill : true
416         }
417     };
418     $.extend(true, optional, opt, compulsory);
419     return optional;
423  * JQPlot column chart
425  * @param elementId
426  *            id of the div element the chart is drawn in
427  */
428 var JQPlotColumnChart = function (elementId) {
429     JQPlotLineChart.call(this, elementId);
431 JQPlotColumnChart.prototype = new JQPlotLineChart();
432 JQPlotColumnChart.prototype.constructor = JQPlotColumnChart;
434 JQPlotColumnChart.prototype.populateOptions = function (dataTable, options) {
435     var optional = {
436         seriesDefaults : {
437             fillToZero : true
438         }
439     };
440     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
441             options);
442     var compulsory = {
443         seriesDefaults : {
444             renderer : $.jqplot.BarRenderer
445         }
446     };
447     $.extend(true, optional, opt, compulsory);
448     return optional;
452  * JQPlot bar chart
454  * @param elementId
455  *            id of the div element the chart is drawn in
456  */
457 var JQPlotBarChart = function (elementId) {
458     JQPlotLineChart.call(this, elementId);
460 JQPlotBarChart.prototype = new JQPlotLineChart();
461 JQPlotBarChart.prototype.constructor = JQPlotBarChart;
463 JQPlotBarChart.prototype.populateOptions = function (dataTable, options) {
464     var columns = dataTable.getColumns();
465     var optional = {
466         axes : {
467             yaxis : {
468                 label : columns[0].name,
469                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer,
470                 renderer : $.jqplot.CategoryAxisRenderer,
471                 ticks : []
472             },
473             xaxis : {
474                 label : (columns.length == 2 ? columns[1].name : 'Values'),
475                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer
476             }
477         },
478         series : [],
479         seriesDefaults : {
480             fillToZero : true
481         }
482     };
483     var compulsory = {
484         seriesDefaults : {
485             renderer : $.jqplot.BarRenderer,
486             rendererOptions : {
487                 barDirection : 'horizontal'
488             }
489         }
490     };
491     $.extend(true, optional, options, compulsory);
493     if (optional.axes.yaxis.ticks.length === 0) {
494         var data = dataTable.getData();
495         for (var i = 0; i < data.length; i++) {
496             optional.axes.yaxis.ticks.push(data[i][0].toString());
497         }
498     }
499     if (optional.series.length === 0) {
500         for (var i = 1; i < columns.length; i++) {
501             optional.series.push({
502                 label : columns[i].name.toString()
503             });
504         }
505     }
506     return optional;
510  * JQPlot pie chart
512  * @param elementId
513  *            id of the div element the chart is drawn in
514  */
515 var JQPlotPieChart = function (elementId) {
516     JQPlotChart.call(this, elementId);
517     this.validator = PieChart.prototype;
519 JQPlotPieChart.prototype = new JQPlotChart();
520 JQPlotPieChart.prototype.constructor = JQPlotPieChart;
522 JQPlotPieChart.prototype.populateOptions = function (dataTable, options) {
523     var optional = {};
524     var compulsory = {
525         seriesDefaults : {
526             renderer : $.jqplot.PieRenderer
527         }
528     };
529     $.extend(true, optional, options, compulsory);
530     return optional;
533 JQPlotPieChart.prototype.prepareData = function (dataTable) {
534     var data = dataTable.getData(), row;
535     var retData = [];
536     for (var i = 0; i < data.length; i++) {
537         row = data[i];
538         retData.push([ row[0], row[1] ]);
539     }
540     return [ retData ];