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);
72 } else if (type.toString().search(/time/i) !== -1) {
73 return $.datepicker.parseDateTime('yy-mm-dd', 'HH:mm:ss', '1970-01-01 ' + val);
74 } else if (type.toString().search(/date/i) !== -1) {
75 return $.datepicker.parseDate('yy-mm-dd', val);
80 ** Classifies the field type into numeric,timeseries or text
81 ** @param field: field type (as in database structure)
83 function getType (field) {
84 if (field.toString().search(/int/i) !== -1 ||
85 field.toString().search(/decimal/i) !== -1 ||
86 field.toString().search(/year/i) !== -1
89 } else if (field.toString().search(/time/i) !== -1 ||
90 field.toString().search(/date/i) !== -1
99 ** Scrolls the view to the display section
101 function scrollToChart () {
102 var x = $('#dataDisplay').offset().top - 100; // 100 provides buffer in viewport
103 $('html,body').animate({ scrollTop: x }, 500);
107 * Unbind all event handlers before tearing down a page
109 AJAX.registerTeardown('tbl_zoom_plot_jqplot.js', function () {
110 $('#tableid_0').off('change');
111 $('#tableid_1').off('change');
112 $('#tableid_2').off('change');
113 $('#tableid_3').off('change');
114 $('#inputFormSubmitId').off('click');
115 $('#togglesearchformlink').off('click');
116 $(document).off('keydown', '#dataDisplay :input');
117 $('button.button-reset').off('click');
118 $('div#resizer').off('resizestop');
119 $('div#querychart').off('jqplotDataClick');
122 AJAX.registerOnload('tbl_zoom_plot_jqplot.js', function () {
123 var cursorMode = ($('input[name=\'mode\']:checked').val() === 'edit') ? 'crosshair' : 'pointer';
124 var currentChart = null;
125 var searchedDataKey = null;
126 var xLabel = $('#tableid_0').val();
127 var yLabel = $('#tableid_1').val();
128 // will be updated via Ajax
129 var xType = $('#types_0').val();
130 var yType = $('#types_1').val();
131 var dataLabel = $('#dataLabel').val();
140 searchedData = JSON.parse($('#querydata').html());
146 ** Input form submit on field change
149 // first column choice corresponds to the X axis
150 $('#tableid_0').change(function () {
151 // AJAX request for field type, collation, operators, and value field
152 $.post('tbl_zoom_select.php', {
153 'ajax_request' : true,
154 'change_tbl_info' : true,
155 'server' : PMA_commonParams.get('server'),
156 'db' : PMA_commonParams.get('db'),
157 'table' : PMA_commonParams.get('table'),
158 'field' : $('#tableid_0').val(),
161 $('#tableFieldsId').find('tr:eq(1) td:eq(0)').html(data.field_type);
162 $('#tableFieldsId').find('tr:eq(1) td:eq(1)').html(data.field_collation);
163 $('#tableFieldsId').find('tr:eq(1) td:eq(2)').html(data.field_operators);
164 $('#tableFieldsId').find('tr:eq(1) td:eq(3)').html(data.field_value);
165 xLabel = $('#tableid_0').val();
166 $('#types_0').val(data.field_type);
167 xType = data.field_type;
168 $('#collations_0').val(data.field_collations);
173 // second column choice corresponds to the Y axis
174 $('#tableid_1').change(function () {
175 // AJAX request for field type, collation, operators, and value field
176 $.post('tbl_zoom_select.php', {
177 'ajax_request' : true,
178 'change_tbl_info' : true,
179 'server' : PMA_commonParams.get('server'),
180 'db' : PMA_commonParams.get('db'),
181 'table' : PMA_commonParams.get('table'),
182 'field' : $('#tableid_1').val(),
185 $('#tableFieldsId').find('tr:eq(2) td:eq(0)').html(data.field_type);
186 $('#tableFieldsId').find('tr:eq(2) td:eq(1)').html(data.field_collation);
187 $('#tableFieldsId').find('tr:eq(2) td:eq(2)').html(data.field_operators);
188 $('#tableFieldsId').find('tr:eq(2) td:eq(3)').html(data.field_value);
189 yLabel = $('#tableid_1').val();
190 $('#types_1').val(data.field_type);
191 yType = data.field_type;
192 $('#collations_1').val(data.field_collations);
197 $('#tableid_2').change(function () {
198 // AJAX request for field type, collation, operators, and value field
199 $.post('tbl_zoom_select.php', {
200 'ajax_request' : true,
201 'change_tbl_info' : true,
202 'server' : PMA_commonParams.get('server'),
203 'db' : PMA_commonParams.get('db'),
204 'table' : PMA_commonParams.get('table'),
205 'field' : $('#tableid_2').val(),
208 $('#tableFieldsId').find('tr:eq(4) td:eq(0)').html(data.field_type);
209 $('#tableFieldsId').find('tr:eq(4) td:eq(1)').html(data.field_collation);
210 $('#tableFieldsId').find('tr:eq(4) td:eq(2)').html(data.field_operators);
211 $('#tableFieldsId').find('tr:eq(4) td:eq(3)').html(data.field_value);
212 $('#types_2').val(data.field_type);
213 $('#collations_2').val(data.field_collations);
218 $('#tableid_3').change(function () {
219 // AJAX request for field type, collation, operators, and value field
220 $.post('tbl_zoom_select.php', {
221 'ajax_request' : true,
222 'change_tbl_info' : true,
223 'server' : PMA_commonParams.get('server'),
224 'db' : PMA_commonParams.get('db'),
225 'table' : PMA_commonParams.get('table'),
226 'field' : $('#tableid_3').val(),
229 $('#tableFieldsId').find('tr:eq(5) td:eq(0)').html(data.field_type);
230 $('#tableFieldsId').find('tr:eq(5) td:eq(1)').html(data.field_collation);
231 $('#tableFieldsId').find('tr:eq(5) td:eq(2)').html(data.field_operators);
232 $('#tableFieldsId').find('tr:eq(5) td:eq(3)').html(data.field_value);
233 $('#types_3').val(data.field_type);
234 $('#collations_3').val(data.field_collations);
240 * Input form validation
242 $('#inputFormSubmitId').click(function () {
243 if ($('#tableid_0').get(0).selectedIndex === 0 || $('#tableid_1').get(0).selectedIndex === 0) {
244 PMA_ajaxShowMessage(PMA_messages.strInputNull);
245 } else if (xLabel === yLabel) {
246 PMA_ajaxShowMessage(PMA_messages.strSameInputs);
251 ** Prepare a div containing a link, otherwise it's incorrectly displayed
252 ** after a couple of clicks
254 $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>')
255 .insertAfter('#zoom_search_form')
256 // don't show it until we have results on-screen
259 $('#togglesearchformlink')
260 .html(PMA_messages.strShowSearchCriteria)
261 .bind('click', function () {
263 $('#zoom_search_form').slideToggle();
264 if ($link.text() === PMA_messages.strHideSearchCriteria) {
265 $link.text(PMA_messages.strShowSearchCriteria);
267 $link.text(PMA_messages.strHideSearchCriteria);
269 // avoid default click action
274 ** Set dialog properties for the data display form
276 var buttonOptions = {};
278 * Handle saving of a row in the editor
280 buttonOptions[PMA_messages.strSave] = function () {
281 // Find changed values by comparing form values with selectedRow Object
282 var newValues = {};// Stores the values changed from original
288 var tempGetVal = function () {
289 return $(this).val();
291 for (key in selectedRow) {
292 var oldVal = selectedRow[key];
293 var newVal = ($('#edit_fields_null_id_' + it).prop('checked')) ? null : $('#edit_fieldID_' + it).val();
294 if (newVal instanceof Array) { // when the column is of type SET
295 newVal = $('#edit_fieldID_' + it).map(tempGetVal).get().join(',');
297 if (oldVal !== newVal) {
298 selectedRow[key] = newVal;
299 newValues[key] = newVal;
300 if (key === xLabel) {
302 searchedData[searchedDataKey][xLabel] = newVal;
303 } else if (key === yLabel) {
305 searchedData[searchedDataKey][yLabel] = newVal;
308 var $input = $('#edit_fieldID_' + it);
309 if ($input.hasClass('bit')) {
310 sqlTypes[key] = 'bit';
312 sqlTypes[key] = null;
317 // Update the chart series and replot
318 if (xChange || yChange) {
319 // Logic similar to plot generation, replot only if xAxis changes or yAxis changes.
320 // Code includes a lot of checks so as to replot only when necessary
322 xCord[searchedDataKey] = selectedRow[xLabel];
323 // [searchedDataKey][0] contains the x value
324 if (xType === 'numeric') {
325 series[0][searchedDataKey][0] = selectedRow[xLabel];
326 } else if (xType === 'time') {
327 series[0][searchedDataKey][0] =
328 getTimeStamp(selectedRow[xLabel], $('#types_0').val());
330 series[0][searchedDataKey][0] = '';
333 currentChart.series[0].data = series[0];
334 // TODO: axis changing
335 currentChart.replot();
338 yCord[searchedDataKey] = selectedRow[yLabel];
339 // [searchedDataKey][1] contains the y value
340 if (yType === 'numeric') {
341 series[0][searchedDataKey][1] = selectedRow[yLabel];
342 } else if (yType === 'time') {
343 series[0][searchedDataKey][1] =
344 getTimeStamp(selectedRow[yLabel], $('#types_1').val());
346 series[0][searchedDataKey][1] = '';
349 currentChart.series[0].data = series[0];
350 // TODO: axis changing
351 currentChart.replot();
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];
363 if (value === null) {
364 sql_query += 'NULL, ';
367 } else if ($.trim(value) === '') {
368 sql_query += '\'\', ';
372 // type explicitly identified
373 if (sqlTypes[key] !== null) {
374 if (sqlTypes[key] === 'bit') {
375 sql_query += 'b\'' + value + '\', ';
377 // type not explicitly identified
379 if (!isNumeric(value)) {
380 sql_query += '\'' + value + '\', ';
382 sql_query += value + ', ';
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
393 'server' : PMA_commonParams.get('server'),
394 'db' : PMA_commonParams.get('db'),
395 'ajax_request' : true,
396 'sql_query' : sql_query,
397 'inline_edit' : false
399 if (typeof data !== 'undefined' && data.success === true) {
400 $('#sqlqueryresultsouter').html(data.sql_query);
401 PMA_highlightSQL($('#sqlqueryresultsouter'));
403 PMA_ajaxShowMessage(data.error, false);
406 }// End database update
407 $('#dataDisplay').dialog('close');
409 buttonOptions[PMA_messages.strCancel] = function () {
410 $(this).dialog('close');
412 $('#dataDisplay').dialog({
414 title: PMA_messages.strDataPointContent,
416 buttons: buttonOptions,
417 width: $('#dataDisplay').width() + 80,
419 $(this).find('input[type=checkbox]').css('margin', '0.5em');
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.
427 $(document).on('keydown', '#dataDisplay :input', function (e) {
428 if (e.which === 13) { // 13 is the ENTER key
430 if (typeof buttonOptions[PMA_messages.strSave] === 'function') {
431 buttonOptions[PMA_messages.strSave].call();
438 * Generate plot using jqplot
441 if (searchedData !== null) {
442 $('#zoom_search_form')
445 $('#togglesearchformlink')
446 .text(PMA_messages.strShowSearchCriteria);
447 $('#togglesearchformdiv').show();
449 var colorCodes = ['#FF0000', '#00FFFF', '#0000FF', '#0000A0', '#FF0080', '#800080', '#FFFF00', '#00FF00', '#FF00FF'];
456 var xMax; // xAxis extreme max
457 var xMin; // xAxis extreme min
458 var yMax; // yAxis extreme max
459 var yMin; // yAxis extreme min
466 // for a scatter plot
472 background: 'rgba(0,0,0,0)'
476 label: $('#tableid_0').val(),
477 labelRenderer: $.jqplot.CanvasAxisLabelRenderer
480 label: $('#tableid_1').val(),
481 labelRenderer: $.jqplot.CanvasAxisLabelRenderer
488 // hide the first y value
489 formatString: '<span class="hide">%s</span>%s'
498 // If data label is not set, do not show tooltips
499 if (dataLabel === '') {
500 options.highlighter.show = false;
503 // Classify types as either numeric,time,text
504 xType = getType(xType);
505 yType = getType(yType);
507 // could have multiple series but we'll have just one
510 if (xType === 'time') {
511 var originalXType = $('#types_0').val();
512 if (originalXType === 'date') {
515 // TODO: does not seem to work
516 // else if (originalXType === 'time') {
519 // format = '%Y-%m-%d %H:%M';
521 $.extend(options.axes.xaxis, {
522 renderer: $.jqplot.DateAxisRenderer,
528 if (yType === 'time') {
529 var originalYType = $('#types_1').val();
530 if (originalYType === 'date') {
533 $.extend(options.axes.yaxis, {
534 renderer: $.jqplot.DateAxisRenderer,
541 $.each(searchedData, function (key, value) {
542 if (xType === 'numeric') {
543 xVal = parseFloat(value[xLabel]);
545 if (xType === 'time') {
546 xVal = getTimeStamp(value[xLabel], originalXType);
548 if (yType === 'numeric') {
549 yVal = parseFloat(value[yLabel]);
551 if (yType === 'time') {
552 yVal = getTimeStamp(value[yLabel], originalYType);
558 value[dataLabel], // for highlighter
559 // (may set an undefined value)
560 value.where_clause, // for click on point
561 key // key from searchedData
565 // under IE 8, the initial display is mangled; after a manual
567 // under IE 9, everything is fine
568 currentChart = $.jqplot('querychart', series, options);
569 currentChart.resetZoom();
571 $('button.button-reset').click(function (event) {
572 event.preventDefault();
573 currentChart.resetZoom();
576 $('div#resizer').resizable();
577 $('div#resizer').bind('resizestop', function (event, ui) {
578 // make room so that the handle will still appear
579 $('div#querychart').height($('div#resizer').height() * 0.96);
580 $('div#querychart').width($('div#resizer').width() * 0.96);
581 currentChart.replot({ resetAxes: true });
584 $('div#querychart').bind('jqplotDataClick',
585 function (event, seriesIndex, pointIndex, data) {
586 searchedDataKey = data[4]; // key from searchedData (global)
589 'ajax_request' : true,
590 'get_data_row' : true,
591 'server' : PMA_commonParams.get('server'),
592 'db' : PMA_commonParams.get('db'),
593 'table' : PMA_commonParams.get('table'),
594 'where_clause' : data[3]
597 $.post('tbl_zoom_select.php', post_params, function (data) {
598 // Row is contained in data.row_info,
599 // now fill the displayResultForm with row values
601 for (key in data.row_info) {
602 var $field = $('#edit_fieldID_' + field_id);
603 var $field_null = $('#edit_fields_null_id_' + field_id);
604 if (data.row_info[key] === null) {
605 $field_null.prop('checked', true);
608 $field_null.prop('checked', false);
609 if ($field.attr('multiple')) { // when the column is of type SET
610 $field.val(data.row_info[key].split(','));
612 $field.val(data.row_info[key]);
617 selectedRow = data.row_info;
620 $('#dataDisplay').dialog('open');
625 $('#help_dialog').click(function () {