2 /* global ColumnType, DataTable, JQPlotChartFactory */ // js/chart.js
3 /* global codeMirrorEditor */ // js/functions.js
8 var currentChart = null;
9 var currentSettings = null;
11 var dateTimeCols = [];
14 function extractDate (dateString) {
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) {
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));
25 matches = dateRegExp.exec(dateString);
26 if (matches !== null && matches.length > 0) {
28 return new Date(match.substr(0, 4), parseInt(match.substr(5, 2), 10) - 1, match.substr(8, 2));
34 function queryChart (data, columnNames, settings) {
35 if ($('#querychart').length === 0) {
41 text : settings.title,
47 background : 'rgba(0,0,0,0)'
51 placement : 'outsideGrid',
59 label : Functions.escapeHtml(settings.xaxisLabel)
62 label : settings.yaxisLabel
65 stackSeries : settings.stackSeries
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]);
79 dataTable.addColumn(ColumnType.STRING, columnNames[settings.mainAxis]);
84 if (settings.seriesColumn === null) {
85 $.each(settings.selectedSeries, function (index, element) {
86 dataTable.addColumn(ColumnType.NUMBER, columnNames[element]);
89 // set data to the data table
90 var columnsToExtract = [settings.mainAxis];
91 $.each(settings.selectedSeries, function (index, element) {
92 columnsToExtract.push(element);
97 for (i = 0; i < data.length; i++) {
100 for (var j = 0; j < columnsToExtract.length; j++) {
101 col = columnNames[columnsToExtract[j]];
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]);
110 } else { // subsequent columns are of type, number
111 newRow.push(parseFloat(row[col]));
116 dataTable.setData(values);
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;
128 $.each(seriesNames, function (seriesName) {
129 dataTable.addColumn(ColumnType.NUMBER, seriesName);
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];
142 valueMap[xValue] = value;
144 seriesNumber = seriesNames[data[i][seriesColumnName]];
145 value[seriesNumber] = parseFloat(data[i][valueColumnName]);
148 $.each(valueMap, function (index, value) {
151 dataTable.setData(values);
154 // draw the chart and return the chart object
155 chart.draw(dataTable, plotSettings);
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();
168 var columnNames = [];
169 $('select[name="chartXAxis"] option').each(function () {
170 columnNames.push(Functions.escapeHtml($(this).text()));
173 currentChart = queryChart(chartData, columnNames, currentSettings);
174 if (currentChart !== null) {
175 $('#saveChart').attr('href', currentChart.toImageString());
178 Functions.ajaxShowMessage(err.message, false);
182 function getSelectedSeries () {
183 var val = $('select[name="chartSeries"]').val() || [];
185 $.each(val, function (i, v) {
186 ret.push(parseInt(v, 10));
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();
197 $('span.span_timeline').hide();
198 if (currentSettings.type === 'timeline') {
199 $('input#radio_line').prop('checked', true);
200 currentSettings.type = 'line';
203 if (numericCols.indexOf(currentSettings.mainAxis) !== -1) {
204 $('span.span_scatter').show();
206 $('span.span_scatter').hide();
207 if (currentSettings.type === 'scatter') {
208 $('input#radio_line').prop('checked', true);
209 currentSettings.type = 'line';
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();
221 if (currentSettings.selectedSeries.length === 1) {
222 $('span.span_pie').show();
223 yAxisTitle = $seriesSelect.children('option:selected').text();
225 $('span.span_pie').hide();
226 if (currentSettings.type === 'pie') {
227 $('input#radio_line').prop('checked', true);
228 currentSettings.type = 'line';
230 yAxisTitle = Messages.strYValues;
232 $('input[name="yaxis_label"]').val(yAxisTitle);
233 currentSettings.yaxisLabel = yAxisTitle;
237 * Unbind all event handlers before tearing down a page
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({
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();
273 $('input[name="barStacked"]').prop('checked', false);
274 $.extend(true, currentSettings, { stackSeries : false });
275 $('span.barStacked').hide();
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);
292 $seriesColumn.prop('disabled', true);
293 $valueColumn.prop('disabled', true);
294 $chartSeries.prop('disabled', false);
295 currentSettings.seriesColumn = null;
296 currentSettings.valueColumn = null;
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 });
306 $.extend(true, currentSettings, { stackSeries : false });
311 // handle changes in chart title
312 $('input[name="chartTitle"]')
313 .on('focus', function () {
314 tempChartTitle = $(this).val();
316 .on('keyup', function () {
317 currentSettings.title = $('input[name="chartTitle"]').val();
320 .on('blur', function () {
321 if ($(this).val() !== tempChartTitle) {
326 // handle changing the x-axis
327 $('select[name="chartXAxis"]').on('change', function () {
332 // handle changing the selected data series
333 $('select[name="chartSeries"]').on('change', function () {
334 onDataSeriesChange();
338 // handle changing the series column
339 $('select[name="chartSeriesColumn"]').on('change', function () {
340 currentSettings.seriesColumn = parseInt($(this).val(), 10);
344 // handle changing the value column
345 $('select[name="chartValueColumn"]').on('change', function () {
346 currentSettings.valueColumn = parseInt($(this).val(), 10);
350 // handle manual changes to the chart x-axis labels
351 $('input[name="xaxis_label"]').on('keyup', function () {
352 currentSettings.xaxisLabel = $(this).val();
356 // handle manual changes to the chart y-axis labels
357 $('input[name="yaxis_label"]').on('keyup', function () {
358 currentSettings.yaxisLabel = $(this).val();
362 // handler for ajax form submission
363 $('#tblchartform').on('submit', function () {
365 if (codeMirrorEditor) {
366 $form[0].elements.sql_query.value = codeMirrorEditor.getValue();
368 if (!Functions.checkSqlQuery($form[0])) {
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);
380 Functions.ajaxRemoveMessage($msgbox);
382 Functions.ajaxShowMessage(data.error, false);
384 }, 'json'); // end $.post()
390 $('#resizer').resizable({
394 .width($('#div_view_options').width() - 50)
395 .trigger('resizestop');
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(),
405 mainAxis : parseInt($('select[name="chartXAxis"]').val(), 10),
406 selectedSeries : getSelectedSeries(),
410 var vals = $('input[name="dateTimeCols"]').val().split(' ');
411 $.each(vals, function (i, v) {
412 dateTimeCols.push(parseInt(v, 10));
415 vals = $('input[name="numericCols"]').val().split(' ');
416 $.each(vals, function (i, v) {
417 numericCols.push(parseInt(v, 10));
421 onDataSeriesChange();
423 $('#tblchartform').trigger('submit');