1 // TODO: change the axis
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 ** @fileoverview JavaScript functions used on tbl_select.php
7 ** @requires js/functions.js
14 function displayHelp() {
16 .append(PMA_messages.strDisplayHelp)
17 .appendTo('#page_content')
21 title: PMA_messages.strHelpTitle
27 ** Extend the array object for max function
30 Array.max = function (array) {
31 return Math.max.apply(Math, array);
35 ** Extend the array object for min function
38 Array.min = function (array) {
39 return Math.min.apply(Math, array);
43 ** Checks if a string contains only numeric value
44 ** @param n: String (to be checked)
46 function isNumeric(n) {
47 return !isNaN(parseFloat(n)) && isFinite(n);
51 ** Checks if an object is empty
52 ** @param n: Object (to be checked)
54 function isEmpty(obj) {
63 ** Converts a date/time into timestamp
64 ** @param val String Date
65 ** @param type Sring Field type(datetime/timestamp/time/date)
67 function getTimeStamp(val, type) {
68 if (type.toString().search(/datetime/i) != -1 ||
69 type.toString().search(/timestamp/i) != -1
71 return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', val);
73 else if (type.toString().search(/time/i) != -1) {
74 return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', '1970-01-01 ' + val);
76 else if (type.toString().search(/date/i) != -1) {
77 return $.datepicker.parseDate('yy-mm-dd', val);
82 ** Classifies the field type into numeric,timeseries or text
83 ** @param field: field type (as in database structure)
85 function getType(field) {
86 if (field.toString().search(/int/i) != -1 ||
87 field.toString().search(/decimal/i) != -1 ||
88 field.toString().search(/year/i) != -1
91 } else if (field.toString().search(/time/i) != -1 ||
92 field.toString().search(/date/i) != -1
100 ** Converts a categorical array into numeric array
101 ** @param array categorical values array
103 function getCord(arr) {
105 var original = $.extend(true, [], arr);
106 arr = jQuery.unique(arr).sort();
107 $.each(original, function (index, value) {
108 newCord.push(jQuery.inArray(value, arr));
110 return [newCord, arr, original];
114 ** Scrolls the view to the display section
116 function scrollToChart() {
117 var x = $('#dataDisplay').offset().top - 100; // 100 provides buffer in viewport
118 $('html,body').animate({scrollTop: x}, 500);
122 * Unbind all event handlers before tearing down a page
124 AJAX.registerTeardown('tbl_zoom_plot_jqplot.js', function () {
125 $('#tableid_0').unbind('change');
126 $('#tableid_1').unbind('change');
127 $('#tableid_2').unbind('change');
128 $('#tableid_3').unbind('change');
129 $('#inputFormSubmitId').unbind('click');
130 $('#togglesearchformlink').unbind('click');
131 $(document).off('keydown', "#dataDisplay :input");
132 $('button.button-reset').unbind('click');
133 $('div#resizer').unbind('resizestop');
134 $('div#querychart').unbind('jqplotDataClick');
137 AJAX.registerOnload('tbl_zoom_plot_jqplot.js', function () {
138 var cursorMode = ($("input[name='mode']:checked").val() == 'edit') ? 'crosshair' : 'pointer';
139 var currentChart = null;
140 var searchedDataKey = null;
141 var xLabel = $('#tableid_0').val();
142 var yLabel = $('#tableid_1').val();
143 // will be updated via Ajax
144 var xType = $('#types_0').val();
145 var yType = $('#types_1').val();
146 var dataLabel = $('#dataLabel').val();
155 searchedData = jQuery.parseJSON($('#querydata').html());
161 ** Input form submit on field change
164 // first column choice corresponds to the X axis
165 $('#tableid_0').change(function () {
166 //AJAX request for field type, collation, operators, and value field
167 $.post('tbl_zoom_select.php', {
168 'ajax_request' : true,
169 'change_tbl_info' : true,
170 'server' : PMA_commonParams.get('server'),
171 'db' : PMA_commonParams.get('db'),
172 'table' : PMA_commonParams.get('table'),
173 'field' : $('#tableid_0').val(),
175 'token' : PMA_commonParams.get('token')
177 $('#tableFieldsId').find('tr:eq(1) td:eq(0)').html(data.field_type);
178 $('#tableFieldsId').find('tr:eq(1) td:eq(1)').html(data.field_collation);
179 $('#tableFieldsId').find('tr:eq(1) td:eq(2)').html(data.field_operators);
180 $('#tableFieldsId').find('tr:eq(1) td:eq(3)').html(data.field_value);
181 xLabel = $('#tableid_0').val();
182 $('#types_0').val(data.field_type);
183 xType = data.field_type;
184 $('#collations_0').val(data.field_collations);
189 // second column choice corresponds to the Y axis
190 $('#tableid_1').change(function () {
191 //AJAX request for field type, collation, operators, and value field
192 $.post('tbl_zoom_select.php', {
193 'ajax_request' : true,
194 'change_tbl_info' : true,
195 'server' : PMA_commonParams.get('server'),
196 'db' : PMA_commonParams.get('db'),
197 'table' : PMA_commonParams.get('table'),
198 'field' : $('#tableid_1').val(),
200 'token' : PMA_commonParams.get('token')
202 $('#tableFieldsId').find('tr:eq(3) td:eq(0)').html(data.field_type);
203 $('#tableFieldsId').find('tr:eq(3) td:eq(1)').html(data.field_collation);
204 $('#tableFieldsId').find('tr:eq(3) td:eq(2)').html(data.field_operators);
205 $('#tableFieldsId').find('tr:eq(3) td:eq(3)').html(data.field_value);
206 yLabel = $('#tableid_1').val();
207 $('#types_1').val(data.field_type);
208 yType = data.field_type;
209 $('#collations_1').val(data.field_collations);
214 $('#tableid_2').change(function () {
215 //AJAX request for field type, collation, operators, and value field
216 $.post('tbl_zoom_select.php', {
217 'ajax_request' : true,
218 'change_tbl_info' : true,
219 'server' : PMA_commonParams.get('server'),
220 'db' : PMA_commonParams.get('db'),
221 'table' : PMA_commonParams.get('table'),
222 'field' : $('#tableid_2').val(),
224 'token' : PMA_commonParams.get('token')
226 $('#tableFieldsId').find('tr:eq(6) td:eq(0)').html(data.field_type);
227 $('#tableFieldsId').find('tr:eq(6) td:eq(1)').html(data.field_collation);
228 $('#tableFieldsId').find('tr:eq(6) td:eq(2)').html(data.field_operators);
229 $('#tableFieldsId').find('tr:eq(6) td:eq(3)').html(data.field_value);
230 $('#types_2').val(data.field_type);
231 $('#collations_2').val(data.field_collations);
236 $('#tableid_3').change(function () {
237 //AJAX request for field type, collation, operators, and value field
238 $.post('tbl_zoom_select.php', {
239 'ajax_request' : true,
240 'change_tbl_info' : true,
241 'server' : PMA_commonParams.get('server'),
242 'db' : PMA_commonParams.get('db'),
243 'table' : PMA_commonParams.get('table'),
244 'field' : $('#tableid_3').val(),
246 'token' : PMA_commonParams.get('token')
248 $('#tableFieldsId').find('tr:eq(8) td:eq(0)').html(data.field_type);
249 $('#tableFieldsId').find('tr:eq(8) td:eq(1)').html(data.field_collation);
250 $('#tableFieldsId').find('tr:eq(8) td:eq(2)').html(data.field_operators);
251 $('#tableFieldsId').find('tr:eq(8) td:eq(3)').html(data.field_value);
252 $('#types_3').val(data.field_type);
253 $('#collations_3').val(data.field_collations);
259 * Input form validation
261 $('#inputFormSubmitId').click(function () {
262 if ($('#tableid_0').get(0).selectedIndex === 0 || $('#tableid_1').get(0).selectedIndex === 0) {
263 PMA_ajaxShowMessage(PMA_messages.strInputNull);
264 } else if (xLabel == yLabel) {
265 PMA_ajaxShowMessage(PMA_messages.strSameInputs);
270 ** Prepare a div containing a link, otherwise it's incorrectly displayed
271 ** after a couple of clicks
273 $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>')
274 .insertAfter('#zoom_search_form')
275 // don't show it until we have results on-screen
278 $('#togglesearchformlink')
279 .html(PMA_messages.strShowSearchCriteria)
280 .bind('click', function () {
282 $('#zoom_search_form').slideToggle();
283 if ($link.text() == PMA_messages.strHideSearchCriteria) {
284 $link.text(PMA_messages.strShowSearchCriteria);
286 $link.text(PMA_messages.strHideSearchCriteria);
288 // avoid default click action
293 ** Set dialog properties for the data display form
295 var buttonOptions = {};
297 * Handle saving of a row in the editor
299 buttonOptions[PMA_messages.strSave] = function () {
300 //Find changed values by comparing form values with selectedRow Object
301 var newValues = {};//Stores the values changed from original
307 var tempGetVal = function () {
308 return $(this).val();
310 for (key in selectedRow) {
311 var oldVal = selectedRow[key];
312 var newVal = ($('#edit_fields_null_id_' + it).prop('checked')) ? null : $('#edit_fieldID_' + it).val();
313 if (newVal instanceof Array) { // when the column is of type SET
314 newVal = $('#edit_fieldID_' + it).map(tempGetVal).get().join(",");
316 if (oldVal != newVal) {
317 selectedRow[key] = newVal;
318 newValues[key] = newVal;
321 searchedData[searchedDataKey][xLabel] = newVal;
322 } else if (key == yLabel) {
324 searchedData[searchedDataKey][yLabel] = newVal;
327 var $input = $('#edit_fieldID_' + it);
328 if ($input.hasClass('bit')) {
329 sqlTypes[key] = 'bit';
331 sqlTypes[key] = null;
336 //Update the chart series and replot
337 if (xChange || yChange) {
338 //Logic similar to plot generation, replot only if xAxis changes or yAxis changes.
339 //Code includes a lot of checks so as to replot only when necessary
341 xCord[searchedDataKey] = selectedRow[xLabel];
342 // [searchedDataKey][0] contains the x value
343 if (xType == 'numeric') {
344 series[0][searchedDataKey][0] = selectedRow[xLabel];
345 } else if (xType == 'time') {
346 series[0][searchedDataKey][0] =
347 getTimeStamp(selectedRow[xLabel], $('#types_0').val());
349 series[0][searchedDataKey][0] = '';
352 currentChart.series[0].data = series[0];
353 // TODO: axis changing
354 currentChart.replot();
358 yCord[searchedDataKey] = selectedRow[yLabel];
359 // [searchedDataKey][1] contains the y value
360 if (yType == 'numeric') {
361 series[0][searchedDataKey][1] = selectedRow[yLabel];
362 } else if (yType == 'time') {
363 series[0][searchedDataKey][1] =
364 getTimeStamp(selectedRow[yLabel], $('#types_1').val());
366 series[0][searchedDataKey][1] = '';
369 currentChart.series[0].data = series[0];
370 // TODO: axis changing
371 currentChart.replot();
375 //Generate SQL query for update
376 if (!isEmpty(newValues)) {
377 var sql_query = 'UPDATE `' + PMA_commonParams.get('table') + '` SET ';
378 for (key in newValues) {
379 sql_query += '`' + key + '`=';
380 var value = newValues[key];
383 if (value === null) {
384 sql_query += 'NULL, ';
387 } else if ($.trim(value) === '') {
392 // type explicitly identified
393 if (sqlTypes[key] !== null) {
394 if (sqlTypes[key] == 'bit') {
395 sql_query += "b'" + value + "', ";
397 // type not explicitly identified
399 if (!isNumeric(value)) {
400 sql_query += "'" + value + "', ";
402 sql_query += value + ', ';
407 // remove two extraneous characters ', '
408 sql_query = sql_query.substring(0, sql_query.length - 2);
409 sql_query += ' WHERE ' + PMA_urldecode(searchedData[searchedDataKey].where_clause);
411 //Post SQL query to sql.php
413 'token' : PMA_commonParams.get('token'),
414 'server' : PMA_commonParams.get('server'),
415 'db' : PMA_commonParams.get('db'),
416 'ajax_request' : true,
417 'sql_query' : sql_query,
418 'inline_edit' : false
420 if (typeof data !== 'undefined' && data.success === true) {
421 $('#sqlqueryresultsouter').html(data.sql_query);
422 PMA_highlightSQL($('#sqlqueryresultsouter'));
424 PMA_ajaxShowMessage(data.error, false);
427 }//End database update
428 $("#dataDisplay").dialog('close');
430 buttonOptions[PMA_messages.strCancel] = function () {
431 $(this).dialog('close');
433 $("#dataDisplay").dialog({
435 title: PMA_messages.strDataPointContent,
437 buttons: buttonOptions,
438 width: $('#dataDisplay').width() + 80,
440 $(this).find('input[type=checkbox]').css('margin', '0.5em');
444 * Attach Ajax event handlers for input fields
445 * in the dialog. Used to submit the Ajax
446 * request when the ENTER key is pressed.
448 $(document).on('keydown', "#dataDisplay :input", function (e) {
449 if (e.which === 13) { // 13 is the ENTER key
451 if (typeof buttonOptions[PMA_messages.strSave] === 'function') {
452 buttonOptions[PMA_messages.strSave].call();
459 * Generate plot using jqplot
462 if (searchedData !== null) {
463 $('#zoom_search_form')
466 $('#togglesearchformlink')
467 .text(PMA_messages.strShowSearchCriteria);
468 $('#togglesearchformdiv').show();
470 var colorCodes = ['#FF0000', '#00FFFF', '#0000FF', '#0000A0', '#FF0080', '#800080', '#FFFF00', '#00FF00', '#FF00FF'];
476 var xMax; // xAxis extreme max
477 var xMin; // xAxis extreme min
478 var yMax; // yAxis extreme max
479 var yMin; // yAxis extreme min
486 // for a scatter plot
492 background: 'rgba(0,0,0,0)'
496 label: $('#tableid_0').val(),
497 labelRenderer: $.jqplot.CanvasAxisLabelRenderer
500 label: $('#tableid_1').val(),
501 labelRenderer: $.jqplot.CanvasAxisLabelRenderer
508 // hide the first y value
509 formatString: '<span class="hide">%s</span>%s'
518 // If data label is not set, do not show tooltips
519 if (dataLabel === '') {
520 options.highlighter.show = false;
523 // Classify types as either numeric,time,text
524 xType = getType(xType);
525 yType = getType(yType);
527 // could have multiple series but we'll have just one
530 if (xType == 'time') {
531 var originalXType = $('#types_0').val();
532 if (originalXType == 'date') {
535 // TODO: does not seem to work
536 //else if (originalXType == 'time') {
539 // format = '%Y-%m-%d %H:%M';
541 $.extend(options.axes.xaxis, {
542 renderer: $.jqplot.DateAxisRenderer,
548 if (yType == 'time') {
549 var originalYType = $('#types_1').val();
550 if (originalYType == 'date') {
553 $.extend(options.axes.yaxis, {
554 renderer: $.jqplot.DateAxisRenderer,
561 $.each(searchedData, function (key, value) {
562 if (xType == 'numeric') {
563 xVal = parseFloat(value[xLabel]);
565 if (xType == 'time') {
566 xVal = getTimeStamp(value[xLabel], originalXType);
568 if (yType == 'numeric') {
569 yVal = parseFloat(value[yLabel]);
571 if (yType == 'time') {
572 yVal = getTimeStamp(value[yLabel], originalYType);
578 value[dataLabel], // for highlighter
579 // (may set an undefined value)
580 value.where_clause, // for click on point
581 key // key from searchedData
585 // under IE 8, the initial display is mangled; after a manual
587 // under IE 9, everything is fine
588 currentChart = $.jqplot('querychart', series, options);
589 currentChart.resetZoom();
591 $('button.button-reset').click(function (event) {
592 event.preventDefault();
593 currentChart.resetZoom();
596 $('div#resizer').resizable();
597 $('div#resizer').bind('resizestop', function (event, ui) {
598 // make room so that the handle will still appear
599 $('div#querychart').height($('div#resizer').height() * 0.96);
600 $('div#querychart').width($('div#resizer').width() * 0.96);
601 currentChart.replot({resetAxes: true});
604 $('div#querychart').bind('jqplotDataClick',
605 function (event, seriesIndex, pointIndex, data) {
606 searchedDataKey = data[4]; // key from searchedData (global)
609 'ajax_request' : true,
610 'get_data_row' : true,
611 'server' : PMA_commonParams.get('server'),
612 'db' : PMA_commonParams.get('db'),
613 'table' : PMA_commonParams.get('table'),
614 'where_clause' : data[3],
615 'token' : PMA_commonParams.get('token')
618 $.post('tbl_zoom_select.php', post_params, function (data) {
619 // Row is contained in data.row_info,
620 // now fill the displayResultForm with row values
622 for (key in data.row_info) {
623 var $field = $('#edit_fieldID_' + field_id);
624 var $field_null = $('#edit_fields_null_id_' + field_id);
625 if (data.row_info[key] === null) {
626 $field_null.prop('checked', true);
629 $field_null.prop('checked', false);
630 if ($field.attr('multiple')) { // when the column is of type SET
631 $field.val(data.row_info[key].split(','));
633 $field.val(data.row_info[key]);
639 selectedRow = data.row_info;
642 $("#dataDisplay").dialog("open");