rte.js 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. /* vim: set expandtab sw=4 ts=4 sts=4: */
  2. /**
  3. * JavaScript functionality for Routines, Triggers and Events.
  4. *
  5. * @package PhpMyadmin
  6. */
  7. /**
  8. * @var RTE Contains all the JavaScript functionality
  9. * for Routines, Triggers and Events
  10. */
  11. var RTE = {
  12. /**
  13. * Construct for the object that provides the
  14. * functionality for Routines, Triggers and Events
  15. */
  16. object: function (type) {
  17. $.extend(this, RTE.COMMON);
  18. this.editorType = type;
  19. switch (type) {
  20. case 'routine':
  21. $.extend(this, RTE.ROUTINE);
  22. break;
  23. case 'trigger':
  24. // nothing extra yet for triggers
  25. break;
  26. case 'event':
  27. $.extend(this, RTE.EVENT);
  28. break;
  29. default:
  30. break;
  31. }
  32. },
  33. /**
  34. * @var string param_template Template for a row in the routine editor
  35. */
  36. param_template: ''
  37. };
  38. /**
  39. * @var RTE.COMMON a JavaScript namespace containing the functionality
  40. * for Routines, Triggers and Events
  41. *
  42. * This namespace is extended by the functionality required
  43. * to handle a specific item (a routine, trigger or event)
  44. * in the relevant javascript files in this folder
  45. */
  46. RTE.COMMON = {
  47. /**
  48. * @var $ajaxDialog Query object containing the reference to the
  49. * dialog that contains the editor
  50. */
  51. $ajaxDialog: null,
  52. /**
  53. * @var syntaxHiglighter Reference to the codemirror editor
  54. */
  55. syntaxHiglighter: null,
  56. /**
  57. * @var buttonOptions Object containing options for
  58. * the jQueryUI dialog buttons
  59. */
  60. buttonOptions: {},
  61. /**
  62. * @var editorType Type of the editor
  63. */
  64. editorType: null,
  65. /**
  66. * Validate editor form fields.
  67. */
  68. validate: function () {
  69. /**
  70. * @var $elm a jQuery object containing the reference
  71. * to an element that is being validated
  72. */
  73. var $elm = null;
  74. // Common validation. At the very least the name
  75. // and the definition must be provided for an item
  76. $elm = $('table.rte_table').last().find('input[name=item_name]');
  77. if ($elm.val() === '') {
  78. $elm.focus();
  79. alert(PMA_messages.strFormEmpty);
  80. return false;
  81. }
  82. $elm = $('table.rte_table').find('textarea[name=item_definition]');
  83. if ($elm.val() === '') {
  84. if (this.syntaxHiglighter !== null) {
  85. this.syntaxHiglighter.focus();
  86. } else {
  87. $('textarea[name=item_definition]').last().focus();
  88. }
  89. alert(PMA_messages.strFormEmpty);
  90. return false;
  91. }
  92. // The validation has so far passed, so now
  93. // we can validate item-specific fields.
  94. return this.validateCustom();
  95. }, // end validate()
  96. /**
  97. * Validate custom editor form fields.
  98. * This function can be overridden by
  99. * other files in this folder
  100. */
  101. validateCustom: function () {
  102. return true;
  103. }, // end validateCustom()
  104. /**
  105. * Execute some code after the ajax
  106. * dialog for the editor is shown.
  107. * This function can be overridden by
  108. * other files in this folder
  109. */
  110. postDialogShow: function () {
  111. // Nothing by default
  112. }, // end postDialogShow()
  113. exportDialog: function ($this) {
  114. var $msg = PMA_ajaxShowMessage();
  115. if ($this.hasClass('mult_submit')) {
  116. var combined = {
  117. success: true,
  118. title: PMA_messages.strExport,
  119. message: '',
  120. error: ''
  121. };
  122. // export anchors of all selected rows
  123. var export_anchors = $('input.checkall:checked').parents('tr').find('.export_anchor');
  124. var count = export_anchors.length;
  125. var returnCount = 0;
  126. // No routine is exportable (due to privilege issues)
  127. if (count === 0) {
  128. PMA_ajaxShowMessage(PMA_messages.NoExportable);
  129. }
  130. export_anchors.each(function () {
  131. $.get($(this).attr('href'), { 'ajax_request': true }, function (data) {
  132. returnCount++;
  133. if (data.success === true) {
  134. combined.message += '\n' + data.message + '\n';
  135. if (returnCount === count) {
  136. showExport(combined);
  137. }
  138. } else {
  139. // complain even if one export is failing
  140. combined.success = false;
  141. combined.error += '\n' + data.error + '\n';
  142. if (returnCount === count) {
  143. showExport(combined);
  144. }
  145. }
  146. });
  147. });
  148. } else {
  149. $.get($this.attr('href'), { 'ajax_request': true }, showExport);
  150. }
  151. PMA_ajaxRemoveMessage($msg);
  152. function showExport (data) {
  153. if (data.success === true) {
  154. PMA_ajaxRemoveMessage($msg);
  155. /**
  156. * @var button_options Object containing options
  157. * for jQueryUI dialog buttons
  158. */
  159. var button_options = {};
  160. button_options[PMA_messages.strClose] = function () {
  161. $(this).dialog('close').remove();
  162. };
  163. /**
  164. * Display the dialog to the user
  165. */
  166. data.message = '<textarea cols="40" rows="15" class="all100">' + data.message + '</textarea>';
  167. var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
  168. width: 500,
  169. buttons: button_options,
  170. title: data.title
  171. });
  172. // Attach syntax highlighted editor to export dialog
  173. /**
  174. * @var $elm jQuery object containing the reference
  175. * to the Export textarea.
  176. */
  177. var $elm = $ajaxDialog.find('textarea');
  178. PMA_getSQLEditor($elm);
  179. } else {
  180. PMA_ajaxShowMessage(data.error, false);
  181. }
  182. } // end showExport()
  183. }, // end exportDialog()
  184. editorDialog: function (is_new, $this) {
  185. var that = this;
  186. /**
  187. * @var $edit_row jQuery object containing the reference to
  188. * the row of the the item being edited
  189. * from the list of items
  190. */
  191. var $edit_row = null;
  192. if ($this.hasClass('edit_anchor')) {
  193. // Remeber the row of the item being edited for later,
  194. // so that if the edit is successful, we can replace the
  195. // row with info about the modified item.
  196. $edit_row = $this.parents('tr');
  197. }
  198. /**
  199. * @var $msg jQuery object containing the reference to
  200. * the AJAX message shown to the user
  201. */
  202. var $msg = PMA_ajaxShowMessage();
  203. $.get($this.attr('href'), { 'ajax_request': true }, function (data) {
  204. if (data.success === true) {
  205. // We have successfully fetched the editor form
  206. PMA_ajaxRemoveMessage($msg);
  207. // Now define the function that is called when
  208. // the user presses the "Go" button
  209. that.buttonOptions[PMA_messages.strGo] = function () {
  210. // Move the data from the codemirror editor back to the
  211. // textarea, where it can be used in the form submission.
  212. if (typeof CodeMirror !== 'undefined') {
  213. that.syntaxHiglighter.save();
  214. }
  215. // Validate editor and submit request, if passed.
  216. if (that.validate()) {
  217. /**
  218. * @var data Form data to be sent in the AJAX request
  219. */
  220. var data = $('form.rte_form').last().serialize();
  221. $msg = PMA_ajaxShowMessage(
  222. PMA_messages.strProcessingRequest
  223. );
  224. var url = $('form.rte_form').last().attr('action');
  225. $.post(url, data, function (data) {
  226. if (data.success === true) {
  227. // Item created successfully
  228. PMA_ajaxRemoveMessage($msg);
  229. PMA_slidingMessage(data.message);
  230. that.$ajaxDialog.dialog('close');
  231. // If we are in 'edit' mode, we must
  232. // remove the reference to the old row.
  233. if (mode === 'edit' && $edit_row !== null) {
  234. $edit_row.remove();
  235. }
  236. // Sometimes, like when moving a trigger from
  237. // a table to another one, the new row should
  238. // not be inserted into the list. In this case
  239. // "data.insert" will be set to false.
  240. if (data.insert) {
  241. // Insert the new row at the correct
  242. // location in the list of items
  243. /**
  244. * @var text Contains the name of an item from
  245. * the list that is used in comparisons
  246. * to find the correct location where
  247. * to insert a new row.
  248. */
  249. var text = '';
  250. /**
  251. * @var inserted Whether a new item has been
  252. * inserted in the list or not
  253. */
  254. var inserted = false;
  255. $('table.data').find('tr').each(function () {
  256. text = $(this)
  257. .children('td')
  258. .eq(0)
  259. .find('strong')
  260. .text()
  261. .toUpperCase();
  262. text = $.trim(text);
  263. if (text !== '' && text > data.name) {
  264. $(this).before(data.new_row);
  265. inserted = true;
  266. return false;
  267. }
  268. });
  269. if (! inserted) {
  270. // If we didn't manage to insert the row yet,
  271. // it must belong at the end of the list,
  272. // so we insert it there.
  273. $('table.data').append(data.new_row);
  274. }
  275. // Fade-in the new row
  276. $('tr.ajaxInsert')
  277. .show('slow')
  278. .removeClass('ajaxInsert');
  279. } else if ($('table.data').find('tr').has('td').length === 0) {
  280. // If we are not supposed to insert the new row,
  281. // we will now check if the table is empty and
  282. // needs to be hidden. This will be the case if
  283. // we were editing the only item in the list,
  284. // which we removed and will not be inserting
  285. // something else in its place.
  286. $('table.data').hide('slow', function () {
  287. $('#nothing2display').show('slow');
  288. });
  289. }
  290. // Now we have inserted the row at the correct
  291. // position, but surely at least some row classes
  292. // are wrong now. So we will itirate throught
  293. // all rows and assign correct classes to them
  294. /**
  295. * @var ct Count of processed rows
  296. */
  297. var ct = 0;
  298. /**
  299. * @var rowclass Class to be attached to the row
  300. * that is being processed
  301. */
  302. var rowclass = '';
  303. $('table.data').find('tr').has('td').each(function () {
  304. rowclass = (ct % 2 === 0) ? 'odd' : 'even';
  305. $(this).removeClass().addClass(rowclass);
  306. ct++;
  307. });
  308. // If this is the first item being added, remove
  309. // the "No items" message and show the list.
  310. if ($('table.data').find('tr').has('td').length > 0 &&
  311. $('#nothing2display').is(':visible')
  312. ) {
  313. $('#nothing2display').hide('slow', function () {
  314. $('table.data').show('slow');
  315. });
  316. }
  317. PMA_reloadNavigation();
  318. } else {
  319. PMA_ajaxShowMessage(data.error, false);
  320. }
  321. }); // end $.post()
  322. } // end "if (that.validate())"
  323. }; // end of function that handles the submission of the Editor
  324. that.buttonOptions[PMA_messages.strClose] = function () {
  325. $(this).dialog('close');
  326. };
  327. /**
  328. * Display the dialog to the user
  329. */
  330. that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
  331. width: 700,
  332. minWidth: 500,
  333. maxHeight: $(window).height(),
  334. buttons: that.buttonOptions,
  335. title: data.title,
  336. modal: true,
  337. open: function () {
  338. if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
  339. $('#rteDialog').dialog('option', 'height', $(window).height());
  340. }
  341. $(this).find('input[name=item_name]').focus();
  342. $(this).find('input.datefield').each(function () {
  343. PMA_addDatepicker($(this).css('width', '95%'), 'date');
  344. });
  345. $(this).find('input.datetimefield').each(function () {
  346. PMA_addDatepicker($(this).css('width', '95%'), 'datetime');
  347. });
  348. $.datepicker.initialized = false;
  349. },
  350. close: function () {
  351. $(this).remove();
  352. }
  353. });
  354. /**
  355. * @var mode Used to remeber whether the editor is in
  356. * "Edit" or "Add" mode
  357. */
  358. var mode = 'add';
  359. if ($('input[name=editor_process_edit]').length > 0) {
  360. mode = 'edit';
  361. }
  362. // Attach syntax highlighted editor to the definition
  363. /**
  364. * @var elm jQuery object containing the reference to
  365. * the Definition textarea.
  366. */
  367. var $elm = $('textarea[name=item_definition]').last();
  368. var linterOptions = {};
  369. linterOptions[that.editorType + '_editor'] = true;
  370. that.syntaxHiglighter = PMA_getSQLEditor($elm, {}, null, linterOptions);
  371. // Execute item-specific code
  372. that.postDialogShow(data);
  373. } else {
  374. PMA_ajaxShowMessage(data.error, false);
  375. }
  376. }); // end $.get()
  377. },
  378. dropDialog: function ($this) {
  379. /**
  380. * @var $curr_row Object containing reference to the current row
  381. */
  382. var $curr_row = $this.parents('tr');
  383. /**
  384. * @var question String containing the question to be asked for confirmation
  385. */
  386. var question = $('<div/>').text(
  387. $curr_row.children('td').children('.drop_sql').html()
  388. );
  389. // We ask for confirmation first here, before submitting the ajax request
  390. $this.PMA_confirm(question, $this.attr('href'), function (url) {
  391. /**
  392. * @var msg jQuery object containing the reference to
  393. * the AJAX message shown to the user
  394. */
  395. var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
  396. var params = getJSConfirmCommonParam(this, $this.getPostData());
  397. $.post(url, params, function (data) {
  398. if (data.success === true) {
  399. /**
  400. * @var $table Object containing reference
  401. * to the main list of elements
  402. */
  403. var $table = $curr_row.parent();
  404. // Check how many rows will be left after we remove
  405. // the one that the user has requested us to remove
  406. if ($table.find('tr').length === 3) {
  407. // If there are two rows left, it means that they are
  408. // the header of the table and the rows that we are
  409. // about to remove, so after the removal there will be
  410. // nothing to show in the table, so we hide it.
  411. $table.hide('slow', function () {
  412. $(this).find('tr.even, tr.odd').remove();
  413. $('.withSelected').remove();
  414. $('#nothing2display').show('slow');
  415. });
  416. } else {
  417. $curr_row.hide('slow', function () {
  418. $(this).remove();
  419. // Now we have removed the row from the list, but maybe
  420. // some row classes are wrong now. So we will itirate
  421. // throught all rows and assign correct classes to them.
  422. /**
  423. * @var ct Count of processed rows
  424. */
  425. var ct = 0;
  426. /**
  427. * @var rowclass Class to be attached to the row
  428. * that is being processed
  429. */
  430. var rowclass = '';
  431. $table.find('tr').has('td').each(function () {
  432. rowclass = (ct % 2 === 1) ? 'odd' : 'even';
  433. $(this).removeClass().addClass(rowclass);
  434. ct++;
  435. });
  436. });
  437. }
  438. // Get rid of the "Loading" message
  439. PMA_ajaxRemoveMessage($msg);
  440. // Show the query that we just executed
  441. PMA_slidingMessage(data.sql_query);
  442. PMA_reloadNavigation();
  443. } else {
  444. PMA_ajaxShowMessage(data.error, false);
  445. }
  446. }); // end $.post()
  447. }); // end $.PMA_confirm()
  448. },
  449. dropMultipleDialog: function ($this) {
  450. // We ask for confirmation here
  451. $this.PMA_confirm(PMA_messages.strDropRTEitems, '', function (url) {
  452. /**
  453. * @var msg jQuery object containing the reference to
  454. * the AJAX message shown to the user
  455. */
  456. var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
  457. // drop anchors of all selected rows
  458. var drop_anchors = $('input.checkall:checked').parents('tr').find('.drop_anchor');
  459. var success = true;
  460. var count = drop_anchors.length;
  461. var returnCount = 0;
  462. drop_anchors.each(function () {
  463. var $anchor = $(this);
  464. /**
  465. * @var $curr_row Object containing reference to the current row
  466. */
  467. var $curr_row = $anchor.parents('tr');
  468. var params = getJSConfirmCommonParam(this, $anchor.getPostData());
  469. $.post($anchor.attr('href'), params, function (data) {
  470. returnCount++;
  471. if (data.success === true) {
  472. /**
  473. * @var $table Object containing reference
  474. * to the main list of elements
  475. */
  476. var $table = $curr_row.parent();
  477. // Check how many rows will be left after we remove
  478. // the one that the user has requested us to remove
  479. if ($table.find('tr').length === 3) {
  480. // If there are two rows left, it means that they are
  481. // the header of the table and the rows that we are
  482. // about to remove, so after the removal there will be
  483. // nothing to show in the table, so we hide it.
  484. $table.hide('slow', function () {
  485. $(this).find('tr.even, tr.odd').remove();
  486. $('.withSelected').remove();
  487. $('#nothing2display').show('slow');
  488. });
  489. } else {
  490. $curr_row.hide('fast', function () {
  491. $(this).remove();
  492. // Now we have removed the row from the list, but maybe
  493. // some row classes are wrong now. So we will itirate
  494. // throught all rows and assign correct classes to them.
  495. /**
  496. * @var ct Count of processed rows
  497. */
  498. var ct = 0;
  499. /**
  500. * @var rowclass Class to be attached to the row
  501. * that is being processed
  502. */
  503. var rowclass = '';
  504. $table.find('tr').has('td').each(function () {
  505. rowclass = (ct % 2 === 1) ? 'odd' : 'even';
  506. $(this).removeClass().addClass(rowclass);
  507. ct++;
  508. });
  509. });
  510. }
  511. if (returnCount === count) {
  512. if (success) {
  513. // Get rid of the "Loading" message
  514. PMA_ajaxRemoveMessage($msg);
  515. $('#rteListForm_checkall').prop({ checked: false, indeterminate: false });
  516. }
  517. PMA_reloadNavigation();
  518. }
  519. } else {
  520. PMA_ajaxShowMessage(data.error, false);
  521. success = false;
  522. if (returnCount === count) {
  523. PMA_reloadNavigation();
  524. }
  525. }
  526. }); // end $.post()
  527. }); // end drop_anchors.each()
  528. }); // end $.PMA_confirm()
  529. }
  530. }; // end RTE namespace
  531. /**
  532. * @var RTE.EVENT JavaScript functionality for events
  533. */
  534. RTE.EVENT = {
  535. validateCustom: function () {
  536. /**
  537. * @var elm a jQuery object containing the reference
  538. * to an element that is being validated
  539. */
  540. var $elm = null;
  541. if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'RECURRING') {
  542. // The interval field must not be empty for recurring events
  543. $elm = this.$ajaxDialog.find('input[name=item_interval_value]');
  544. if ($elm.val() === '') {
  545. $elm.focus();
  546. alert(PMA_messages.strFormEmpty);
  547. return false;
  548. }
  549. } else {
  550. // The execute_at field must not be empty for "once off" events
  551. $elm = this.$ajaxDialog.find('input[name=item_execute_at]');
  552. if ($elm.val() === '') {
  553. $elm.focus();
  554. alert(PMA_messages.strFormEmpty);
  555. return false;
  556. }
  557. }
  558. return true;
  559. }
  560. };
  561. /**
  562. * @var RTE.ROUTINE JavaScript functionality for routines
  563. */
  564. RTE.ROUTINE = {
  565. /**
  566. * Overriding the postDialogShow() function defined in common.js
  567. *
  568. * @param data JSON-encoded data from the ajax request
  569. */
  570. postDialogShow: function (data) {
  571. // Cache the template for a parameter table row
  572. RTE.param_template = data.param_template;
  573. var that = this;
  574. // Make adjustments in the dialog to make it AJAX compatible
  575. $('td.routine_param_remove').show();
  576. $('input[name=routine_removeparameter]').remove();
  577. $('input[name=routine_addparameter]').css('width', '100%');
  578. // Enable/disable the 'options' dropdowns for parameters as necessary
  579. $('table.routine_params_table').last().find('th[colspan=2]').attr('colspan', '1');
  580. $('table.routine_params_table').last().find('tr').has('td').each(function () {
  581. that.setOptionsForParameter(
  582. $(this).find('select[name^=item_param_type]'),
  583. $(this).find('input[name^=item_param_length]'),
  584. $(this).find('select[name^=item_param_opts_text]'),
  585. $(this).find('select[name^=item_param_opts_num]')
  586. );
  587. });
  588. // Enable/disable the 'options' dropdowns for
  589. // function return value as necessary
  590. this.setOptionsForParameter(
  591. $('table.rte_table').last().find('select[name=item_returntype]'),
  592. $('table.rte_table').last().find('input[name=item_returnlength]'),
  593. $('table.rte_table').last().find('select[name=item_returnopts_text]'),
  594. $('table.rte_table').last().find('select[name=item_returnopts_num]')
  595. );
  596. // Allow changing parameter order
  597. $('.routine_params_table tbody').sortable({
  598. containment: '.routine_params_table tbody',
  599. handle: '.dragHandle',
  600. stop: function (event, ui) {
  601. that.reindexParameters();
  602. },
  603. });
  604. },
  605. /**
  606. * Reindexes the parameters after dropping a parameter or reordering parameters
  607. */
  608. reindexParameters: function () {
  609. /**
  610. * @var index Counter used for reindexing the input
  611. * fields in the routine parameters table
  612. */
  613. var index = 0;
  614. $('table.routine_params_table tbody').find('tr').each(function () {
  615. $(this).find(':input').each(function () {
  616. /**
  617. * @var inputname The value of the name attribute of
  618. * the input field being reindexed
  619. */
  620. var inputname = $(this).attr('name');
  621. if (inputname.substr(0, 14) === 'item_param_dir') {
  622. $(this).attr('name', inputname.substr(0, 14) + '[' + index + ']');
  623. } else if (inputname.substr(0, 15) === 'item_param_name') {
  624. $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']');
  625. } else if (inputname.substr(0, 15) === 'item_param_type') {
  626. $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']');
  627. } else if (inputname.substr(0, 17) === 'item_param_length') {
  628. $(this).attr('name', inputname.substr(0, 17) + '[' + index + ']');
  629. $(this).attr('id', 'item_param_length_' + index);
  630. } else if (inputname.substr(0, 20) === 'item_param_opts_text') {
  631. $(this).attr('name', inputname.substr(0, 20) + '[' + index + ']');
  632. } else if (inputname.substr(0, 19) === 'item_param_opts_num') {
  633. $(this).attr('name', inputname.substr(0, 19) + '[' + index + ']');
  634. }
  635. });
  636. index++;
  637. });
  638. },
  639. /**
  640. * Overriding the validateCustom() function defined in common.js
  641. */
  642. validateCustom: function () {
  643. /**
  644. * @var isSuccess Stores the outcome of the validation
  645. */
  646. var isSuccess = true;
  647. /**
  648. * @var inputname The value of the "name" attribute for
  649. * the field that is being processed
  650. */
  651. var inputname = '';
  652. this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () {
  653. // Every parameter of a routine must have
  654. // a non-empty direction, name and type
  655. if (isSuccess) {
  656. $(this).find(':input').each(function () {
  657. inputname = $(this).attr('name');
  658. if (inputname.substr(0, 14) === 'item_param_dir' ||
  659. inputname.substr(0, 15) === 'item_param_name' ||
  660. inputname.substr(0, 15) === 'item_param_type') {
  661. if ($(this).val() === '') {
  662. $(this).focus();
  663. isSuccess = false;
  664. return false;
  665. }
  666. }
  667. });
  668. } else {
  669. return false;
  670. }
  671. });
  672. if (! isSuccess) {
  673. alert(PMA_messages.strFormEmpty);
  674. return false;
  675. }
  676. this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () {
  677. // SET, ENUM, VARCHAR and VARBINARY fields must have length/values
  678. var $inputtyp = $(this).find('select[name^=item_param_type]');
  679. var $inputlen = $(this).find('input[name^=item_param_length]');
  680. if ($inputtyp.length && $inputlen.length) {
  681. if (($inputtyp.val() === 'ENUM' || $inputtyp.val() === 'SET' || $inputtyp.val().substr(0, 3) === 'VAR') &&
  682. $inputlen.val() === ''
  683. ) {
  684. $inputlen.focus();
  685. isSuccess = false;
  686. return false;
  687. }
  688. }
  689. });
  690. if (! isSuccess) {
  691. alert(PMA_messages.strFormEmpty);
  692. return false;
  693. }
  694. if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'FUNCTION') {
  695. // The length/values of return variable for functions must
  696. // be set, if the type is SET, ENUM, VARCHAR or VARBINARY.
  697. var $returntyp = this.$ajaxDialog.find('select[name=item_returntype]');
  698. var $returnlen = this.$ajaxDialog.find('input[name=item_returnlength]');
  699. if (($returntyp.val() === 'ENUM' || $returntyp.val() === 'SET' || $returntyp.val().substr(0, 3) === 'VAR') &&
  700. $returnlen.val() === ''
  701. ) {
  702. $returnlen.focus();
  703. alert(PMA_messages.strFormEmpty);
  704. return false;
  705. }
  706. }
  707. if ($('select[name=item_type]').find(':selected').val() === 'FUNCTION') {
  708. // A function must contain a RETURN statement in its definition
  709. if (this.$ajaxDialog.find('table.rte_table').find('textarea[name=item_definition]').val().toUpperCase().indexOf('RETURN') < 0) {
  710. this.syntaxHiglighter.focus();
  711. alert(PMA_messages.MissingReturn);
  712. return false;
  713. }
  714. }
  715. return true;
  716. },
  717. /**
  718. * Enable/disable the "options" dropdown and "length" input for
  719. * parameters and the return variable in the routine editor
  720. * as necessary.
  721. *
  722. * @param type a jQuery object containing the reference
  723. * to the "Type" dropdown box
  724. * @param len a jQuery object containing the reference
  725. * to the "Length" input box
  726. * @param text a jQuery object containing the reference
  727. * to the dropdown box with options for
  728. * parameters of text type
  729. * @param num a jQuery object containing the reference
  730. * to the dropdown box with options for
  731. * parameters of numeric type
  732. */
  733. setOptionsForParameter: function ($type, $len, $text, $num) {
  734. /**
  735. * @var no_opts a jQuery object containing the reference
  736. * to an element to be displayed when no
  737. * options are available
  738. */
  739. var $no_opts = $text.parent().parent().find('.no_opts');
  740. /**
  741. * @var no_len a jQuery object containing the reference
  742. * to an element to be displayed when no
  743. * "length/values" field is available
  744. */
  745. var $no_len = $len.parent().parent().find('.no_len');
  746. // Process for parameter options
  747. switch ($type.val()) {
  748. case 'TINYINT':
  749. case 'SMALLINT':
  750. case 'MEDIUMINT':
  751. case 'INT':
  752. case 'BIGINT':
  753. case 'DECIMAL':
  754. case 'FLOAT':
  755. case 'DOUBLE':
  756. case 'REAL':
  757. $text.parent().hide();
  758. $num.parent().show();
  759. $no_opts.hide();
  760. break;
  761. case 'TINYTEXT':
  762. case 'TEXT':
  763. case 'MEDIUMTEXT':
  764. case 'LONGTEXT':
  765. case 'CHAR':
  766. case 'VARCHAR':
  767. case 'SET':
  768. case 'ENUM':
  769. $text.parent().show();
  770. $num.parent().hide();
  771. $no_opts.hide();
  772. break;
  773. default:
  774. $text.parent().hide();
  775. $num.parent().hide();
  776. $no_opts.show();
  777. break;
  778. }
  779. // Process for parameter length
  780. switch ($type.val()) {
  781. case 'DATE':
  782. case 'TINYBLOB':
  783. case 'TINYTEXT':
  784. case 'BLOB':
  785. case 'TEXT':
  786. case 'MEDIUMBLOB':
  787. case 'MEDIUMTEXT':
  788. case 'LONGBLOB':
  789. case 'LONGTEXT':
  790. $text.closest('tr').find('a:first').hide();
  791. $len.parent().hide();
  792. $no_len.show();
  793. break;
  794. default:
  795. if ($type.val() === 'ENUM' || $type.val() === 'SET') {
  796. $text.closest('tr').find('a:first').show();
  797. } else {
  798. $text.closest('tr').find('a:first').hide();
  799. }
  800. $len.parent().show();
  801. $no_len.hide();
  802. break;
  803. }
  804. },
  805. executeDialog: function ($this) {
  806. var that = this;
  807. /**
  808. * @var msg jQuery object containing the reference to
  809. * the AJAX message shown to the user
  810. */
  811. var $msg = PMA_ajaxShowMessage();
  812. var params = getJSConfirmCommonParam($this[0], $this.getPostData());
  813. $.post($this.attr('href'), params, function (data) {
  814. if (data.success === true) {
  815. PMA_ajaxRemoveMessage($msg);
  816. // If 'data.dialog' is true we show a dialog with a form
  817. // to get the input parameters for routine, otherwise
  818. // we just show the results of the query
  819. if (data.dialog) {
  820. // Define the function that is called when
  821. // the user presses the "Go" button
  822. that.buttonOptions[PMA_messages.strGo] = function () {
  823. /**
  824. * @var data Form data to be sent in the AJAX request
  825. */
  826. var data = $('form.rte_form').last().serialize();
  827. $msg = PMA_ajaxShowMessage(
  828. PMA_messages.strProcessingRequest
  829. );
  830. $.post('db_routines.php', data, function (data) {
  831. if (data.success === true) {
  832. // Routine executed successfully
  833. PMA_ajaxRemoveMessage($msg);
  834. PMA_slidingMessage(data.message);
  835. $ajaxDialog.dialog('close');
  836. } else {
  837. PMA_ajaxShowMessage(data.error, false);
  838. }
  839. });
  840. };
  841. that.buttonOptions[PMA_messages.strClose] = function () {
  842. $(this).dialog('close');
  843. };
  844. /**
  845. * Display the dialog to the user
  846. */
  847. var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
  848. width: 650,
  849. buttons: that.buttonOptions,
  850. title: data.title,
  851. modal: true,
  852. close: function () {
  853. $(this).remove();
  854. }
  855. });
  856. $ajaxDialog.find('input[name^=params]').first().focus();
  857. /**
  858. * Attach the datepickers to the relevant form fields
  859. */
  860. $ajaxDialog.find('input.datefield, input.datetimefield').each(function () {
  861. PMA_addDatepicker($(this).css('width', '95%'));
  862. });
  863. /*
  864. * Define the function if the user presses enter
  865. */
  866. $('form.rte_form').on('keyup', function (event) {
  867. event.preventDefault();
  868. if (event.keyCode === 13) {
  869. /**
  870. * @var data Form data to be sent in the AJAX request
  871. */
  872. var data = $(this).serialize();
  873. $msg = PMA_ajaxShowMessage(
  874. PMA_messages.strProcessingRequest
  875. );
  876. var url = $(this).attr('action');
  877. $.post(url, data, function (data) {
  878. if (data.success === true) {
  879. // Routine executed successfully
  880. PMA_ajaxRemoveMessage($msg);
  881. PMA_slidingMessage(data.message);
  882. $('form.rte_form').off('keyup');
  883. $ajaxDialog.remove();
  884. } else {
  885. PMA_ajaxShowMessage(data.error, false);
  886. }
  887. });
  888. }
  889. });
  890. } else {
  891. // Routine executed successfully
  892. PMA_slidingMessage(data.message);
  893. }
  894. } else {
  895. PMA_ajaxShowMessage(data.error, false);
  896. }
  897. }); // end $.post()
  898. }
  899. };
  900. /**
  901. * Attach Ajax event handlers for the Routines, Triggers and Events editor
  902. */
  903. $(function () {
  904. /**
  905. * Attach Ajax event handlers for the Add/Edit functionality.
  906. */
  907. $(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) {
  908. event.preventDefault();
  909. var type = $(this).attr('href').substr(0, $(this).attr('href').indexOf('?'));
  910. if (type.indexOf('routine') !== -1) {
  911. type = 'routine';
  912. } else if (type.indexOf('trigger') !== -1) {
  913. type = 'trigger';
  914. } else if (type.indexOf('event') !== -1) {
  915. type = 'event';
  916. } else {
  917. type = '';
  918. }
  919. var dialog = new RTE.object(type);
  920. dialog.editorDialog($(this).hasClass('add_anchor'), $(this));
  921. }); // end $(document).on()
  922. /**
  923. * Attach Ajax event handlers for the Execute routine functionality
  924. */
  925. $(document).on('click', 'a.ajax.exec_anchor', function (event) {
  926. event.preventDefault();
  927. var dialog = new RTE.object('routine');
  928. dialog.executeDialog($(this));
  929. }); // end $(document).on()
  930. /**
  931. * Attach Ajax event handlers for Export of Routines, Triggers and Events
  932. */
  933. $(document).on('click', 'a.ajax.export_anchor', function (event) {
  934. event.preventDefault();
  935. var dialog = new RTE.object();
  936. dialog.exportDialog($(this));
  937. }); // end $(document).on()
  938. $(document).on('click', '#rteListForm.ajax .mult_submit[value="export"]', function (event) {
  939. event.preventDefault();
  940. var dialog = new RTE.object();
  941. dialog.exportDialog($(this));
  942. }); // end $(document).on()
  943. /**
  944. * Attach Ajax event handlers for Drop functionality
  945. * of Routines, Triggers and Events.
  946. */
  947. $(document).on('click', 'a.ajax.drop_anchor', function (event) {
  948. event.preventDefault();
  949. var dialog = new RTE.object();
  950. dialog.dropDialog($(this));
  951. }); // end $(document).on()
  952. $(document).on('click', '#rteListForm.ajax .mult_submit[value="drop"]', function (event) {
  953. event.preventDefault();
  954. var dialog = new RTE.object();
  955. dialog.dropMultipleDialog($(this));
  956. }); // end $(document).on()
  957. /**
  958. * Attach Ajax event handlers for the "Change event/routine type"
  959. * functionality in the events editor, so that the correct
  960. * rows are shown in the editor when changing the event type
  961. */
  962. $(document).on('change', 'select[name=item_type]', function () {
  963. $(this)
  964. .closest('table')
  965. .find('tr.recurring_event_row, tr.onetime_event_row, tr.routine_return_row, .routine_direction_cell')
  966. .toggle();
  967. }); // end $(document).on()
  968. /**
  969. * Attach Ajax event handlers for the "Change parameter type"
  970. * functionality in the routines editor, so that the correct
  971. * option/length fields, if any, are shown when changing
  972. * a parameter type
  973. */
  974. $(document).on('change', 'select[name^=item_param_type]', function () {
  975. /**
  976. * @var row jQuery object containing the reference to
  977. * a row in the routine parameters table
  978. */
  979. var $row = $(this).parents('tr').first();
  980. var rte = new RTE.object('routine');
  981. rte.setOptionsForParameter(
  982. $row.find('select[name^=item_param_type]'),
  983. $row.find('input[name^=item_param_length]'),
  984. $row.find('select[name^=item_param_opts_text]'),
  985. $row.find('select[name^=item_param_opts_num]')
  986. );
  987. }); // end $(document).on()
  988. /**
  989. * Attach Ajax event handlers for the "Change the type of return
  990. * variable of function" functionality, so that the correct fields,
  991. * if any, are shown when changing the function return type type
  992. */
  993. $(document).on('change', 'select[name=item_returntype]', function () {
  994. var rte = new RTE.object('routine');
  995. var $table = $(this).closest('table.rte_table');
  996. rte.setOptionsForParameter(
  997. $table.find('select[name=item_returntype]'),
  998. $table.find('input[name=item_returnlength]'),
  999. $table.find('select[name=item_returnopts_text]'),
  1000. $table.find('select[name=item_returnopts_num]')
  1001. );
  1002. }); // end $(document).on()
  1003. /**
  1004. * Attach Ajax event handlers for the "Add parameter to routine" functionality
  1005. */
  1006. $(document).on('click', 'input[name=routine_addparameter]', function (event) {
  1007. event.preventDefault();
  1008. /**
  1009. * @var routine_params_table jQuery object containing the reference
  1010. * to the routine parameters table
  1011. */
  1012. var $routine_params_table = $(this).closest('div.ui-dialog').find('.routine_params_table');
  1013. /**
  1014. * @var new_param_row A string containing the HTML code for the
  1015. * new row for the routine parameters table
  1016. */
  1017. var new_param_row = RTE.param_template.replace(/%s/g, $routine_params_table.find('tr').length - 1);
  1018. // Append the new row to the parameters table
  1019. $routine_params_table.append(new_param_row);
  1020. // Make sure that the row is correctly shown according to the type of routine
  1021. if ($(this).closest('div.ui-dialog').find('table.rte_table select[name=item_type]').val() === 'FUNCTION') {
  1022. $('tr.routine_return_row').show();
  1023. $('td.routine_direction_cell').hide();
  1024. }
  1025. /**
  1026. * @var newrow jQuery object containing the reference to the newly
  1027. * inserted row in the routine parameters table
  1028. */
  1029. var $newrow = $(this).closest('div.ui-dialog').find('table.routine_params_table').find('tr').has('td').last();
  1030. // Enable/disable the 'options' dropdowns for parameters as necessary
  1031. var rte = new RTE.object('routine');
  1032. rte.setOptionsForParameter(
  1033. $newrow.find('select[name^=item_param_type]'),
  1034. $newrow.find('input[name^=item_param_length]'),
  1035. $newrow.find('select[name^=item_param_opts_text]'),
  1036. $newrow.find('select[name^=item_param_opts_num]')
  1037. );
  1038. }); // end $(document).on()
  1039. /**
  1040. * Attach Ajax event handlers for the
  1041. * "Remove parameter from routine" functionality
  1042. */
  1043. $(document).on('click', 'a.routine_param_remove_anchor', function (event) {
  1044. event.preventDefault();
  1045. $(this).parent().parent().remove();
  1046. // After removing a parameter, the indices of the name attributes in
  1047. // the input fields lose the correct order and need to be reordered.
  1048. RTE.ROUTINE.reindexParameters();
  1049. }); // end $(document).on()
  1050. }); // end of $()