sql.js 38 KB

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