rte.js 39 KB

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