select.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. "use strict";
  2. /**
  3. * @fileoverview JavaScript functions used on /table/search
  4. *
  5. * @requires jQuery
  6. * @requires js/functions.js
  7. */
  8. /* global changeValueFieldType, verifyAfterSearchFieldChange */
  9. // js/table/change.js
  10. /* global openGISEditor, gisEditorLoaded, loadJSAndGISEditor, loadGISEditor */
  11. // js/gis_data_editor.js
  12. var TableSelect = {};
  13. /**
  14. * Checks if given data-type is numeric or date.
  15. *
  16. * @param {string} dataType Column data-type
  17. *
  18. * @return {(boolean|string)}
  19. */
  20. TableSelect.checkIfDataTypeNumericOrDate = function (dataType) {
  21. // To test for numeric data-types.
  22. var numericRegExp = new RegExp('TINYINT|SMALLINT|MEDIUMINT|INT|BIGINT|DECIMAL|FLOAT|DOUBLE|REAL', 'i'); // To test for date data-types.
  23. var dateRegExp = new RegExp('DATETIME|DATE|TIMESTAMP|TIME|YEAR', 'i'); // Return matched data-type
  24. if (numericRegExp.test(dataType)) {
  25. return numericRegExp.exec(dataType)[0];
  26. }
  27. if (dateRegExp.test(dataType)) {
  28. return dateRegExp.exec(dataType)[0];
  29. }
  30. return false;
  31. };
  32. /**
  33. * Unbind all event handlers before tearing down a page
  34. */
  35. AJAX.registerTeardown('table/select.js', function () {
  36. $('#togglesearchformlink').off('click');
  37. $(document).off('submit', '#tbl_search_form.ajax');
  38. $('select.geom_func').off('change');
  39. $(document).off('click', 'span.open_search_gis_editor');
  40. $('body').off('change', 'select[name*="criteriaColumnOperators"]'); // Fix for bug #13778, changed 'click' to 'change'
  41. });
  42. AJAX.registerOnload('table/select.js', function () {
  43. /**
  44. * Prepare a div containing a link, otherwise it's incorrectly displayed
  45. * after a couple of clicks
  46. */
  47. $('<div id="togglesearchformdiv"><a id="togglesearchformlink"></a></div>').insertAfter('#tbl_search_form') // don't show it until we have results on-screen
  48. .hide();
  49. $('#togglesearchformlink').html(Messages.strShowSearchCriteria).on('click', function () {
  50. var $link = $(this);
  51. $('#tbl_search_form').slideToggle();
  52. if ($link.text() === Messages.strHideSearchCriteria) {
  53. $link.text(Messages.strShowSearchCriteria);
  54. } else {
  55. $link.text(Messages.strHideSearchCriteria);
  56. } // avoid default click action
  57. return false;
  58. });
  59. var tableRows = $('#fieldset_table_qbe select.column-operator');
  60. $.each(tableRows, function (index, item) {
  61. $(item).on('change', function () {
  62. changeValueFieldType(this, index);
  63. verifyAfterSearchFieldChange(index, '#tbl_search_form');
  64. });
  65. });
  66. /**
  67. * Ajax event handler for Table search
  68. */
  69. $(document).on('submit', '#tbl_search_form.ajax', function (event) {
  70. var unaryFunctions = ['IS NULL', 'IS NOT NULL', '= \'\'', '!= \'\''];
  71. var geomUnaryFunctions = ['IsEmpty', 'IsSimple', 'IsRing', 'IsClosed']; // jQuery object to reuse
  72. var $searchForm = $(this);
  73. event.preventDefault(); // empty previous search results while we are waiting for new results
  74. $('#sqlqueryresultsouter').empty();
  75. var $msgbox = Functions.ajaxShowMessage(Messages.strSearching, false);
  76. Functions.prepareForAjaxRequest($searchForm);
  77. var values = {};
  78. $searchForm.find(':input').each(function () {
  79. var $input = $(this);
  80. if ($input.attr('type') === 'checkbox' || $input.attr('type') === 'radio') {
  81. if ($input.is(':checked')) {
  82. values[this.name] = $input.val();
  83. }
  84. } else {
  85. values[this.name] = $input.val();
  86. }
  87. });
  88. var columnCount = $('select[name="columnsToDisplay[]"] option').length; // Submit values only for the columns that have unary column operator or a search criteria
  89. for (var a = 0; a < columnCount; a++) {
  90. if ($.inArray(values['criteriaColumnOperators[' + a + ']'], unaryFunctions) >= 0) {
  91. continue;
  92. }
  93. if (values['geom_func[' + a + ']'] && $.inArray(values['geom_func[' + a + ']'], geomUnaryFunctions) >= 0) {
  94. continue;
  95. }
  96. if (values['criteriaValues[' + a + ']'] === '' || values['criteriaValues[' + a + ']'] === null) {
  97. delete values['criteriaValues[' + a + ']'];
  98. delete values['criteriaColumnOperators[' + a + ']'];
  99. delete values['criteriaColumnNames[' + a + ']'];
  100. delete values['criteriaColumnTypes[' + a + ']'];
  101. delete values['criteriaColumnCollations[' + a + ']'];
  102. }
  103. } // If all columns are selected, use a single parameter to indicate that
  104. if (values['columnsToDisplay[]'] !== null) {
  105. if (values['columnsToDisplay[]'].length === columnCount) {
  106. delete values['columnsToDisplay[]'];
  107. values.displayAllColumns = true;
  108. }
  109. } else {
  110. values.displayAllColumns = true;
  111. }
  112. $.post($searchForm.attr('action'), values, function (data) {
  113. Functions.ajaxRemoveMessage($msgbox);
  114. if (typeof data !== 'undefined' && data.success === true) {
  115. if (typeof data.sql_query !== 'undefined') {
  116. // zero rows
  117. $('#sqlqueryresultsouter').html(data.sql_query);
  118. } else {
  119. // results found
  120. $('#sqlqueryresultsouter').html(data.message);
  121. $('.sqlqueryresults').trigger('makegrid');
  122. }
  123. $('#tbl_search_form') // workaround for bug #3168569 - Issue on toggling the "Hide search criteria" in chrome.
  124. .slideToggle().hide();
  125. $('#togglesearchformlink') // always start with the Show message
  126. .text(Messages.strShowSearchCriteria);
  127. $('#togglesearchformdiv') // now it's time to show the div containing the link
  128. .show(); // needed for the display options slider in the results
  129. Functions.initSlider();
  130. $('html, body').animate({
  131. scrollTop: 0
  132. }, 'fast');
  133. } else {
  134. $('#sqlqueryresultsouter').html(data.error);
  135. }
  136. Functions.highlightSql($('#sqlqueryresultsouter'));
  137. }); // end $.post()
  138. }); // Following section is related to the 'function based search' for geometry data types.
  139. // Initially hide all the open_gis_editor spans
  140. $('span.open_search_gis_editor').hide();
  141. $('select.geom_func').on('change', function () {
  142. var $geomFuncSelector = $(this);
  143. var binaryFunctions = ['Contains', 'Crosses', 'Disjoint', 'Equals', 'Intersects', 'Overlaps', 'Touches', 'Within', 'MBRContains', 'MBRDisjoint', 'MBREquals', 'MBRIntersects', 'MBROverlaps', 'MBRTouches', 'MBRWithin', 'ST_Contains', 'ST_Crosses', 'ST_Disjoint', 'ST_Equals', 'ST_Intersects', 'ST_Overlaps', 'ST_Touches', 'ST_Within'];
  144. var tempArray = ['Envelope', 'EndPoint', 'StartPoint', 'ExteriorRing', 'Centroid', 'PointOnSurface'];
  145. var outputGeomFunctions = binaryFunctions.concat(tempArray); // If the chosen function takes two geometry objects as parameters
  146. var $operator = $geomFuncSelector.parents('tr').find('td').eq(4).find('select');
  147. if ($.inArray($geomFuncSelector.val(), binaryFunctions) >= 0) {
  148. $operator.prop('readonly', true);
  149. } else {
  150. $operator.prop('readonly', false);
  151. } // if the chosen function's output is a geometry, enable GIS editor
  152. var $editorSpan = $geomFuncSelector.parents('tr').find('span.open_search_gis_editor');
  153. if ($.inArray($geomFuncSelector.val(), outputGeomFunctions) >= 0) {
  154. $editorSpan.show();
  155. } else {
  156. $editorSpan.hide();
  157. }
  158. });
  159. $(document).on('click', 'span.open_search_gis_editor', function (event) {
  160. event.preventDefault();
  161. var $span = $(this); // Current value
  162. var value = $span.parent('td').children('input[type=\'text\']').val(); // Field name
  163. var field = 'Parameter'; // Column type
  164. var geomFunc = $span.parents('tr').find('.geom_func').val();
  165. var type;
  166. if (geomFunc === 'Envelope') {
  167. type = 'polygon';
  168. } else if (geomFunc === 'ExteriorRing') {
  169. type = 'linestring';
  170. } else {
  171. type = 'point';
  172. } // Names of input field and null checkbox
  173. var inputName = $span.parent('td').children('input[type=\'text\']').attr('name'); // Token
  174. openGISEditor();
  175. if (!gisEditorLoaded) {
  176. loadJSAndGISEditor(value, field, type, inputName);
  177. } else {
  178. loadGISEditor(value, field, type, inputName);
  179. }
  180. });
  181. /**
  182. * Ajax event handler for Range-Search.
  183. */
  184. $('body').on('change', 'select[name*="criteriaColumnOperators"]', function () {
  185. // Fix for bug #13778, changed 'click' to 'change'
  186. var $sourceSelect = $(this); // Get the column name.
  187. var columnName = $(this).closest('tr').find('th').first().text(); // Get the data-type of column excluding size.
  188. var dataType = $(this).closest('tr').find('td[data-type]').attr('data-type');
  189. dataType = TableSelect.checkIfDataTypeNumericOrDate(dataType); // Get the operator.
  190. var operator = $(this).val();
  191. if ((operator === 'BETWEEN' || operator === 'NOT BETWEEN') && dataType) {
  192. var $msgbox = Functions.ajaxShowMessage();
  193. $.ajax({
  194. url: 'index.php?route=/table/search',
  195. type: 'POST',
  196. data: {
  197. 'server': CommonParams.get('server'),
  198. 'ajax_request': 1,
  199. 'db': $('input[name="db"]').val(),
  200. 'table': $('input[name="table"]').val(),
  201. 'column': columnName,
  202. 'range_search': 1
  203. },
  204. success: function success(response) {
  205. Functions.ajaxRemoveMessage($msgbox);
  206. if (response.success) {
  207. // Get the column min value.
  208. var min = response.column_data.min ? '(' + Messages.strColumnMin + ' ' + response.column_data.min + ')' : ''; // Get the column max value.
  209. var max = response.column_data.max ? '(' + Messages.strColumnMax + ' ' + response.column_data.max + ')' : '';
  210. var buttonOptions = {};
  211. buttonOptions[Messages.strGo] = function () {
  212. var minValue = $('#min_value').val();
  213. var maxValue = $('#max_value').val();
  214. var finalValue = '';
  215. if (minValue.length && maxValue.length) {
  216. finalValue = minValue + ', ' + maxValue;
  217. }
  218. var $targetField = $sourceSelect.closest('tr').find('[name*="criteriaValues"]'); // If target field is a select list.
  219. if ($targetField.is('select')) {
  220. $targetField.val(finalValue);
  221. var $options = $targetField.find('option');
  222. var $closestMin = null;
  223. var $closestMax = null; // Find closest min and max value.
  224. $options.each(function () {
  225. if ($closestMin === null || Math.abs($(this).val() - minValue) < Math.abs($closestMin.val() - minValue)) {
  226. $closestMin = $(this);
  227. }
  228. if ($closestMax === null || Math.abs($(this).val() - maxValue) < Math.abs($closestMax.val() - maxValue)) {
  229. $closestMax = $(this);
  230. }
  231. });
  232. $closestMin.attr('selected', 'selected');
  233. $closestMax.attr('selected', 'selected');
  234. } else {
  235. $targetField.val(finalValue);
  236. }
  237. $(this).dialog('close');
  238. };
  239. buttonOptions[Messages.strCancel] = function () {
  240. $(this).dialog('close');
  241. }; // Display dialog box.
  242. $('<div></div>').append('<fieldset>' + '<legend>' + operator + '</legend>' + '<label for="min_value">' + Messages.strMinValue + '</label>' + '<input type="text" id="min_value">' + '<br>' + '<span class="small_font">' + min + '</span>' + '<br>' + '<label for="max_value">' + Messages.strMaxValue + '</label>' + '<input type="text" id="max_value">' + '<br>' + '<span class="small_font">' + max + '</span>' + '</fieldset>').dialog({
  243. width: 'auto',
  244. maxHeight: 400,
  245. modal: true,
  246. buttons: buttonOptions,
  247. title: Messages.strRangeSearch,
  248. open: function open() {
  249. // Add datepicker wherever required.
  250. Functions.addDatepicker($('#min_value'), dataType);
  251. Functions.addDatepicker($('#max_value'), dataType);
  252. },
  253. close: function close() {
  254. $(this).remove();
  255. }
  256. });
  257. } else {
  258. Functions.ajaxShowMessage(response.error);
  259. }
  260. },
  261. error: function error() {
  262. Functions.ajaxShowMessage(Messages.strErrorProcessingRequest);
  263. }
  264. });
  265. }
  266. });
  267. var windowWidth = $(window).width();
  268. $('.jsresponsive').css('max-width', windowWidth - 69 + 'px');
  269. });