Translated using Weblate (Slovenian)
[phpmyadmin.git] / js / table / chart.js
blob29905e41b1f278b6f7581734d0c5a14474ce2977
2 /* global ColumnType, DataTable, JQPlotChartFactory */ // js/chart.js
3 /* global codeMirrorEditor */ // js/functions.js
5 var chartData = {};
6 var tempChartTitle;
8 var currentChart = null;
9 var currentSettings = null;
11 var dateTimeCols = [];
12 var numericCols = [];
14 function extractDate (dateString) {
15     var matches;
16     var match;
17     var dateTimeRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/;
18     var dateRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
20     matches = dateTimeRegExp.exec(dateString);
21     if (matches !== null && matches.length > 0) {
22         match = matches[0];
23         return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2), match.substr(11, 2), match.substr(14, 2), match.substr(17, 2));
24     } else {
25         matches = dateRegExp.exec(dateString);
26         if (matches !== null && matches.length > 0) {
27             match = matches[0];
28             return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2));
29         }
30     }
31     return null;
34 function queryChart (data, columnNames, settings) {
35     if ($('#querychart').length === 0) {
36         return;
37     }
39     var plotSettings = {
40         title : {
41             text : settings.title,
42             escapeHtml: true
43         },
44         grid : {
45             drawBorder : false,
46             shadow : false,
47             background : 'rgba(0,0,0,0)'
48         },
49         legend : {
50             show : true,
51             placement : 'outsideGrid',
52             location : 'e',
53             rendererOptions: {
54                 numberColumns: 2
55             }
56         },
57         axes : {
58             xaxis : {
59                 label : Functions.escapeHtml(settings.xaxisLabel)
60             },
61             yaxis : {
62                 label : settings.yaxisLabel
63             }
64         },
65         stackSeries : settings.stackSeries
66     };
68     // create the chart
69     var factory = new JQPlotChartFactory();
70     var chart = factory.createChart(settings.type, 'querychart');
72     // create the data table and add columns
73     var dataTable = new DataTable();
74     if (settings.type === 'timeline') {
75         dataTable.addColumn(ColumnType.DATE, columnNames[settings.mainAxis]);
76     } else if (settings.type === 'scatter') {
77         dataTable.addColumn(ColumnType.NUMBER, columnNames[settings.mainAxis]);
78     } else {
79         dataTable.addColumn(ColumnType.STRING, columnNames[settings.mainAxis]);
80     }
82     var i;
83     var values = [];
84     if (settings.seriesColumn === null) {
85         $.each(settings.selectedSeries, function (index, element) {
86             dataTable.addColumn(ColumnType.NUMBER, columnNames[element]);
87         });
89         // set data to the data table
90         var columnsToExtract = [settings.mainAxis];
91         $.each(settings.selectedSeries, function (index, element) {
92             columnsToExtract.push(element);
93         });
94         var newRow;
95         var row;
96         var col;
97         for (i = 0; i < data.length; i++) {
98             row = data[i];
99             newRow = [];
100             for (var j = 0; j < columnsToExtract.length; j++) {
101                 col = columnNames[columnsToExtract[j]];
102                 if (j === 0) {
103                     if (settings.type === 'timeline') { // first column is date type
104                         newRow.push(extractDate(row[col]));
105                     } else if (settings.type === 'scatter') {
106                         newRow.push(parseFloat(row[col]));
107                     } else { // first column is string type
108                         newRow.push(row[col]);
109                     }
110                 } else { // subsequent columns are of type, number
111                     newRow.push(parseFloat(row[col]));
112                 }
113             }
114             values.push(newRow);
115         }
116         dataTable.setData(values);
117     } else {
118         var seriesNames = {};
119         var seriesNumber = 1;
120         var seriesColumnName = columnNames[settings.seriesColumn];
121         for (i = 0; i < data.length; i++) {
122             if (! seriesNames[data[i][seriesColumnName]]) {
123                 seriesNames[data[i][seriesColumnName]] = seriesNumber;
124                 seriesNumber++;
125             }
126         }
128         $.each(seriesNames, function (seriesName) {
129             dataTable.addColumn(ColumnType.NUMBER, seriesName);
130         });
132         var valueMap = {};
133         var xValue;
134         var value;
135         var mainAxisName = columnNames[settings.mainAxis];
136         var valueColumnName = columnNames[settings.valueColumn];
137         for (i = 0; i < data.length; i++) {
138             xValue = data[i][mainAxisName];
139             value = valueMap[xValue];
140             if (! value) {
141                 value = [xValue];
142                 valueMap[xValue] = value;
143             }
144             seriesNumber = seriesNames[data[i][seriesColumnName]];
145             value[seriesNumber] = parseFloat(data[i][valueColumnName]);
146         }
148         $.each(valueMap, function (index, value) {
149             values.push(value);
150         });
151         dataTable.setData(values);
152     }
154     // draw the chart and return the chart object
155     chart.draw(dataTable, plotSettings);
156     return chart;
159 function drawChart () {
160     currentSettings.width = $('#resizer').width() - 20;
161     currentSettings.height = $('#resizer').height() - 20;
163     // TODO: a better way using .redraw() ?
164     if (currentChart !== null) {
165         currentChart.destroy();
166     }
168     var columnNames = [];
169     $('select[name="chartXAxis"] option').each(function () {
170         columnNames.push(Functions.escapeHtml($(this).text()));
171     });
172     try {
173         currentChart = queryChart(chartData, columnNames, currentSettings);
174         if (currentChart !== null) {
175             $('#saveChart').attr('href', currentChart.toImageString());
176         }
177     } catch (err) {
178         Functions.ajaxShowMessage(err.message, false);
179     }
182 function getSelectedSeries () {
183     var val = $('select[name="chartSeries"]').val() || [];
184     var ret = [];
185     $.each(val, function (i, v) {
186         ret.push(parseInt(v, 10));
187     });
188     return ret;
191 function onXAxisChange () {
192     var $xAxisSelect = $('select[name="chartXAxis"]');
193     currentSettings.mainAxis = parseInt($xAxisSelect.val(), 10);
194     if (dateTimeCols.indexOf(currentSettings.mainAxis) !== -1) {
195         $('span.span_timeline').show();
196     } else {
197         $('span.span_timeline').hide();
198         if (currentSettings.type === 'timeline') {
199             $('input#radio_line').prop('checked', true);
200             currentSettings.type = 'line';
201         }
202     }
203     if (numericCols.indexOf(currentSettings.mainAxis) !== -1) {
204         $('span.span_scatter').show();
205     } else {
206         $('span.span_scatter').hide();
207         if (currentSettings.type === 'scatter') {
208             $('input#radio_line').prop('checked', true);
209             currentSettings.type = 'line';
210         }
211     }
212     var xAxisTitle = $xAxisSelect.children('option:selected').text();
213     $('input[name="xaxis_label"]').val(xAxisTitle);
214     currentSettings.xaxisLabel = xAxisTitle;
217 function onDataSeriesChange () {
218     var $seriesSelect = $('select[name="chartSeries"]');
219     currentSettings.selectedSeries = getSelectedSeries();
220     var yAxisTitle;
221     if (currentSettings.selectedSeries.length === 1) {
222         $('span.span_pie').show();
223         yAxisTitle = $seriesSelect.children('option:selected').text();
224     } else {
225         $('span.span_pie').hide();
226         if (currentSettings.type === 'pie') {
227             $('input#radio_line').prop('checked', true);
228             currentSettings.type = 'line';
229         }
230         yAxisTitle = Messages.strYValues;
231     }
232     $('input[name="yaxis_label"]').val(yAxisTitle);
233     currentSettings.yaxisLabel = yAxisTitle;
237  * Unbind all event handlers before tearing down a page
238  */
239 AJAX.registerTeardown('table/chart.js', function () {
240     $('input[name="chartType"]').off('click');
241     $('input[name="barStacked"]').off('click');
242     $('input[name="chkAlternative"]').off('click');
243     $('input[name="chartTitle"]').off('focus').off('keyup').off('blur');
244     $('select[name="chartXAxis"]').off('change');
245     $('select[name="chartSeries"]').off('change');
246     $('select[name="chartSeriesColumn"]').off('change');
247     $('select[name="chartValueColumn"]').off('change');
248     $('input[name="xaxis_label"]').off('keyup');
249     $('input[name="yaxis_label"]').off('keyup');
250     $('#resizer').off('resizestop');
251     $('#tblchartform').off('submit');
254 AJAX.registerOnload('table/chart.js', function () {
255     // handle manual resize
256     $('#resizer').on('resizestop', function () {
257         // make room so that the handle will still appear
258         $('#querychart').height($('#resizer').height() * 0.96);
259         $('#querychart').width($('#resizer').width() * 0.96);
260         if (currentChart !== null) {
261             currentChart.redraw({
262                 resetAxes : true
263             });
264         }
265     });
267     // handle chart type changes
268     $('input[name="chartType"]').on('click', function () {
269         var type = currentSettings.type = $(this).val();
270         if (type === 'bar' || type === 'column' || type === 'area') {
271             $('span.barStacked').show();
272         } else {
273             $('input[name="barStacked"]').prop('checked', false);
274             $.extend(true, currentSettings, { stackSeries : false });
275             $('span.barStacked').hide();
276         }
277         drawChart();
278     });
280     // handle chosing alternative data format
281     $('input[name="chkAlternative"]').on('click', function () {
282         var $seriesColumn = $('select[name="chartSeriesColumn"]');
283         var $valueColumn  = $('select[name="chartValueColumn"]');
284         var $chartSeries  = $('select[name="chartSeries"]');
285         if ($(this).is(':checked')) {
286             $seriesColumn.prop('disabled', false);
287             $valueColumn.prop('disabled', false);
288             $chartSeries.prop('disabled', true);
289             currentSettings.seriesColumn = parseInt($seriesColumn.val(), 10);
290             currentSettings.valueColumn = parseInt($valueColumn.val(), 10);
291         } else {
292             $seriesColumn.prop('disabled', true);
293             $valueColumn.prop('disabled', true);
294             $chartSeries.prop('disabled', false);
295             currentSettings.seriesColumn = null;
296             currentSettings.valueColumn = null;
297         }
298         drawChart();
299     });
301     // handle stacking for bar, column and area charts
302     $('input[name="barStacked"]').on('click', function () {
303         if ($(this).is(':checked')) {
304             $.extend(true, currentSettings, { stackSeries : true });
305         } else {
306             $.extend(true, currentSettings, { stackSeries : false });
307         }
308         drawChart();
309     });
311     // handle changes in chart title
312     $('input[name="chartTitle"]')
313         .on('focus', function () {
314             tempChartTitle = $(this).val();
315         })
316         .on('keyup', function () {
317             currentSettings.title = $('input[name="chartTitle"]').val();
318             drawChart();
319         })
320         .on('blur', function () {
321             if ($(this).val() !== tempChartTitle) {
322                 drawChart();
323             }
324         });
326     // handle changing the x-axis
327     $('select[name="chartXAxis"]').on('change', function () {
328         onXAxisChange();
329         drawChart();
330     });
332     // handle changing the selected data series
333     $('select[name="chartSeries"]').on('change', function () {
334         onDataSeriesChange();
335         drawChart();
336     });
338     // handle changing the series column
339     $('select[name="chartSeriesColumn"]').on('change', function () {
340         currentSettings.seriesColumn = parseInt($(this).val(), 10);
341         drawChart();
342     });
344     // handle changing the value column
345     $('select[name="chartValueColumn"]').on('change', function () {
346         currentSettings.valueColumn = parseInt($(this).val(), 10);
347         drawChart();
348     });
350     // handle manual changes to the chart x-axis labels
351     $('input[name="xaxis_label"]').on('keyup', function () {
352         currentSettings.xaxisLabel = $(this).val();
353         drawChart();
354     });
356     // handle manual changes to the chart y-axis labels
357     $('input[name="yaxis_label"]').on('keyup', function () {
358         currentSettings.yaxisLabel = $(this).val();
359         drawChart();
360     });
362     // handler for ajax form submission
363     $('#tblchartform').on('submit', function () {
364         var $form = $(this);
365         if (codeMirrorEditor) {
366             $form[0].elements.sql_query.value = codeMirrorEditor.getValue();
367         }
368         if (!Functions.checkSqlQuery($form[0])) {
369             return false;
370         }
372         var $msgbox = Functions.ajaxShowMessage();
373         Functions.prepareForAjaxRequest($form);
374         $.post($form.attr('action'), $form.serialize(), function (data) {
375             if (typeof data !== 'undefined' &&
376                     data.success === true &&
377                     typeof data.chartData !== 'undefined') {
378                 chartData = JSON.parse(data.chartData);
379                 drawChart();
380                 Functions.ajaxRemoveMessage($msgbox);
381             } else {
382                 Functions.ajaxShowMessage(data.error, false);
383             }
384         }, 'json'); // end $.post()
386         return false;
387     });
389     // from jQuery UI
390     $('#resizer').resizable({
391         minHeight: 240,
392         minWidth: 300
393     })
394         .width($('#div_view_options').width() - 50)
395         .trigger('resizestop');
397     currentSettings = {
398         type : 'line',
399         width : $('#resizer').width() - 20,
400         height : $('#resizer').height() - 20,
401         xaxisLabel : $('input[name="xaxis_label"]').val(),
402         yaxisLabel : $('input[name="yaxis_label"]').val(),
403         title : $('input[name="chartTitle"]').val(),
404         stackSeries : false,
405         mainAxis : parseInt($('select[name="chartXAxis"]').val(), 10),
406         selectedSeries : getSelectedSeries(),
407         seriesColumn : null
408     };
410     var vals = $('input[name="dateTimeCols"]').val().split(' ');
411     $.each(vals, function (i, v) {
412         dateTimeCols.push(parseInt(v, 10));
413     });
415     vals = $('input[name="numericCols"]').val().split(' ');
416     $.each(vals, function (i, v) {
417         numericCols.push(parseInt(v, 10));
418     });
420     onXAxisChange();
421     onDataSeriesChange();
423     $('#tblchartform').trigger('submit');