sql.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. /* vim: set expandtab sw=4 ts=4 sts=4: */
  2. /**
  3. * @fileoverview functions used wherever an sql query form is used
  4. *
  5. * @requires jQuery
  6. * @requires js/functions.js
  7. *
  8. */
  9. var $data_a;
  10. var prevScrollX = 0;
  11. /**
  12. * decode a string URL_encoded
  13. *
  14. * @param string str
  15. * @return string the URL-decoded string
  16. */
  17. function PMA_urldecode (str) {
  18. if (typeof str !== 'undefined') {
  19. return decodeURIComponent(str.replace(/\+/g, '%20'));
  20. }
  21. }
  22. /**
  23. * endecode a string URL_decoded
  24. *
  25. * @param string str
  26. * @return string the URL-encoded string
  27. */
  28. function PMA_urlencode (str) {
  29. if (typeof str !== 'undefined') {
  30. return encodeURIComponent(str).replace(/\%20/g, '+');
  31. }
  32. }
  33. /**
  34. * Saves SQL query in local storage or cookie
  35. *
  36. * @param string SQL query
  37. * @return void
  38. */
  39. function PMA_autosaveSQL (query) {
  40. if (query) {
  41. if (isStorageSupported('localStorage')) {
  42. window.localStorage.auto_saved_sql = query;
  43. } else {
  44. Cookies.set('auto_saved_sql', query);
  45. }
  46. }
  47. }
  48. /**
  49. * Saves SQL query with sort in local storage or cookie
  50. *
  51. * @param string SQL query
  52. * @return void
  53. */
  54. function PMA_autosaveSQLSort (query) {
  55. if (query) {
  56. if (isStorageSupported('localStorage')) {
  57. window.localStorage.auto_saved_sql_sort = query;
  58. } else {
  59. Cookies.set('auto_saved_sql_sort', query);
  60. }
  61. }
  62. }
  63. /**
  64. * Clear saved SQL query with sort in local storage or cookie
  65. *
  66. * @return void
  67. */
  68. function PMA_clearAutoSavedSQLSort () {
  69. if (isStorageSupported('localStorage')) {
  70. window.localStorage.removeItem('auto_saved_sql_sort');
  71. } else {
  72. Cookies.set('auto_saved_sql_sort', '');
  73. }
  74. }
  75. /**
  76. * Get the field name for the current field. Required to construct the query
  77. * for grid editing
  78. *
  79. * @param $table_results enclosing results table
  80. * @param $this_field jQuery object that points to the current field's tr
  81. */
  82. function getFieldName ($table_results, $this_field) {
  83. var this_field_index = $this_field.index();
  84. // ltr or rtl direction does not impact how the DOM was generated
  85. // check if the action column in the left exist
  86. var left_action_exist = !$table_results.find('th:first').hasClass('draggable');
  87. // number of column span for checkbox and Actions
  88. var left_action_skip = left_action_exist ? $table_results.find('th:first').attr('colspan') - 1 : 0;
  89. // If this column was sorted, the text of the a element contains something
  90. // like <small>1</small> that is useful to indicate the order in case
  91. // of a sort on multiple columns; however, we dont want this as part
  92. // of the column name so we strip it ( .clone() to .end() )
  93. var field_name = $table_results
  94. .find('thead')
  95. .find('th:eq(' + (this_field_index - left_action_skip) + ') a')
  96. .clone() // clone the element
  97. .children() // select all the children
  98. .remove() // remove all of them
  99. .end() // go back to the selected element
  100. .text(); // grab the text
  101. // happens when just one row (headings contain no a)
  102. if (field_name === '') {
  103. var $heading = $table_results.find('thead').find('th:eq(' + (this_field_index - left_action_skip) + ')').children('span');
  104. // may contain column comment enclosed in a span - detach it temporarily to read the column name
  105. var $tempColComment = $heading.children().detach();
  106. field_name = $heading.text();
  107. // re-attach the column comment
  108. $heading.append($tempColComment);
  109. }
  110. field_name = $.trim(field_name);
  111. return field_name;
  112. }
  113. /**
  114. * Unbind all event handlers before tearing down a page
  115. */
  116. AJAX.registerTeardown('sql.js', function () {
  117. $(document).off('click', 'a.delete_row.ajax');
  118. $(document).off('submit', '.bookmarkQueryForm');
  119. $('input#bkm_label').off('input');
  120. $(document).off('makegrid', '.sqlqueryresults');
  121. $(document).off('stickycolumns', '.sqlqueryresults');
  122. $('#togglequerybox').off('click');
  123. $(document).off('click', '#button_submit_query');
  124. $(document).off('change', '#id_bookmark');
  125. $('input[name=\'bookmark_variable\']').off('keypress');
  126. $(document).off('submit', '#sqlqueryform.ajax');
  127. $(document).off('click', 'input[name=navig].ajax');
  128. $(document).off('submit', 'form[name=\'displayOptionsForm\'].ajax');
  129. $(document).off('mouseenter', 'th.column_heading.pointer');
  130. $(document).off('mouseleave', 'th.column_heading.pointer');
  131. $(document).off('click', 'th.column_heading.marker');
  132. $(window).off('scroll');
  133. $(document).off('keyup', '.filter_rows');
  134. $(document).off('click', '#printView');
  135. if (codemirror_editor) {
  136. codemirror_editor.off('change');
  137. } else {
  138. $('#sqlquery').off('input propertychange');
  139. }
  140. $('body').off('click', '.navigation .showAllRows');
  141. $('body').off('click', 'a.browse_foreign');
  142. $('body').off('click', '#simulate_dml');
  143. $('body').off('keyup', '#sqlqueryform');
  144. $('body').off('click', 'form[name="resultsForm"].ajax button[name="submit_mult"], form[name="resultsForm"].ajax input[name="submit_mult"]');
  145. });
  146. /**
  147. * @description <p>Ajax scripts for sql and browse pages</p>
  148. *
  149. * Actions ajaxified here:
  150. * <ul>
  151. * <li>Retrieve results of an SQL query</li>
  152. * <li>Paginate the results table</li>
  153. * <li>Sort the results table</li>
  154. * <li>Change table according to display options</li>
  155. * <li>Grid editing of data</li>
  156. * <li>Saving a bookmark</li>
  157. * </ul>
  158. *
  159. * @name document.ready
  160. * @memberOf jQuery
  161. */
  162. AJAX.registerOnload('sql.js', function () {
  163. $(function () {
  164. if (codemirror_editor) {
  165. codemirror_editor.on('change', function () {
  166. PMA_autosaveSQL(codemirror_editor.getValue());
  167. });
  168. } else {
  169. $('#sqlquery').on('input propertychange', function () {
  170. PMA_autosaveSQL($('#sqlquery').val());
  171. });
  172. var useLocalStorageValue = isStorageSupported('localStorage') && typeof window.localStorage.auto_saved_sql_sort !== 'undefined';
  173. // Save sql query with sort
  174. if ($('#RememberSorting') !== undefined && $('#RememberSorting').is(':checked')) {
  175. $('select[name="sql_query"]').on('change', function () {
  176. PMA_autosaveSQLSort($(this).val());
  177. });
  178. $('.sortlink').on('click', function () {
  179. PMA_clearAutoSavedSQLSort();
  180. });
  181. } else {
  182. PMA_clearAutoSavedSQLSort();
  183. }
  184. // If sql query with sort for current table is stored, change sort by key select value
  185. var sortStoredQuery = useLocalStorageValue ? window.localStorage.auto_saved_sql_sort : Cookies.get('auto_saved_sql_sort');
  186. if (typeof sortStoredQuery !== 'undefined' && sortStoredQuery !== $('select[name="sql_query"]').val() && $('select[name="sql_query"] option[value="' + sortStoredQuery + '"]').length !== 0) {
  187. $('select[name="sql_query"]').val(sortStoredQuery).change();
  188. }
  189. }
  190. });
  191. // Delete row from SQL results
  192. $(document).on('click', 'a.delete_row.ajax', function (e) {
  193. e.preventDefault();
  194. var question = PMA_sprintf(PMA_messages.strDoYouReally, escapeHtml($(this).closest('td').find('div').text()));
  195. var $link = $(this);
  196. $link.PMA_confirm(question, $link.attr('href'), function (url) {
  197. $msgbox = PMA_ajaxShowMessage();
  198. var argsep = PMA_commonParams.get('arg_separator');
  199. var params = 'ajax_request=1' + argsep + 'is_js_confirmed=1';
  200. var postData = $link.getPostData();
  201. if (postData) {
  202. params += argsep + postData;
  203. }
  204. $.post(url, params, function (data) {
  205. if (data.success) {
  206. PMA_ajaxShowMessage(data.message);
  207. $link.closest('tr').remove();
  208. } else {
  209. PMA_ajaxShowMessage(data.error, false);
  210. }
  211. });
  212. });
  213. });
  214. // Ajaxification for 'Bookmark this SQL query'
  215. $(document).on('submit', '.bookmarkQueryForm', function (e) {
  216. e.preventDefault();
  217. PMA_ajaxShowMessage();
  218. var argsep = PMA_commonParams.get('arg_separator');
  219. $.post($(this).attr('action'), 'ajax_request=1' + argsep + $(this).serialize(), function (data) {
  220. if (data.success) {
  221. PMA_ajaxShowMessage(data.message);
  222. } else {
  223. PMA_ajaxShowMessage(data.error, false);
  224. }
  225. });
  226. });
  227. /* Hides the bookmarkoptions checkboxes when the bookmark label is empty */
  228. $('input#bkm_label').on('input', function () {
  229. $('input#id_bkm_all_users, input#id_bkm_replace')
  230. .parent()
  231. .toggle($(this).val().length > 0);
  232. }).trigger('input');
  233. /**
  234. * Attach Event Handler for 'Copy to clipbpard
  235. */
  236. $(document).on('click', '#copyToClipBoard', function (event) {
  237. event.preventDefault();
  238. var textArea = document.createElement('textarea');
  239. //
  240. // *** This styling is an extra step which is likely not required. ***
  241. //
  242. // Why is it here? To ensure:
  243. // 1. the element is able to have focus and selection.
  244. // 2. if element was to flash render it has minimal visual impact.
  245. // 3. less flakyness with selection and copying which **might** occur if
  246. // the textarea element is not visible.
  247. //
  248. // The likelihood is the element won't even render, not even a flash,
  249. // so some of these are just precautions. However in IE the element
  250. // is visible whilst the popup box asking the user for permission for
  251. // the web page to copy to the clipboard.
  252. //
  253. // Place in top-left corner of screen regardless of scroll position.
  254. textArea.style.position = 'fixed';
  255. textArea.style.top = 0;
  256. textArea.style.left = 0;
  257. // Ensure it has a small width and height. Setting to 1px / 1em
  258. // doesn't work as this gives a negative w/h on some browsers.
  259. textArea.style.width = '2em';
  260. textArea.style.height = '2em';
  261. // We don't need padding, reducing the size if it does flash render.
  262. textArea.style.padding = 0;
  263. // Clean up any borders.
  264. textArea.style.border = 'none';
  265. textArea.style.outline = 'none';
  266. textArea.style.boxShadow = 'none';
  267. // Avoid flash of white box if rendered for any reason.
  268. textArea.style.background = 'transparent';
  269. textArea.value = '';
  270. $('#serverinfo a').each(function () {
  271. textArea.value += $(this).text().split(':')[1].trim() + '/';
  272. });
  273. textArea.value += '\t\t' + window.location.href;
  274. textArea.value += '\n';
  275. $('.success').each(function () {
  276. textArea.value += $(this).text() + '\n\n';
  277. });
  278. $('.sql pre').each(function () {
  279. textArea.value += $(this).text() + '\n\n';
  280. });
  281. $('.table_results .column_heading a').each(function () {
  282. // Don't copy ordering number text within <small> tag
  283. textArea.value += $(this).clone().find('small').remove().end().text() + '\t';
  284. });
  285. textArea.value += '\n';
  286. $('.table_results tbody tr').each(function () {
  287. $(this).find('.data span').each(function () {
  288. textArea.value += $(this).text() + '\t';
  289. });
  290. textArea.value += '\n';
  291. });
  292. document.body.appendChild(textArea);
  293. textArea.select();
  294. try {
  295. document.execCommand('copy');
  296. } catch (err) {
  297. alert('Sorry! Unable to copy');
  298. }
  299. document.body.removeChild(textArea);
  300. }); // end of Copy to Clipboard action
  301. /**
  302. * Attach Event Handler for 'Print' link
  303. */
  304. $(document).on('click', '#printView', function (event) {
  305. event.preventDefault();
  306. // Take to preview mode
  307. printPreview();
  308. }); // end of 'Print' action
  309. /**
  310. * Attach the {@link makegrid} function to a custom event, which will be
  311. * triggered manually everytime the table of results is reloaded
  312. * @memberOf jQuery
  313. */
  314. $(document).on('makegrid', '.sqlqueryresults', function () {
  315. $('.table_results').each(function () {
  316. PMA_makegrid(this);
  317. });
  318. });
  319. /*
  320. * Attach a custom event for sticky column headings which will be
  321. * triggered manually everytime the table of results is reloaded
  322. * @memberOf jQuery
  323. */
  324. $(document).on('stickycolumns', '.sqlqueryresults', function () {
  325. $('.sticky_columns').remove();
  326. $('.table_results').each(function () {
  327. var $table_results = $(this);
  328. // add sticky columns div
  329. var $stick_columns = initStickyColumns($table_results);
  330. rearrangeStickyColumns($stick_columns, $table_results);
  331. // adjust sticky columns on scroll
  332. $(window).on('scroll', function () {
  333. handleStickyColumns($stick_columns, $table_results);
  334. });
  335. });
  336. });
  337. /**
  338. * Append the "Show/Hide query box" message to the query input form
  339. *
  340. * @memberOf jQuery
  341. * @name appendToggleSpan
  342. */
  343. // do not add this link more than once
  344. if (! $('#sqlqueryform').find('a').is('#togglequerybox')) {
  345. $('<a id="togglequerybox"></a>')
  346. .html(PMA_messages.strHideQueryBox)
  347. .appendTo('#sqlqueryform')
  348. // initially hidden because at this point, nothing else
  349. // appears under the link
  350. .hide();
  351. // Attach the toggling of the query box visibility to a click
  352. $('#togglequerybox').bind('click', function () {
  353. var $link = $(this);
  354. $link.siblings().slideToggle('fast');
  355. if ($link.text() === PMA_messages.strHideQueryBox) {
  356. $link.text(PMA_messages.strShowQueryBox);
  357. // cheap trick to add a spacer between the menu tabs
  358. // and "Show query box"; feel free to improve!
  359. $('#togglequerybox_spacer').remove();
  360. $link.before('<br id="togglequerybox_spacer" />');
  361. } else {
  362. $link.text(PMA_messages.strHideQueryBox);
  363. }
  364. // avoid default click action
  365. return false;
  366. });
  367. }
  368. /**
  369. * Event handler for sqlqueryform.ajax button_submit_query
  370. *
  371. * @memberOf jQuery
  372. */
  373. $(document).on('click', '#button_submit_query', function (event) {
  374. $('.success,.error').hide();
  375. // hide already existing error or success message
  376. var $form = $(this).closest('form');
  377. // the Go button related to query submission was clicked,
  378. // instead of the one related to Bookmarks, so empty the
  379. // id_bookmark selector to avoid misinterpretation in
  380. // import.php about what needs to be done
  381. $form.find('select[name=id_bookmark]').val('');
  382. // let normal event propagation happen
  383. });
  384. /**
  385. * Event handler to show appropiate number of variable boxes
  386. * based on the bookmarked query
  387. */
  388. $(document).on('change', '#id_bookmark', function (event) {
  389. var varCount = $(this).find('option:selected').data('varcount');
  390. if (typeof varCount === 'undefined') {
  391. varCount = 0;
  392. }
  393. var $varDiv = $('#bookmark_variables');
  394. $varDiv.empty();
  395. for (var i = 1; i <= varCount; i++) {
  396. $varDiv.append($('<label for="bookmark_variable_' + i + '">' + PMA_sprintf(PMA_messages.strBookmarkVariable, i) + '</label>'));
  397. $varDiv.append($('<input type="text" size="10" name="bookmark_variable[' + i + ']" id="bookmark_variable_' + i + '"/>'));
  398. }
  399. if (varCount === 0) {
  400. $varDiv.parent('.formelement').hide();
  401. } else {
  402. $varDiv.parent('.formelement').show();
  403. }
  404. });
  405. /**
  406. * Event handler for hitting enter on sqlqueryform bookmark_variable
  407. * (the Variable textfield in Bookmarked SQL query section)
  408. *
  409. * @memberOf jQuery
  410. */
  411. $('input[name=bookmark_variable]').on('keypress', function (event) {
  412. // force the 'Enter Key' to implicitly click the #button_submit_bookmark
  413. var keycode = (event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode));
  414. if (keycode === 13) { // keycode for enter key
  415. // When you press enter in the sqlqueryform, which
  416. // has 2 submit buttons, the default is to run the
  417. // #button_submit_query, because of the tabindex
  418. // attribute.
  419. // This submits #button_submit_bookmark instead,
  420. // because when you are in the Bookmarked SQL query
  421. // section and hit enter, you expect it to do the
  422. // same action as the Go button in that section.
  423. $('#button_submit_bookmark').click();
  424. return false;
  425. } else {
  426. return true;
  427. }
  428. });
  429. /**
  430. * Ajax Event handler for 'SQL Query Submit'
  431. *
  432. * @see PMA_ajaxShowMessage()
  433. * @memberOf jQuery
  434. * @name sqlqueryform_submit
  435. */
  436. $(document).on('submit', '#sqlqueryform.ajax', function (event) {
  437. event.preventDefault();
  438. var $form = $(this);
  439. if (codemirror_editor) {
  440. $form[0].elements.sql_query.value = codemirror_editor.getValue();
  441. }
  442. if (! checkSqlQuery($form[0])) {
  443. return false;
  444. }
  445. // remove any div containing a previous error message
  446. $('div.error').remove();
  447. var $msgbox = PMA_ajaxShowMessage();
  448. var $sqlqueryresultsouter = $('#sqlqueryresultsouter');
  449. PMA_prepareForAjaxRequest($form);
  450. var argsep = PMA_commonParams.get('arg_separator');
  451. $.post($form.attr('action'), $form.serialize() + argsep + 'ajax_page_request=true', function (data) {
  452. if (typeof data !== 'undefined' && data.success === true) {
  453. // success happens if the query returns rows or not
  454. // show a message that stays on screen
  455. if (typeof data.action_bookmark !== 'undefined') {
  456. // view only
  457. if ('1' === data.action_bookmark) {
  458. $('#sqlquery').text(data.sql_query);
  459. // send to codemirror if possible
  460. setQuery(data.sql_query);
  461. }
  462. // delete
  463. if ('2' === data.action_bookmark) {
  464. $('#id_bookmark option[value=\'' + data.id_bookmark + '\']').remove();
  465. // if there are no bookmarked queries now (only the empty option),
  466. // remove the bookmark section
  467. if ($('#id_bookmark option').length === 1) {
  468. $('#fieldsetBookmarkOptions').hide();
  469. $('#fieldsetBookmarkOptionsFooter').hide();
  470. }
  471. }
  472. }
  473. $sqlqueryresultsouter
  474. .show()
  475. .html(data.message);
  476. PMA_highlightSQL($sqlqueryresultsouter);
  477. if (data._menu) {
  478. if (history && history.pushState) {
  479. history.replaceState({
  480. menu : data._menu
  481. },
  482. null
  483. );
  484. AJAX.handleMenu.replace(data._menu);
  485. } else {
  486. PMA_MicroHistory.menus.replace(data._menu);
  487. PMA_MicroHistory.menus.add(data._menuHash, data._menu);
  488. }
  489. } else if (data._menuHash) {
  490. if (! (history && history.pushState)) {
  491. PMA_MicroHistory.menus.replace(PMA_MicroHistory.menus.get(data._menuHash));
  492. }
  493. }
  494. if (data._params) {
  495. PMA_commonParams.setAll(data._params);
  496. }
  497. if (typeof data.ajax_reload !== 'undefined') {
  498. if (data.ajax_reload.reload) {
  499. if (data.ajax_reload.table_name) {
  500. PMA_commonParams.set('table', data.ajax_reload.table_name);
  501. PMA_commonActions.refreshMain();
  502. } else {
  503. PMA_reloadNavigation();
  504. }
  505. }
  506. } else if (typeof data.reload !== 'undefined') {
  507. // this happens if a USE or DROP command was typed
  508. PMA_commonActions.setDb(data.db);
  509. var url;
  510. if (data.db) {
  511. if (data.table) {
  512. url = 'table_sql.php';
  513. } else {
  514. url = 'db_sql.php';
  515. }
  516. } else {
  517. url = 'server_sql.php';
  518. }
  519. PMA_commonActions.refreshMain(url, function () {
  520. $('#sqlqueryresultsouter')
  521. .show()
  522. .html(data.message);
  523. PMA_highlightSQL($('#sqlqueryresultsouter'));
  524. });
  525. }
  526. $('.sqlqueryresults').trigger('makegrid').trigger('stickycolumns');
  527. $('#togglequerybox').show();
  528. PMA_init_slider();
  529. if (typeof data.action_bookmark === 'undefined') {
  530. if ($('#sqlqueryform input[name="retain_query_box"]').is(':checked') !== true) {
  531. if ($('#togglequerybox').siblings(':visible').length > 0) {
  532. $('#togglequerybox').trigger('click');
  533. }
  534. }
  535. }
  536. } else if (typeof data !== 'undefined' && data.success === false) {
  537. // show an error message that stays on screen
  538. $sqlqueryresultsouter
  539. .show()
  540. .html(data.error);
  541. }
  542. PMA_ajaxRemoveMessage($msgbox);
  543. }); // end $.post()
  544. }); // end SQL Query submit
  545. /**
  546. * Ajax Event handler for the display options
  547. * @memberOf jQuery
  548. * @name displayOptionsForm_submit
  549. */
  550. $(document).on('submit', 'form[name=\'displayOptionsForm\'].ajax', function (event) {
  551. event.preventDefault();
  552. $form = $(this);
  553. var $msgbox = PMA_ajaxShowMessage();
  554. var argsep = PMA_commonParams.get('arg_separator');
  555. $.post($form.attr('action'), $form.serialize() + argsep + 'ajax_request=true', function (data) {
  556. PMA_ajaxRemoveMessage($msgbox);
  557. var $sqlqueryresults = $form.parents('.sqlqueryresults');
  558. $sqlqueryresults
  559. .html(data.message)
  560. .trigger('makegrid')
  561. .trigger('stickycolumns');
  562. PMA_init_slider();
  563. PMA_highlightSQL($sqlqueryresults);
  564. }); // end $.post()
  565. }); // end displayOptionsForm handler
  566. // Filter row handling. --STARTS--
  567. $(document).on('keyup', '.filter_rows', function () {
  568. var unique_id = $(this).data('for');
  569. var $target_table = $('.table_results[data-uniqueId=\'' + unique_id + '\']');
  570. var $header_cells = $target_table.find('th[data-column]');
  571. var target_columns = Array();
  572. // To handle colspan=4, in case of edit,copy etc options.
  573. var dummy_th = ($('.edit_row_anchor').length !== 0 ?
  574. '<th class="hide dummy_th"></th><th class="hide dummy_th"></th><th class="hide dummy_th"></th>'
  575. : '');
  576. // Selecting columns that will be considered for filtering and searching.
  577. $header_cells.each(function () {
  578. target_columns.push($.trim($(this).text()));
  579. });
  580. var phrase = $(this).val();
  581. // Set same value to both Filter rows fields.
  582. $('.filter_rows[data-for=\'' + unique_id + '\']').not(this).val(phrase);
  583. // Handle colspan.
  584. $target_table.find('thead > tr').prepend(dummy_th);
  585. $.uiTableFilter($target_table, phrase, target_columns);
  586. $target_table.find('th.dummy_th').remove();
  587. });
  588. // Filter row handling. --ENDS--
  589. // Prompt to confirm on Show All
  590. $('body').on('click', '.navigation .showAllRows', function (e) {
  591. e.preventDefault();
  592. var $form = $(this).parents('form');
  593. if (! $(this).is(':checked')) { // already showing all rows
  594. submitShowAllForm();
  595. } else {
  596. $form.PMA_confirm(PMA_messages.strShowAllRowsWarning, $form.attr('action'), function (url) {
  597. submitShowAllForm();
  598. });
  599. }
  600. function submitShowAllForm () {
  601. var argsep = PMA_commonParams.get('arg_separator');
  602. var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
  603. PMA_ajaxShowMessage();
  604. AJAX.source = $form;
  605. $.post($form.attr('action'), submitData, AJAX.responseHandler);
  606. }
  607. });
  608. $('body').on('keyup', '#sqlqueryform', function () {
  609. PMA_handleSimulateQueryButton();
  610. });
  611. /**
  612. * Ajax event handler for 'Simulate DML'.
  613. */
  614. $('body').on('click', '#simulate_dml', function () {
  615. var $form = $('#sqlqueryform');
  616. var query = '';
  617. var delimiter = $('#id_sql_delimiter').val();
  618. var db_name = $form.find('input[name="db"]').val();
  619. if (codemirror_editor) {
  620. query = codemirror_editor.getValue();
  621. } else {
  622. query = $('#sqlquery').val();
  623. }
  624. if (query.length === 0) {
  625. alert(PMA_messages.strFormEmpty);
  626. $('#sqlquery').focus();
  627. return false;
  628. }
  629. var $msgbox = PMA_ajaxShowMessage();
  630. $.ajax({
  631. type: 'POST',
  632. url: $form.attr('action'),
  633. data: {
  634. server: PMA_commonParams.get('server'),
  635. db: db_name,
  636. ajax_request: '1',
  637. simulate_dml: '1',
  638. sql_query: query,
  639. sql_delimiter: delimiter
  640. },
  641. success: function (response) {
  642. PMA_ajaxRemoveMessage($msgbox);
  643. if (response.success) {
  644. var dialog_content = '<div class="preview_sql">';
  645. if (response.sql_data) {
  646. var len = response.sql_data.length;
  647. for (var i = 0; i < len; i++) {
  648. dialog_content += '<strong>' + PMA_messages.strSQLQuery +
  649. '</strong>' + response.sql_data[i].sql_query +
  650. PMA_messages.strMatchedRows +
  651. ' <a href="' + response.sql_data[i].matched_rows_url +
  652. '">' + response.sql_data[i].matched_rows + '</a><br>';
  653. if (i < len - 1) {
  654. dialog_content += '<hr>';
  655. }
  656. }
  657. } else {
  658. dialog_content += response.message;
  659. }
  660. dialog_content += '</div>';
  661. var $dialog_content = $(dialog_content);
  662. var button_options = {};
  663. button_options[PMA_messages.strClose] = function () {
  664. $(this).dialog('close');
  665. };
  666. var $response_dialog = $('<div />').append($dialog_content).dialog({
  667. minWidth: 540,
  668. maxHeight: 400,
  669. modal: true,
  670. buttons: button_options,
  671. title: PMA_messages.strSimulateDML,
  672. open: function () {
  673. PMA_highlightSQL($(this));
  674. },
  675. close: function () {
  676. $(this).remove();
  677. }
  678. });
  679. } else {
  680. PMA_ajaxShowMessage(response.error);
  681. }
  682. },
  683. error: function (response) {
  684. PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest);
  685. }
  686. });
  687. });
  688. /**
  689. * Handles multi submits of results browsing page such as edit, delete and export
  690. */
  691. $('body').on('click', 'form[name="resultsForm"].ajax button[name="submit_mult"], form[name="resultsForm"].ajax input[name="submit_mult"]', function (e) {
  692. e.preventDefault();
  693. var $button = $(this);
  694. var $form = $button.closest('form');
  695. var argsep = PMA_commonParams.get('arg_separator');
  696. var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val();
  697. PMA_ajaxShowMessage();
  698. AJAX.source = $form;
  699. $.post($form.attr('action'), submitData, AJAX.responseHandler);
  700. });
  701. }); // end $()
  702. /**
  703. * Starting from some th, change the class of all td under it.
  704. * If isAddClass is specified, it will be used to determine whether to add or remove the class.
  705. */
  706. function PMA_changeClassForColumn ($this_th, newclass, isAddClass) {
  707. // index 0 is the th containing the big T
  708. var th_index = $this_th.index();
  709. var has_big_t = $this_th.closest('tr').children(':first').hasClass('column_action');
  710. // .eq() is zero-based
  711. if (has_big_t) {
  712. th_index--;
  713. }
  714. var $table = $this_th.parents('.table_results');
  715. if (! $table.length) {
  716. $table = $this_th.parents('table').siblings('.table_results');
  717. }
  718. var $tds = $table.find('tbody tr').find('td.data:eq(' + th_index + ')');
  719. if (isAddClass === undefined) {
  720. $tds.toggleClass(newclass);
  721. } else {
  722. $tds.toggleClass(newclass, isAddClass);
  723. }
  724. }
  725. /**
  726. * Handles browse foreign values modal dialog
  727. *
  728. * @param object $this_a reference to the browse foreign value link
  729. */
  730. function browseForeignDialog ($this_a) {
  731. var formId = '#browse_foreign_form';
  732. var showAllId = '#foreign_showAll';
  733. var tableId = '#browse_foreign_table';
  734. var filterId = '#input_foreign_filter';
  735. var $dialog = null;
  736. var argSep = PMA_commonParams.get('arg_separator');
  737. var params = $this_a.getPostData();
  738. params += argSep + 'ajax_request=true';
  739. $.post($this_a.attr('href'), params, function (data) {
  740. // Creates browse foreign value dialog
  741. $dialog = $('<div>').append(data.message).dialog({
  742. title: PMA_messages.strBrowseForeignValues,
  743. width: Math.min($(window).width() - 100, 700),
  744. maxHeight: $(window).height() - 100,
  745. dialogClass: 'browse_foreign_modal',
  746. close: function (ev, ui) {
  747. // remove event handlers attached to elements related to dialog
  748. $(tableId).off('click', 'td a.foreign_value');
  749. $(formId).off('click', showAllId);
  750. $(formId).off('submit');
  751. // remove dialog itself
  752. $(this).remove();
  753. },
  754. modal: true
  755. });
  756. }).done(function () {
  757. var showAll = false;
  758. $(tableId).on('click', 'td a.foreign_value', function (e) {
  759. e.preventDefault();
  760. var $input = $this_a.prev('input[type=text]');
  761. // Check if input exists or get CEdit edit_box
  762. if ($input.length === 0) {
  763. $input = $this_a.closest('.edit_area').prev('.edit_box');
  764. }
  765. // Set selected value as input value
  766. $input.val($(this).data('key'));
  767. $dialog.dialog('close');
  768. });
  769. $(formId).on('click', showAllId, function () {
  770. showAll = true;
  771. });
  772. $(formId).on('submit', function (e) {
  773. e.preventDefault();
  774. // if filter value is not equal to old value
  775. // then reset page number to 1
  776. if ($(filterId).val() !== $(filterId).data('old')) {
  777. $(formId).find('select[name=pos]').val('0');
  778. }
  779. var postParams = $(this).serializeArray();
  780. // if showAll button was clicked to submit form then
  781. // add showAll button parameter to form
  782. if (showAll) {
  783. postParams.push({
  784. name: $(showAllId).attr('name'),
  785. value: $(showAllId).val()
  786. });
  787. }
  788. // updates values in dialog
  789. $.post($(this).attr('action') + '?ajax_request=1', postParams, function (data) {
  790. var $obj = $('<div>').html(data.message);
  791. $(formId).html($obj.find(formId).html());
  792. $(tableId).html($obj.find(tableId).html());
  793. });
  794. showAll = false;
  795. });
  796. });
  797. }
  798. AJAX.registerOnload('sql.js', function () {
  799. $('body').on('click', 'a.browse_foreign', function (e) {
  800. e.preventDefault();
  801. browseForeignDialog($(this));
  802. });
  803. /**
  804. * vertical column highlighting in horizontal mode when hovering over the column header
  805. */
  806. $(document).on('mouseenter', 'th.column_heading.pointer', function (e) {
  807. PMA_changeClassForColumn($(this), 'hover', true);
  808. });
  809. $(document).on('mouseleave', 'th.column_heading.pointer', function (e) {
  810. PMA_changeClassForColumn($(this), 'hover', false);
  811. });
  812. /**
  813. * vertical column marking in horizontal mode when clicking the column header
  814. */
  815. $(document).on('click', 'th.column_heading.marker', function () {
  816. PMA_changeClassForColumn($(this), 'marked');
  817. });
  818. /**
  819. * create resizable table
  820. */
  821. $('.sqlqueryresults').trigger('makegrid').trigger('stickycolumns');
  822. });
  823. /*
  824. * Profiling Chart
  825. */
  826. function makeProfilingChart () {
  827. if ($('#profilingchart').length === 0 ||
  828. $('#profilingchart').html().length !== 0 ||
  829. !$.jqplot || !$.jqplot.Highlighter || !$.jqplot.PieRenderer
  830. ) {
  831. return;
  832. }
  833. var data = [];
  834. $.each(JSON.parse($('#profilingChartData').html()), function (key, value) {
  835. data.push([key, parseFloat(value)]);
  836. });
  837. // Remove chart and data divs contents
  838. $('#profilingchart').html('').show();
  839. $('#profilingChartData').html('');
  840. PMA_createProfilingChart('profilingchart', data);
  841. }
  842. /*
  843. * initialize profiling data tables
  844. */
  845. function initProfilingTables () {
  846. if (!$.tablesorter) {
  847. return;
  848. }
  849. $('#profiletable').tablesorter({
  850. widgets: ['zebra'],
  851. sortList: [[0, 0]],
  852. textExtraction: function (node) {
  853. if (node.children.length > 0) {
  854. return node.children[0].innerHTML;
  855. } else {
  856. return node.innerHTML;
  857. }
  858. }
  859. });
  860. $('#profilesummarytable').tablesorter({
  861. widgets: ['zebra'],
  862. sortList: [[1, 1]],
  863. textExtraction: function (node) {
  864. if (node.children.length > 0) {
  865. return node.children[0].innerHTML;
  866. } else {
  867. return node.innerHTML;
  868. }
  869. }
  870. });
  871. }
  872. /*
  873. * Set position, left, top, width of sticky_columns div
  874. */
  875. function setStickyColumnsPosition ($sticky_columns, $table_results, position, top, left, margin_left) {
  876. $sticky_columns
  877. .css('position', position)
  878. .css('top', top)
  879. .css('left', left ? left : 'auto')
  880. .css('margin-left', margin_left ? margin_left : '0px')
  881. .css('width', $table_results.width());
  882. }
  883. /*
  884. * Initialize sticky columns
  885. */
  886. function initStickyColumns ($table_results) {
  887. return $('<table class="sticky_columns"></table>')
  888. .insertBefore($table_results)
  889. .css('position', 'fixed')
  890. .css('z-index', '98')
  891. .css('width', $table_results.width())
  892. .css('margin-left', $('#page_content').css('margin-left'))
  893. .css('top', $('#floating_menubar').height())
  894. .css('display', 'none');
  895. }
  896. /*
  897. * Arrange/Rearrange columns in sticky header
  898. */
  899. function rearrangeStickyColumns ($sticky_columns, $table_results) {
  900. var $originalHeader = $table_results.find('thead');
  901. var $originalColumns = $originalHeader.find('tr:first').children();
  902. var $clonedHeader = $originalHeader.clone();
  903. // clone width per cell
  904. $clonedHeader.find('tr:first').children().width(function (i,val) {
  905. var width = $originalColumns.eq(i).width();
  906. var is_firefox = navigator.userAgent.indexOf('Firefox') > -1;
  907. if (! is_firefox) {
  908. width += 1;
  909. }
  910. return width;
  911. });
  912. $sticky_columns.empty().append($clonedHeader);
  913. }
  914. /*
  915. * Adjust sticky columns on horizontal/vertical scroll for all tables
  916. */
  917. function handleAllStickyColumns () {
  918. $('.sticky_columns').each(function () {
  919. handleStickyColumns($(this), $(this).next('.table_results'));
  920. });
  921. }
  922. /*
  923. * Adjust sticky columns on horizontal/vertical scroll
  924. */
  925. function handleStickyColumns ($sticky_columns, $table_results) {
  926. var currentScrollX = $(window).scrollLeft();
  927. var windowOffset = $(window).scrollTop();
  928. var tableStartOffset = $table_results.offset().top;
  929. var tableEndOffset = tableStartOffset + $table_results.height();
  930. if (windowOffset >= tableStartOffset && windowOffset <= tableEndOffset) {
  931. // for horizontal scrolling
  932. if (prevScrollX !== currentScrollX) {
  933. prevScrollX = currentScrollX;
  934. setStickyColumnsPosition($sticky_columns, $table_results, 'absolute', $('#floating_menubar').height() + windowOffset - tableStartOffset);
  935. // for vertical scrolling
  936. } else {
  937. setStickyColumnsPosition($sticky_columns, $table_results, 'fixed', $('#floating_menubar').height(), $('#pma_navigation').width() - currentScrollX, $('#page_content').css('margin-left'));
  938. }
  939. $sticky_columns.show();
  940. } else {
  941. $sticky_columns.hide();
  942. }
  943. }
  944. AJAX.registerOnload('sql.js', function () {
  945. makeProfilingChart();
  946. initProfilingTables();
  947. });