fix in tab ui for top.restoreSession
[openemr.git] / phpmyadmin / js / chart.js
blob93153add6200b4cacbb058fe8bf2144502ab6cdc
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',
12     SCATTER: 'scatter'
15 /**
16  * Column type enumeration
17  */
18 var ColumnType = {
19     STRING : 'string',
20     NUMBER : 'number',
21     BOOLEAN : 'boolean',
22     DATE : 'date'
25 /**
26  * Abstract chart factory which defines the contract for chart factories
27  */
28 var ChartFactory = function () {
30 ChartFactory.prototype = {
31     createChart : function (type, options) {
32         throw new Error("createChart must be implemented by a subclass");
33     }
36 /**
37  * Abstract chart which defines the contract for charts
38  *
39  * @param elementId
40  *            id of the div element the chart is drawn in
41  */
42 var Chart = function (elementId) {
43     this.elementId = elementId;
45 Chart.prototype = {
46     draw : function (data, options) {
47         throw new Error("draw must be implemented by a subclass");
48     },
49     redraw : function (options) {
50         throw new Error("redraw must be implemented by a subclass");
51     },
52     destroy : function () {
53         throw new Error("destroy must be implemented by a subclass");
54     },
55     toImageString : function() {
56         throw new Error("toImageString must be implemented by a subclass");
57     }
60 /**
61  * Abstract representation of charts that operates on DataTable where,<br />
62  * <ul>
63  * <li>First column provides index to the data.</li>
64  * <li>Each subsequent columns are of type
65  * <code>ColumnType.NUMBER<code> and represents a data series.</li>
66  * </ul>
67  * Line chart, area chart, bar chart, column chart are typical examples.
68  *
69  * @param elementId
70  *            id of the div element the chart is drawn in
71  */
72 var BaseChart = function (elementId) {
73     Chart.call(this, elementId);
75 BaseChart.prototype = new Chart();
76 BaseChart.prototype.constructor = BaseChart;
77 BaseChart.prototype.validateColumns = function (dataTable) {
78     var columns = dataTable.getColumns();
79     if (columns.length < 2) {
80         throw new Error("Minimum of two columns are required for this chart");
81     }
82     for (var i = 1; i < columns.length; i++) {
83         if (columns[i].type != ColumnType.NUMBER) {
84             throw new Error("Column " + (i + 1) + " should be of type 'Number'");
85         }
86     }
87     return true;
90 /**
91  * Abstract pie chart
92  *
93  * @param elementId
94  *            id of the div element the chart is drawn in
95  */
96 var PieChart = function (elementId) {
97     BaseChart.call(this, elementId);
99 PieChart.prototype = new BaseChart();
100 PieChart.prototype.constructor = PieChart;
101 PieChart.prototype.validateColumns = function (dataTable) {
102     var columns = dataTable.getColumns();
103     if (columns.length > 2) {
104         throw new Error("Pie charts can draw only one series");
105     }
106     return BaseChart.prototype.validateColumns.call(this, dataTable);
110  * Abstract timeline chart
112  * @param elementId
113  *            id of the div element the chart is drawn in
114  */
115 var TimelineChart = function (elementId) {
116     BaseChart.call(this, elementId);
118 TimelineChart.prototype = new BaseChart();
119 TimelineChart.prototype.constructor = TimelineChart;
120 TimelineChart.prototype.validateColumns = function (dataTable) {
121     var result = BaseChart.prototype.validateColumns.call(this, dataTable);
122     if (result) {
123         var columns = dataTable.getColumns();
124         if (columns[0].type != ColumnType.DATE) {
125             throw new Error("First column of timeline chart need to be a date column");
126         }
127     }
128     return result;
132  * Abstract scatter chart
134  * @param elementId
135  *            id of the div element the chart is drawn in
136  */
137 var ScatterChart = function(elementId) {
138     BaseChart.call(this, elementId);
140 ScatterChart.prototype = new BaseChart();
141 ScatterChart.prototype.constructor = ScatterChart;
142 ScatterChart.prototype.validateColumns = function (dataTable) {
143     var result = BaseChart.prototype.validateColumns.call(this, dataTable);
144     if (result) {
145         var columns = dataTable.getColumns();
146         if (columns[0].type != ColumnType.NUMBER) {
147             throw new Error("First column of scatter chart need to be a numeric column");
148         }
149     }
150     return result;
154  * The data table contains column information and data for the chart.
155  */
156 var DataTable = function () {
157     var columns = [];
158     var data = null;
160     this.addColumn = function (type, name) {
161         columns.push({
162             'type' : type,
163             'name' : name
164         });
165     };
167     this.getColumns = function () {
168         return columns;
169     };
171     this.setData = function (rows) {
172         data = rows;
173         fillMissingValues();
174     };
176     this.getData = function () {
177         return data;
178     };
180     var fillMissingValues = function () {
181         if (columns.length === 0) {
182             throw new Error("Set columns first");
183         }
184         var row;
185         for (var i = 0; i < data.length; i++) {
186             row = data[i];
187             if (row.length > columns.length) {
188                 row.splice(columns.length - 1, row.length - columns.length);
189             } else if (row.length < columns.length) {
190                 for (var j = row.length; j < columns.length; j++) {
191                     row.push(null);
192                 }
193             }
194         }
195     };
198 /*******************************************************************************
199  * JQPlot specific code
200  ******************************************************************************/
203  * Abstract JQplot chart
205  * @param elementId
206  *            id of the div element the chart is drawn in
207  */
208 var JQPlotChart = function (elementId) {
209     Chart.call(this, elementId);
210     this.plot = null;
211     this.validator = null;
213 JQPlotChart.prototype = new Chart();
214 JQPlotChart.prototype.constructor = JQPlotChart;
215 JQPlotChart.prototype.draw = function (data, options) {
216     if (this.validator.validateColumns(data)) {
217         this.plot = $.jqplot(this.elementId, this.prepareData(data), this
218                 .populateOptions(data, options));
219     }
221 JQPlotChart.prototype.destroy = function () {
222     if (this.plot !== null) {
223         this.plot.destroy();
224     }
226 JQPlotChart.prototype.redraw = function (options) {
227     if (this.plot !== null) {
228         this.plot.replot(options);
229     }
231 JQPlotChart.prototype.toImageString = function (options) {
232     if (this.plot !== null) {
233         return $('#' + this.elementId).jqplotToImageStr({});
234     }
236 JQPlotChart.prototype.populateOptions = function (dataTable, options) {
237     throw new Error("populateOptions must be implemented by a subclass");
239 JQPlotChart.prototype.prepareData = function (dataTable) {
240     throw new Error("prepareData must be implemented by a subclass");
244  * JQPlot line chart
246  * @param elementId
247  *            id of the div element the chart is drawn in
248  */
249 var JQPlotLineChart = function (elementId) {
250     JQPlotChart.call(this, elementId);
251     this.validator = BaseChart.prototype;
253 JQPlotLineChart.prototype = new JQPlotChart();
254 JQPlotLineChart.prototype.constructor = JQPlotLineChart;
256 JQPlotLineChart.prototype.populateOptions = function (dataTable, options) {
257     var columns = dataTable.getColumns();
258     var optional = {
259         axes : {
260             xaxis : {
261                 label : columns[0].name,
262                 renderer : $.jqplot.CategoryAxisRenderer,
263                 ticks : []
264             },
265             yaxis : {
266                 label : (columns.length == 2 ? columns[1].name : 'Values'),
267                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer
268             }
269         },
270         highlighter: {
271             show: true,
272             tooltipAxes: 'y',
273             formatString:'%d'
274         },
275         series : []
276     };
277     $.extend(true, optional, options);
279     if (optional.series.length === 0) {
280         for (var i = 1; i < columns.length; i++) {
281             optional.series.push({
282                 label : columns[i].name.toString()
283             });
284         }
285     }
286     if (optional.axes.xaxis.ticks.length === 0) {
287         var data = dataTable.getData();
288         for (var j = 0; j < data.length; j++) {
289             optional.axes.xaxis.ticks.push(data[j][0].toString());
290         }
291     }
292     return optional;
295 JQPlotLineChart.prototype.prepareData = function (dataTable) {
296     var data = dataTable.getData(), row;
297     var retData = [], retRow;
298     for (var i = 0; i < data.length; i++) {
299         row = data[i];
300         for (var j = 1; j < row.length; j++) {
301             retRow = retData[j - 1];
302             if (retRow === undefined) {
303                 retRow = [];
304                 retData[j - 1] = retRow;
305             }
306             retRow.push(row[j]);
307         }
308     }
309     return retData;
313  * JQPlot spline chart
315  * @param elementId
316  *            id of the div element the chart is drawn in
317  */
318 var JQPlotSplineChart = function (elementId) {
319     JQPlotLineChart.call(this, elementId);
321 JQPlotSplineChart.prototype = new JQPlotLineChart();
322 JQPlotSplineChart.prototype.constructor = JQPlotSplineChart;
324 JQPlotSplineChart.prototype.populateOptions = function (dataTable, options) {
325     var optional = {};
326     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
327             options);
328     var compulsory = {
329         seriesDefaults : {
330             rendererOptions : {
331                 smooth : true
332             }
333         }
334     };
335     $.extend(true, optional, opt, compulsory);
336     return optional;
340  * JQPlot scatter chart
342  * @param elementId
343  *            id of the div element the chart is drawn in
344  */
345 var JQPlotScatterChart = function (elementId) {
346     JQPlotChart.call(this, elementId);
347     this.validator = ScatterChart.prototype;
349 JQPlotScatterChart.prototype = new JQPlotChart();
350 JQPlotScatterChart.prototype.constructor = JQPlotScatterChart;
352 JQPlotScatterChart.prototype.populateOptions = function (dataTable, options) {
353     var columns = dataTable.getColumns();
354     var optional = {
355         axes : {
356             xaxis : {
357                 label : columns[0].name
358             },
359             yaxis : {
360                 label : (columns.length == 2 ? columns[1].name : 'Values'),
361                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer
362             }
363         },
364         highlighter: {
365             show: true,
366             tooltipAxes: 'xy',
367             formatString:'%d, %d'
368         },
369         series : []
370     };
371     for (var i = 1; i < columns.length; i++) {
372         optional.series.push({
373             label : columns[i].name.toString()
374         });
375     }
377     var compulsory = {
378         seriesDefaults : {
379             showLine: false,
380             markerOptions: {
381                 size: 7,
382                 style: "x"
383             }
384         }
385     };
387     $.extend(true, optional, options, compulsory);
388     return optional;
391 JQPlotScatterChart.prototype.prepareData = function (dataTable) {
392     var data = dataTable.getData(), row;
393     var retData = [], retRow;
394     for (var i = 0; i < data.length; i++) {
395         row = data[i];
396         if (row[0]) {
397             for (var j = 1; j < row.length; j++) {
398                 retRow = retData[j - 1];
399                 if (retRow === undefined) {
400                     retRow = [];
401                     retData[j - 1] = retRow;
402                 }
403                 retRow.push([row[0], row[j]]);
404             }
405         }
406     }
407     return retData;
411  * JQPlot timeline chart
413  * @param elementId
414  *            id of the div element the chart is drawn in
415  */
416 var JQPlotTimelineChart = function (elementId) {
417     JQPlotLineChart.call(this, elementId);
418     this.validator = TimelineChart.prototype;
420 JQPlotTimelineChart.prototype = new JQPlotLineChart();
421 JQPlotTimelineChart.prototype.constructor = JQPlotTimelineChart;
423 JQPlotTimelineChart.prototype.populateOptions = function (dataTable, options) {
424     var optional = {
425         axes : {
426             xaxis : {
427                 tickOptions : {
428                     formatString: '%b %#d, %y'
429                 }
430             }
431         }
432     };
433     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable, options);
434     var compulsory = {
435         axes : {
436             xaxis : {
437                 renderer : $.jqplot.DateAxisRenderer
438             }
439         }
440     };
441     $.extend(true, optional, opt, compulsory);
442     return optional;
445 JQPlotTimelineChart.prototype.prepareData = function (dataTable) {
446     var data = dataTable.getData(), row, d;
447     var retData = [], retRow;
448     for (var i = 0; i < data.length; i++) {
449         row = data[i];
450         d = row[0];
451         for (var j = 1; j < row.length; j++) {
452             retRow = retData[j - 1];
453             if (retRow === undefined) {
454                 retRow = [];
455                 retData[j - 1] = retRow;
456             }
457             if (d !== null) {
458                 retRow.push([d.getTime(), row[j]]);
459             }
460         }
461     }
462     return retData;
466  * JQPlot area chart
468  * @param elementId
469  *            id of the div element the chart is drawn in
470  */
471 var JQPlotAreaChart = function (elementId) {
472     JQPlotLineChart.call(this, elementId);
474 JQPlotAreaChart.prototype = new JQPlotLineChart();
475 JQPlotAreaChart.prototype.constructor = JQPlotAreaChart;
477 JQPlotAreaChart.prototype.populateOptions = function (dataTable, options) {
478     var optional = {
479         seriesDefaults : {
480             fillToZero : true
481         }
482     };
483     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
484             options);
485     var compulsory = {
486         seriesDefaults : {
487             fill : true
488         }
489     };
490     $.extend(true, optional, opt, compulsory);
491     return optional;
495  * JQPlot column chart
497  * @param elementId
498  *            id of the div element the chart is drawn in
499  */
500 var JQPlotColumnChart = function (elementId) {
501     JQPlotLineChart.call(this, elementId);
503 JQPlotColumnChart.prototype = new JQPlotLineChart();
504 JQPlotColumnChart.prototype.constructor = JQPlotColumnChart;
506 JQPlotColumnChart.prototype.populateOptions = function (dataTable, options) {
507     var optional = {
508         seriesDefaults : {
509             fillToZero : true
510         }
511     };
512     var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
513             options);
514     var compulsory = {
515         seriesDefaults : {
516             renderer : $.jqplot.BarRenderer
517         }
518     };
519     $.extend(true, optional, opt, compulsory);
520     return optional;
524  * JQPlot bar chart
526  * @param elementId
527  *            id of the div element the chart is drawn in
528  */
529 var JQPlotBarChart = function (elementId) {
530     JQPlotLineChart.call(this, elementId);
532 JQPlotBarChart.prototype = new JQPlotLineChart();
533 JQPlotBarChart.prototype.constructor = JQPlotBarChart;
535 JQPlotBarChart.prototype.populateOptions = function (dataTable, options) {
536     var columns = dataTable.getColumns();
537     var optional = {
538         axes : {
539             yaxis : {
540                 label : columns[0].name,
541                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer,
542                 renderer : $.jqplot.CategoryAxisRenderer,
543                 ticks : []
544             },
545             xaxis : {
546                 label : (columns.length == 2 ? columns[1].name : 'Values'),
547                 labelRenderer : $.jqplot.CanvasAxisLabelRenderer
548             }
549         },
550         highlighter: {
551             show: true,
552             tooltipAxes: 'x',
553             formatString:'%d'
554         },
555         series : [],
556         seriesDefaults : {
557             fillToZero : true
558         }
559     };
560     var compulsory = {
561         seriesDefaults : {
562             renderer : $.jqplot.BarRenderer,
563             rendererOptions : {
564                 barDirection : 'horizontal'
565             }
566         }
567     };
568     $.extend(true, optional, options, compulsory);
570     if (optional.axes.yaxis.ticks.length === 0) {
571         var data = dataTable.getData();
572         for (var i = 0; i < data.length; i++) {
573             optional.axes.yaxis.ticks.push(data[i][0].toString());
574         }
575     }
576     if (optional.series.length === 0) {
577         for (var j = 1; j < columns.length; j++) {
578             optional.series.push({
579                 label : columns[j].name.toString()
580             });
581         }
582     }
583     return optional;
587  * JQPlot pie chart
589  * @param elementId
590  *            id of the div element the chart is drawn in
591  */
592 var JQPlotPieChart = function (elementId) {
593     JQPlotChart.call(this, elementId);
594     this.validator = PieChart.prototype;
596 JQPlotPieChart.prototype = new JQPlotChart();
597 JQPlotPieChart.prototype.constructor = JQPlotPieChart;
599 JQPlotPieChart.prototype.populateOptions = function (dataTable, options) {
600     var optional = {
601         highlighter: {
602             show: true,
603             tooltipAxes: 'xy',
604             formatString:'%s, %d',
605             useAxesFormatters: false
606         }
607     };
608     var compulsory = {
609         seriesDefaults : {
610             renderer : $.jqplot.PieRenderer
611         }
612     };
613     $.extend(true, optional, options, compulsory);
614     return optional;
617 JQPlotPieChart.prototype.prepareData = function (dataTable) {
618     var data = dataTable.getData(), row;
619     var retData = [];
620     for (var i = 0; i < data.length; i++) {
621         row = data[i];
622         retData.push([ row[0], row[1] ]);
623     }
624     return [ retData ];
628  * Chart factory that returns JQPlotCharts
629  */
630 var JQPlotChartFactory = function () {
632 JQPlotChartFactory.prototype = new ChartFactory();
633 JQPlotChartFactory.prototype.createChart = function (type, elementId) {
634     var chart = null;
635     switch (type) {
636     case ChartType.LINE:
637         chart = new JQPlotLineChart(elementId);
638         break;
639     case ChartType.SPLINE:
640         chart = new JQPlotSplineChart(elementId);
641         break;
642     case ChartType.TIMELINE:
643         chart = new JQPlotTimelineChart(elementId);
644         break;
645     case ChartType.AREA:
646         chart = new JQPlotAreaChart(elementId);
647         break;
648     case ChartType.BAR:
649         chart = new JQPlotBarChart(elementId);
650         break;
651     case ChartType.COLUMN:
652         chart = new JQPlotColumnChart(elementId);
653         break;
654     case ChartType.PIE:
655         chart = new JQPlotPieChart(elementId);
656         break;
657     case ChartType.SCATTER:
658         chart = new JQPlotScatterChart(elementId);
659         break;
660     }
662     return chart;