Translated using Weblate (Italian)
[phpmyadmin.git] / js / tbl_zoom_plot_jqplot.js
blobe7aff887f795286baa02259493f57bbf75e402d8
1 // TODO: change the axis
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4  ** @fileoverview JavaScript functions used on tbl_select.php
5  **
6  ** @requires    jQuery
7  ** @requires    js/functions.js
8  **/
11 /**
12  **  Display Help/Info
13  **/
14 function displayHelp() {
15     PMA_ajaxShowMessage(PMA_messages.strDisplayHelp, 10000);
18 /**
19  ** Extend the array object for max function
20  ** @param array
21  **/
22 Array.max = function (array) {
23     return Math.max.apply(Math, array);
26 /**
27  ** Extend the array object for min function
28  ** @param array
29  **/
30 Array.min = function (array) {
31     return Math.min.apply(Math, array);
34 /**
35  ** Checks if a string contains only numeric value
36  ** @param n: String (to be checked)
37  **/
38 function isNumeric(n) {
39     return !isNaN(parseFloat(n)) && isFinite(n);
42 /**
43  ** Checks if an object is empty
44  ** @param n: Object (to be checked)
45  **/
46 function isEmpty(obj) {
47     var name;
48     for (name in obj) {
49         return false;
50     }
51     return true;
54 /**
55  ** Converts a date/time into timestamp
56  ** @param val  String Date
57  ** @param type Sring  Field type(datetime/timestamp/time/date)
58  **/
59 function getTimeStamp(val, type) {
60     if (type.toString().search(/datetime/i) != -1 ||
61         type.toString().search(/timestamp/i) != -1
62     ) {
63         return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', val);
64     }
65     else if (type.toString().search(/time/i) != -1) {
66         return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', '1970-01-01 ' + val);
67     }
68     else if (type.toString().search(/date/i) != -1) {
69         return $.datepicker.parseDate('yy-mm-dd', val);
70     }
73 /**
74  ** Classifies the field type into numeric,timeseries or text
75  ** @param field: field type (as in database structure)
76  **/
77 function getType(field) {
78     if (field.toString().search(/int/i) != -1 ||
79         field.toString().search(/decimal/i) != -1 ||
80         field.toString().search(/year/i) != -1
81     ) {
82         return 'numeric';
83     } else if (field.toString().search(/time/i) != -1 ||
84         field.toString().search(/date/i) != -1
85     ) {
86         return 'time';
87     } else {
88         return 'text';
89     }
91 /**
92  ** Converts a categorical array into numeric array
93  ** @param array categorical values array
94  **/
95 function getCord(arr) {
96     var newCord = [];
97     var original = $.extend(true, [], arr);
98     arr = jQuery.unique(arr).sort();
99     $.each(original, function (index, value) {
100         newCord.push(jQuery.inArray(value, arr));
101     });
102     return [newCord, arr, original];
106  ** Scrolls the view to the display section
107  **/
108 function scrollToChart() {
109     var x = $('#dataDisplay').offset().top - 100; // 100 provides buffer in viewport
110     $('html,body').animate({scrollTop: x}, 500);
114  * Unbind all event handlers before tearing down a page
115  */
116 AJAX.registerTeardown('tbl_zoom_plot_jqplot.js', function () {
117     $('#tableid_0').unbind('change');
118     $('#tableid_1').unbind('change');
119     $('#tableid_2').unbind('change');
120     $('#tableid_3').unbind('change');
121     $('#inputFormSubmitId').unbind('click');
122     $('#togglesearchformlink').unbind('click');
123     $("#dataDisplay").find(':input').die('keydown');
124     $('button.button-reset').unbind('click');
125     $('div#resizer').unbind('resizestop');
126     $('div#querychart').unbind('jqplotDataClick');
129 AJAX.registerOnload('tbl_zoom_plot_jqplot.js', function () {
130     var cursorMode = ($("input[name='mode']:checked").val() == 'edit') ? 'crosshair' : 'pointer';
131     var currentChart = null;
132     var searchedDataKey = null;
133     var xLabel = $('#tableid_0').val();
134     var yLabel = $('#tableid_1').val();
135     // will be updated via Ajax
136     var xType = $('#types_0').val();
137     var yType = $('#types_1').val();
138     var dataLabel = $('#dataLabel').val();
139     var lastX;
140     var lastY;
141     var zoomRatio = 1;
144     // Get query result
145     var searchedData = jQuery.parseJSON($('#querydata').html());
147     /**
148      ** Input form submit on field change
149      **/
151     // first column choice corresponds to the X axis
152     $('#tableid_0').change(function () {
153         //AJAX request for field type, collation, operators, and value field
154         $.post('tbl_zoom_select.php', {
155             'ajax_request' : true,
156             'change_tbl_info' : true,
157             'db' : PMA_commonParams.get('db'),
158             'table' : PMA_commonParams.get('table'),
159             'field' : $('#tableid_0').val(),
160             'it' : 0,
161             'token' : PMA_commonParams.get('token')
162         }, function (data) {
163             $('#tableFieldsId tr:eq(1) td:eq(0)').html(data.field_type);
164             $('#tableFieldsId tr:eq(1) td:eq(1)').html(data.field_collation);
165             $('#tableFieldsId tr:eq(1) td:eq(2)').html(data.field_operators);
166             $('#tableFieldsId tr:eq(1) td:eq(3)').html(data.field_value);
167             xLabel = $('#tableid_0').val();
168             $('#types_0').val(data.field_type);
169             xType = data.field_type;
170             $('#collations_0').val(data.field_collations);
171             addDateTimePicker();
172         });
173     });
175     // second column choice corresponds to the Y axis
176     $('#tableid_1').change(function () {
177         //AJAX request for field type, collation, operators, and value field
178         $.post('tbl_zoom_select.php', {
179             'ajax_request' : true,
180             'change_tbl_info' : true,
181             'db' : PMA_commonParams.get('db'),
182             'table' : PMA_commonParams.get('table'),
183             'field' : $('#tableid_1').val(),
184             'it' : 1,
185             'token' : PMA_commonParams.get('token')
186         }, function (data) {
187             $('#tableFieldsId tr:eq(3) td:eq(0)').html(data.field_type);
188             $('#tableFieldsId tr:eq(3) td:eq(1)').html(data.field_collation);
189             $('#tableFieldsId tr:eq(3) td:eq(2)').html(data.field_operators);
190             $('#tableFieldsId tr:eq(3) td:eq(3)').html(data.field_value);
191             yLabel = $('#tableid_1').val();
192             $('#types_1').val(data.field_type);
193             yType = data.field_type;
194             $('#collations_1').val(data.field_collations);
195             addDateTimePicker();
196         });
197     });
199     $('#tableid_2').change(function () {
200         //AJAX request for field type, collation, operators, and value field
201         $.post('tbl_zoom_select.php', {
202             'ajax_request' : true,
203             'change_tbl_info' : true,
204             'db' : PMA_commonParams.get('db'),
205             'table' : PMA_commonParams.get('table'),
206             'field' : $('#tableid_2').val(),
207             'it' : 2,
208             'token' : PMA_commonParams.get('token')
209         }, function (data) {
210             $('#tableFieldsId tr:eq(6) td:eq(0)').html(data.field_type);
211             $('#tableFieldsId tr:eq(6) td:eq(1)').html(data.field_collation);
212             $('#tableFieldsId tr:eq(6) td:eq(2)').html(data.field_operators);
213             $('#tableFieldsId tr:eq(6) td:eq(3)').html(data.field_value);
214             $('#types_2').val(data.field_type);
215             $('#collations_2').val(data.field_collations);
216             addDateTimePicker();
217         });
218     });
220     $('#tableid_3').change(function () {
221         //AJAX request for field type, collation, operators, and value field
222         $.post('tbl_zoom_select.php', {
223             'ajax_request' : true,
224             'change_tbl_info' : true,
225             'db' : PMA_commonParams.get('db'),
226             'table' : PMA_commonParams.get('table'),
227             'field' : $('#tableid_3').val(),
228             'it' : 3,
229             'token' : PMA_commonParams.get('token')
230         }, function (data) {
231             $('#tableFieldsId tr:eq(8) td:eq(0)').html(data.field_type);
232             $('#tableFieldsId tr:eq(8) td:eq(1)').html(data.field_collation);
233             $('#tableFieldsId tr:eq(8) td:eq(2)').html(data.field_operators);
234             $('#tableFieldsId tr:eq(8) td:eq(3)').html(data.field_value);
235             $('#types_3').val(data.field_type);
236             $('#collations_3').val(data.field_collations);
237             addDateTimePicker();
238         });
239     });
241     /**
242      * Input form validation
243      **/
244     $('#inputFormSubmitId').click(function () {
245         if ($('#tableid_0').get(0).selectedIndex === 0 || $('#tableid_1').get(0).selectedIndex === 0) {
246             PMA_ajaxShowMessage(PMA_messages.strInputNull);
247         } else if (xLabel == yLabel) {
248             PMA_ajaxShowMessage(PMA_messages.strSameInputs);
249         }
250     });
252     /**
253       ** Prepare a div containing a link, otherwise it's incorrectly displayed
254       ** after a couple of clicks
255       **/
256     $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>')
257         .insertAfter('#zoom_search_form')
258         // don't show it until we have results on-screen
259         .hide();
261     $('#togglesearchformlink')
262         .html(PMA_messages.strShowSearchCriteria)
263         .bind('click', function () {
264             var $link = $(this);
265             $('#zoom_search_form').slideToggle();
266             if ($link.text() == PMA_messages.strHideSearchCriteria) {
267                 $link.text(PMA_messages.strShowSearchCriteria);
268             } else {
269                 $link.text(PMA_messages.strHideSearchCriteria);
270             }
271             // avoid default click action
272             return false;
273         });
275     /**
276      ** Set dialog properties for the data display form
277      **/
278     var buttonOptions = {};
279     /*
280      * Handle saving of a row in the editor
281      */
282     buttonOptions[PMA_messages.strSave] = function () {
283         //Find changed values by comparing form values with selectedRow Object
284         var newValues = {};//Stores the values changed from original
285         var sqlTypes = {};
286         var it = 0;
287         var xChange = false;
288         var yChange = false;
289         var key;
290         for (key in selectedRow) {
291             var oldVal = selectedRow[key];
292             var newVal = ($('#edit_fields_null_id_' + it).prop('checked')) ? null : $('#edit_fieldID_' + it).val();
293             if (newVal instanceof Array) { // when the column is of type SET
294                 newVal =  $('#edit_fieldID_' + it).map(function () {
295                     return $(this).val();
296                 }).get().join(",");
297             }
298             if (oldVal != newVal) {
299                 selectedRow[key] = newVal;
300                 newValues[key] = newVal;
301                 if (key == xLabel) {
302                     xChange = true;
303                     searchedData[searchedDataKey][xLabel] = newVal;
304                 } else if (key == yLabel) {
305                     yChange = true;
306                     searchedData[searchedDataKey][yLabel] = newVal;
307                 }
308             }
309             var $input = $('#edit_fieldID_' + it);
310             if ($input.hasClass('bit')) {
311                 sqlTypes[key] = 'bit';
312             } else {
313                 sqlTypes[key] = null;
314             }
315             it++;
316         } //End data update
318         //Update the chart series and replot
319         if (xChange || yChange) {
320             //Logic similar to plot generation, replot only if xAxis changes or yAxis changes.
321             //Code includes a lot of checks so as to replot only when necessary
322             if (xChange) {
323                 xCord[searchedDataKey] = selectedRow[xLabel];
324                 // [searchedDataKey][0] contains the x value
325                 if (xType == 'numeric') {
326                     series[0][searchedDataKey][0] = selectedRow[xLabel];
327                 } else if (xType == 'time') {
328                     series[0][searchedDataKey][0] =
329                         getTimeStamp(selectedRow[xLabel], $('#types_0').val());
330                 } else {
331                     // TODO: text values
332                 }
333                 currentChart.series[0].data = series[0];
334                 // TODO: axis changing
335                 currentChart.replot();
337             }
338             if (yChange) {
339                 yCord[searchedDataKey] = selectedRow[yLabel];
340                 // [searchedDataKey][1] contains the y value
341                 if (yType == 'numeric') {
342                     series[0][searchedDataKey][1] = selectedRow[yLabel];
343                 } else if (yType == 'time') {
344                     series[0][searchedDataKey][1] =
345                         getTimeStamp(selectedRow[yLabel], $('#types_1').val());
346                 } else {
347                     // TODO: text values
348                 }
349                 currentChart.series[0].data = series[0];
350                 // TODO: axis changing
351                 currentChart.replot();
352             }
353         } //End plot update
355         //Generate SQL query for update
356         if (!isEmpty(newValues)) {
357             var sql_query = 'UPDATE `' + PMA_commonParams.get('table') + '` SET ';
358             for (key in newValues) {
359                 sql_query += '`' + key + '`=';
360                 var value = newValues[key];
362                 // null
363                 if (value === null) {
364                     sql_query += 'NULL, ';
366                 // empty
367                 } else if ($.trim(value) === '') {
368                     sql_query += "'', ";
370                 // other
371                 } else {
372                     // type explicitly identified
373                     if (sqlTypes[key] !== null) {
374                         if (sqlTypes[key] == 'bit') {
375                             sql_query += "b'" + value + "', ";
376                         }
377                     // type not explicitly identified
378                     } else {
379                         if (!isNumeric(value)) {
380                             sql_query += "'" + value + "', ";
381                         } else {
382                             sql_query += value + ', ';
383                         }
384                     }
385                 }
386             }
387             // remove two extraneous characters ', '
388             sql_query = sql_query.substring(0, sql_query.length - 2);
389             sql_query += ' WHERE ' + PMA_urldecode(searchedData[searchedDataKey].where_clause);
391             //Post SQL query to sql.php
392             $.post('sql.php', {
393                     'token' : PMA_commonParams.get('token'),
394                     'db' : PMA_commonParams.get('db'),
395                     'ajax_request' : true,
396                     'sql_query' : sql_query,
397                     'inline_edit' : false
398                 }, function (data) {
399                     if (data.success === true) {
400                         $('#sqlqueryresults').html(data.sql_query);
401                         $("#sqlqueryresults").trigger('appendAnchor');
402                     } else {
403                         PMA_ajaxShowMessage(data.error, false);
404                     }
405                 }); //End $.post
406         }//End database update
407         $("#dataDisplay").dialog('close');
408     };
409     buttonOptions[PMA_messages.strCancel] = function () {
410         $(this).dialog('close');
411     };
412     $("#dataDisplay").dialog({
413         autoOpen: false,
414         title: PMA_messages.strDataPointContent,
415         modal: true,
416         buttons: buttonOptions,
417         width: $('#dataDisplay').width() + 80,
418         open: function () {
419             $(this).find('input[type=checkbox]').css('margin', '0.5em');
420         }
421     });
422     /**
423      * Attach Ajax event handlers for input fields
424      * in the dialog. Used to submit the Ajax
425      * request when the ENTER key is pressed.
426      */
427     $("#dataDisplay").find(':input').live('keydown', function (e) {
428         if (e.which === 13) { // 13 is the ENTER key
429             e.preventDefault();
430             if (typeof buttonOptions[PMA_messages.strSave] === 'function') {
431                 buttonOptions[PMA_messages.strSave].call();
432             }
433         }
434     });
437     /*
438      * Generate plot using jqplot
439      */
441     if (searchedData !== null) {
442         $('#zoom_search_form')
443          .slideToggle()
444          .hide();
445         $('#togglesearchformlink')
446          .text(PMA_messages.strShowSearchCriteria);
447         $('#togglesearchformdiv').show();
448         var selectedRow;
449         var colorCodes = ['#FF0000', '#00FFFF', '#0000FF', '#0000A0', '#FF0080', '#800080', '#FFFF00', '#00FF00', '#FF00FF'];
450         var series = [];
451         var xCord = [];
452         var yCord = [];
453         var tempX, tempY;
454         var it = 0;
455         var xMax; // xAxis extreme max
456         var xMin; // xAxis extreme min
457         var yMax; // yAxis extreme max
458         var yMin; // yAxis extreme min
459         var xVal;
460         var yVal;
461         var format;
463         var options = {
464             series: [
465                 // for a scatter plot
466                 { showLine: false }
467             ],
468             grid: {
469                 drawBorder: false,
470                 shadow: false,
471                 background: 'rgba(0,0,0,0)'
472             },
473             axes: {
474                 xaxis: {
475                     label: $('#tableid_0').val(),
476                     labelRenderer: $.jqplot.CanvasAxisLabelRenderer
477                 },
478                 yaxis: {
479                     label: $('#tableid_1').val(),
480                     labelRenderer: $.jqplot.CanvasAxisLabelRenderer
481                 }
482             },
483             highlighter: {
484                 show: true,
485                 tooltipAxes: 'y',
486                 yvalues: 2,
487                 // hide the first y value
488                 formatString: '<span class="hide">%s</span>%s'
489             },
490             cursor: {
491                 show: true,
492                 zoom: true,
493                 showTooltip: false
494             }
495         };
497         // If data label is not set, do not show tooltips
498         if (dataLabel === '') {
499             options.highlighter.show = false;
500         }
502         // Classify types as either numeric,time,text
503         xType = getType(xType);
504         yType = getType(yType);
506         // could have multiple series but we'll have just one
507         series[0] = [];
509         if (xType == 'time') {
510             var originalXType = $('#types_0').val();
511             if (originalXType == 'date') {
512                 format = '%Y-%m-%d';
513             }
514             // TODO: does not seem to work
515             //else if (originalXType == 'time') {
516               //  format = '%H:%M';
517             //} else {
518             //    format = '%Y-%m-%d %H:%M';
519             //}
520             $.extend(options.axes.xaxis, {
521                 renderer: $.jqplot.DateAxisRenderer,
522                 tickOptions: {
523                     formatString: format
524                 }
525             });
526         }
527         if (yType == 'time') {
528             var originalYType = $('#types_1').val();
529             if (originalYType == 'date') {
530                 format = '%Y-%m-%d';
531             }
532             $.extend(options.axes.yaxis, {
533                 renderer: $.jqplot.DateAxisRenderer,
534                 tickOptions: {
535                     formatString: format
536                 }
537             });
538         }
540         $.each(searchedData, function (key, value) {
541             if (xType == 'numeric') {
542                 xVal = parseFloat(value[xLabel]);
543             }
544             if (xType == 'time') {
545                 xVal = getTimeStamp(value[xLabel], originalXType);
546             }
547             if (yType == 'numeric') {
548                 yVal = parseFloat(value[yLabel]);
549             }
550             if (yType == 'time') {
551                 yVal = getTimeStamp(value[yLabel], originalYType);
552             }
553             series[0].push([
554                 xVal,
555                 yVal,
556                 // extra Y values
557                 value[dataLabel], // for highlighter
558                                   // (may set an undefined value)
559                 value.where_clause, // for click on point
560                 key               // key from searchedData
561             ]);
562         });
564         // under IE 8, the initial display is mangled; after a manual
565         // resizing, it's ok
566         // under IE 9, everything is fine
567         currentChart = $.jqplot('querychart', series, options);
568         currentChart.resetZoom();
570         $('button.button-reset').click(function (event) {
571             event.preventDefault();
572             currentChart.resetZoom();
573         });
575         $('div#resizer').resizable();
576         $('div#resizer').bind('resizestop', function (event, ui) {
577             // make room so that the handle will still appear
578             $('div#querychart').height($('div#resizer').height() * 0.96);
579             $('div#querychart').width($('div#resizer').width() * 0.96);
580             currentChart.replot({resetAxes: true});
581         });
583         $('div#querychart').bind('jqplotDataClick',
584             function (event, seriesIndex, pointIndex, data) {
585                 searchedDataKey = data[4]; // key from searchedData (global)
586                 var field_id = 0;
587                 var post_params = {
588                     'ajax_request' : true,
589                     'get_data_row' : true,
590                     'db' : PMA_commonParams.get('db'),
591                     'table' : PMA_commonParams.get('table'),
592                     'where_clause' : data[3],
593                     'token' : PMA_commonParams.get('token')
594                 };
596                 $.post('tbl_zoom_select.php', post_params, function (data) {
597                     // Row is contained in data.row_info,
598                     // now fill the displayResultForm with row values
599                     var key;
600                     for (key in data.row_info) {
601                         $field = $('#edit_fieldID_' + field_id);
602                         $field_null = $('#edit_fields_null_id_' + field_id);
603                         if (data.row_info[key] === null) {
604                             $field_null.prop('checked', true);
605                             $field.val('');
606                         } else {
607                             $field_null.prop('checked', false);
608                             if ($field.attr('multiple')) { // when the column is of type SET
609                                 $field.val(data.row_info[key].split(','));
610                             } else {
611                                 $field.val(data.row_info[key]);
612                             }
613                         }
614                         field_id++;
615                     }
616                     selectedRow = {};
617                     selectedRow = data.row_info;
618                 });
620                 $("#dataDisplay").dialog("open");
621             }
622         );
623     }