tbl_change.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /* vim: set expandtab sw=4 ts=4 sts=4: */
  2. /**
  3. * @fileoverview function used in table data manipulation pages
  4. *
  5. * @requires jQuery
  6. * @requires jQueryUI
  7. * @requires js/functions.js
  8. *
  9. */
  10. /**
  11. * Modify form controls when the "NULL" checkbox is checked
  12. *
  13. * @param theType string the MySQL field type
  14. * @param urlField string the urlencoded field name - OBSOLETE
  15. * @param md5Field string the md5 hashed field name
  16. * @param multi_edit string the multi_edit row sequence number
  17. *
  18. * @return boolean always true
  19. */
  20. function nullify (theType, urlField, md5Field, multi_edit) {
  21. var rowForm = document.forms.insertForm;
  22. if (typeof(rowForm.elements['funcs' + multi_edit + '[' + md5Field + ']']) !== 'undefined') {
  23. rowForm.elements['funcs' + multi_edit + '[' + md5Field + ']'].selectedIndex = -1;
  24. }
  25. // "ENUM" field with more than 20 characters
  26. if (Number(theType) === 1) {
  27. rowForm.elements['fields' + multi_edit + '[' + md5Field + ']'][1].selectedIndex = -1;
  28. // Other "ENUM" field
  29. } else if (Number(theType) === 2) {
  30. var elts = rowForm.elements['fields' + multi_edit + '[' + md5Field + ']'];
  31. // when there is just one option in ENUM:
  32. if (elts.checked) {
  33. elts.checked = false;
  34. } else {
  35. var elts_cnt = elts.length;
  36. for (var i = 0; i < elts_cnt; i++) {
  37. elts[i].checked = false;
  38. } // end for
  39. } // end if
  40. // "SET" field
  41. } else if (Number(theType) === 3) {
  42. rowForm.elements['fields' + multi_edit + '[' + md5Field + '][]'].selectedIndex = -1;
  43. // Foreign key field (drop-down)
  44. } else if (Number(theType) === 4) {
  45. rowForm.elements['fields' + multi_edit + '[' + md5Field + ']'].selectedIndex = -1;
  46. // foreign key field (with browsing icon for foreign values)
  47. } else if (Number(theType) === 6) {
  48. rowForm.elements['fields' + multi_edit + '[' + md5Field + ']'].value = '';
  49. // Other field types
  50. } else /* if (theType === 5)*/ {
  51. rowForm.elements['fields' + multi_edit + '[' + md5Field + ']'].value = '';
  52. } // end if... else if... else
  53. return true;
  54. } // end of the 'nullify()' function
  55. /**
  56. * javascript DateTime format validation.
  57. * its used to prevent adding default (0000-00-00 00:00:00) to database when user enter wrong values
  58. * Start of validation part
  59. */
  60. // function checks the number of days in febuary
  61. function daysInFebruary (year) {
  62. return (((year % 4 === 0) && (((year % 100 !== 0)) || (year % 400 === 0))) ? 29 : 28);
  63. }
  64. // function to convert single digit to double digit
  65. function fractionReplace (num) {
  66. num = parseInt(num, 10);
  67. return num >= 1 && num <= 9 ? '0' + num : '00';
  68. }
  69. /* function to check the validity of date
  70. * The following patterns are accepted in this validation (accepted in mysql as well)
  71. * 1) 2001-12-23
  72. * 2) 2001-1-2
  73. * 3) 02-12-23
  74. * 4) And instead of using '-' the following punctuations can be used (+,.,*,^,@,/) All these are accepted by mysql as well. Therefore no issues
  75. */
  76. function isDate (val, tmstmp) {
  77. val = val.replace(/[.|*|^|+|//|@]/g, '-');
  78. var arrayVal = val.split('-');
  79. for (var a = 0; a < arrayVal.length; a++) {
  80. if (arrayVal[a].length === 1) {
  81. arrayVal[a] = fractionReplace(arrayVal[a]);
  82. }
  83. }
  84. val = arrayVal.join('-');
  85. var pos = 2;
  86. var dtexp = new RegExp(/^([0-9]{4})-(((01|03|05|07|08|10|12)-((0[0-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)-((0[0-9])|([1-2][0-9])|30))|((00)-(00)))$/);
  87. if (val.length === 8) {
  88. pos = 0;
  89. }
  90. if (dtexp.test(val)) {
  91. var month = parseInt(val.substring(pos + 3, pos + 5), 10);
  92. var day = parseInt(val.substring(pos + 6, pos + 8), 10);
  93. var year = parseInt(val.substring(0, pos + 2), 10);
  94. if (month === 2 && day > daysInFebruary(year)) {
  95. return false;
  96. }
  97. if (val.substring(0, pos + 2).length === 2) {
  98. year = parseInt('20' + val.substring(0, pos + 2), 10);
  99. }
  100. if (tmstmp === true) {
  101. if (year < 1978) {
  102. return false;
  103. }
  104. if (year > 2038 || (year > 2037 && day > 19 && month >= 1) || (year > 2037 && month > 1)) {
  105. return false;
  106. }
  107. }
  108. } else {
  109. return false;
  110. }
  111. return true;
  112. }
  113. /* function to check the validity of time
  114. * The following patterns are accepted in this validation (accepted in mysql as well)
  115. * 1) 2:3:4
  116. * 2) 2:23:43
  117. * 3) 2:23:43.123456
  118. */
  119. function isTime (val) {
  120. var arrayVal = val.split(':');
  121. for (var a = 0, l = arrayVal.length; a < l; a++) {
  122. if (arrayVal[a].length === 1) {
  123. arrayVal[a] = fractionReplace(arrayVal[a]);
  124. }
  125. }
  126. val = arrayVal.join(':');
  127. var tmexp = new RegExp(/^(-)?(([0-7]?[0-9][0-9])|(8[0-2][0-9])|(83[0-8])):((0[0-9])|([1-5][0-9])):((0[0-9])|([1-5][0-9]))(\.[0-9]{1,6}){0,1}$/);
  128. return tmexp.test(val);
  129. }
  130. /**
  131. * To check whether insert section is ignored or not
  132. */
  133. function checkForCheckbox (multi_edit) {
  134. if ($('#insert_ignore_' + multi_edit).length) {
  135. return $('#insert_ignore_' + multi_edit).is(':unchecked');
  136. }
  137. return true;
  138. }
  139. function verificationsAfterFieldChange (urlField, multi_edit, theType) {
  140. var evt = window.event || arguments.callee.caller.arguments[0];
  141. var target = evt.target || evt.srcElement;
  142. var $this_input = $(':input[name^=\'fields[multi_edit][' + multi_edit + '][' +
  143. urlField + ']\']');
  144. // the function drop-down that corresponds to this input field
  145. var $this_function = $('select[name=\'funcs[multi_edit][' + multi_edit + '][' +
  146. urlField + ']\']');
  147. var function_selected = false;
  148. if (typeof $this_function.val() !== 'undefined' &&
  149. $this_function.val() !== null &&
  150. $this_function.val().length > 0
  151. ) {
  152. function_selected = true;
  153. }
  154. // To generate the textbox that can take the salt
  155. var new_salt_box = '<br><input type=text name=salt[multi_edit][' + multi_edit + '][' + urlField + ']' +
  156. ' id=salt_' + target.id + ' placeholder=\'' + PMA_messages.strEncryptionKey + '\'>';
  157. // If encrypting or decrypting functions that take salt as input is selected append the new textbox for salt
  158. if (target.value === 'AES_ENCRYPT' ||
  159. target.value === 'AES_DECRYPT' ||
  160. target.value === 'DES_ENCRYPT' ||
  161. target.value === 'DES_DECRYPT' ||
  162. target.value === 'ENCRYPT') {
  163. if (!($('#salt_' + target.id).length)) {
  164. $this_input.after(new_salt_box);
  165. }
  166. } else {
  167. // Remove the textbox for salt
  168. $('#salt_' + target.id).prev('br').remove();
  169. $('#salt_' + target.id).remove();
  170. }
  171. if (target.value === 'AES_DECRYPT'
  172. || target.value === 'AES_ENCRYPT'
  173. || target.value === 'MD5') {
  174. $('#' + target.id).rules('add', {
  175. validationFunctionForFuns: {
  176. param: $this_input,
  177. depends: function () {
  178. return checkForCheckbox(multi_edit);
  179. }
  180. }
  181. });
  182. }
  183. // Unchecks the corresponding "NULL" control
  184. $('input[name=\'fields_null[multi_edit][' + multi_edit + '][' + urlField + ']\']').prop('checked', false);
  185. // Unchecks the Ignore checkbox for the current row
  186. $('input[name=\'insert_ignore_' + multi_edit + '\']').prop('checked', false);
  187. var charExceptionHandling;
  188. if (theType.substring(0,4) === 'char') {
  189. charExceptionHandling = theType.substring(5,6);
  190. } else if (theType.substring(0,7) === 'varchar') {
  191. charExceptionHandling = theType.substring(8,9);
  192. }
  193. if (function_selected) {
  194. $this_input.removeAttr('min');
  195. $this_input.removeAttr('max');
  196. // @todo: put back attributes if corresponding function is deselected
  197. }
  198. if ($this_input.data('rulesadded') === null && ! function_selected) {
  199. // call validate before adding rules
  200. $($this_input[0].form).validate();
  201. // validate for date time
  202. if (theType === 'datetime' || theType === 'time' || theType === 'date' || theType === 'timestamp') {
  203. $this_input.rules('add', {
  204. validationFunctionForDateTime: {
  205. param: theType,
  206. depends: function () {
  207. return checkForCheckbox(multi_edit);
  208. }
  209. }
  210. });
  211. }
  212. // validation for integer type
  213. if ($this_input.data('type') === 'INT') {
  214. var mini = parseInt($this_input.attr('min'));
  215. var maxi = parseInt($this_input.attr('max'));
  216. $this_input.rules('add', {
  217. number: {
  218. param : true,
  219. depends: function () {
  220. return checkForCheckbox(multi_edit);
  221. }
  222. },
  223. min: {
  224. param: mini,
  225. depends: function () {
  226. if (isNaN($this_input.val())) {
  227. return false;
  228. } else {
  229. return checkForCheckbox(multi_edit);
  230. }
  231. }
  232. },
  233. max: {
  234. param: maxi,
  235. depends: function () {
  236. if (isNaN($this_input.val())) {
  237. return false;
  238. } else {
  239. return checkForCheckbox(multi_edit);
  240. }
  241. }
  242. }
  243. });
  244. // validation for CHAR types
  245. } else if ($this_input.data('type') === 'CHAR') {
  246. var maxlen = $this_input.data('maxlength');
  247. if (typeof maxlen !== 'undefined') {
  248. if (maxlen <= 4) {
  249. maxlen = charExceptionHandling;
  250. }
  251. $this_input.rules('add', {
  252. maxlength: {
  253. param: maxlen,
  254. depends: function () {
  255. return checkForCheckbox(multi_edit);
  256. }
  257. }
  258. });
  259. }
  260. // validate binary & blob types
  261. } else if ($this_input.data('type') === 'HEX') {
  262. $this_input.rules('add', {
  263. validationFunctionForHex: {
  264. param: true,
  265. depends: function () {
  266. return checkForCheckbox(multi_edit);
  267. }
  268. }
  269. });
  270. }
  271. $this_input.data('rulesadded', true);
  272. } else if ($this_input.data('rulesadded') === true && function_selected) {
  273. // remove any rules added
  274. $this_input.rules('remove');
  275. // remove any error messages
  276. $this_input
  277. .removeClass('error')
  278. .removeAttr('aria-invalid')
  279. .siblings('.error')
  280. .remove();
  281. $this_input.data('rulesadded', null);
  282. }
  283. }
  284. /* End of fields validation*/
  285. /**
  286. * Unbind all event handlers before tearing down a page
  287. */
  288. AJAX.registerTeardown('tbl_change.js', function () {
  289. $(document).off('click', 'span.open_gis_editor');
  290. $(document).off('click', 'input[name^=\'insert_ignore_\']');
  291. $(document).off('click', 'input[name=\'gis_data[save]\']');
  292. $(document).off('click', 'input.checkbox_null');
  293. $('select[name="submit_type"]').off('change');
  294. $(document).off('change', '#insert_rows');
  295. });
  296. /**
  297. * Ajax handlers for Change Table page
  298. *
  299. * Actions Ajaxified here:
  300. * Submit Data to be inserted into the table.
  301. * Restart insertion with 'N' rows.
  302. */
  303. AJAX.registerOnload('tbl_change.js', function () {
  304. if ($('#insertForm').length) {
  305. // validate the comment form when it is submitted
  306. $('#insertForm').validate();
  307. jQuery.validator.addMethod('validationFunctionForHex', function (value, element) {
  308. return value.match(/^[a-f0-9]*$/i) !== null;
  309. });
  310. jQuery.validator.addMethod('validationFunctionForFuns', function (value, element, options) {
  311. if (value.substring(0, 3) === 'AES' && options.data('type') !== 'HEX') {
  312. return false;
  313. }
  314. return !(value.substring(0, 3) === 'MD5' &&
  315. typeof options.data('maxlength') !== 'undefined' &&
  316. options.data('maxlength') < 32);
  317. });
  318. jQuery.validator.addMethod('validationFunctionForDateTime', function (value, element, options) {
  319. var dt_value = value;
  320. var theType = options;
  321. if (theType === 'date') {
  322. return isDate(dt_value);
  323. } else if (theType === 'time') {
  324. return isTime(dt_value);
  325. } else if (theType === 'datetime' || theType === 'timestamp') {
  326. var tmstmp = false;
  327. dt_value = dt_value.trim();
  328. if (dt_value === 'CURRENT_TIMESTAMP' || dt_value === 'current_timestamp()') {
  329. return true;
  330. }
  331. if (theType === 'timestamp') {
  332. tmstmp = true;
  333. }
  334. if (dt_value === '0000-00-00 00:00:00') {
  335. return true;
  336. }
  337. var dv = dt_value.indexOf(' ');
  338. if (dv === -1) { // Only the date component, which is valid
  339. return isDate(dt_value, tmstmp);
  340. }
  341. return isDate(dt_value.substring(0, dv), tmstmp) &&
  342. isTime(dt_value.substring(dv + 1));
  343. }
  344. });
  345. /*
  346. * message extending script must be run
  347. * after initiation of functions
  348. */
  349. extendingValidatorMessages();
  350. }
  351. $.datepicker.initialized = false;
  352. $(document).on('click', 'span.open_gis_editor', function (event) {
  353. event.preventDefault();
  354. var $span = $(this);
  355. // Current value
  356. var value = $span.parent('td').children('input[type=\'text\']').val();
  357. // Field name
  358. var field = $span.parents('tr').children('td:first').find('input[type=\'hidden\']').val();
  359. // Column type
  360. var type = $span.parents('tr').find('span.column_type').text();
  361. // Names of input field and null checkbox
  362. var input_name = $span.parent('td').children('input[type=\'text\']').attr('name');
  363. openGISEditor();
  364. if (!gisEditorLoaded) {
  365. loadJSAndGISEditor(value, field, type, input_name);
  366. } else {
  367. loadGISEditor(value, field, type, input_name);
  368. }
  369. });
  370. /**
  371. * Forced validation check of fields
  372. */
  373. $(document).on('click','input[name^=\'insert_ignore_\']', function (event) {
  374. $('#insertForm').valid();
  375. });
  376. /**
  377. * Uncheck the null checkbox as geometry data is placed on the input field
  378. */
  379. $(document).on('click', 'input[name=\'gis_data[save]\']', function (event) {
  380. var input_name = $('form#gis_data_editor_form').find('input[name=\'input_name\']').val();
  381. var $null_checkbox = $('input[name=\'' + input_name + '\']').parents('tr').find('.checkbox_null');
  382. $null_checkbox.prop('checked', false);
  383. });
  384. /**
  385. * Handles all current checkboxes for Null; this only takes care of the
  386. * checkboxes on currently displayed rows as the rows generated by
  387. * "Continue insertion" are handled in the "Continue insertion" code
  388. *
  389. */
  390. $(document).on('click', 'input.checkbox_null', function () {
  391. nullify(
  392. // use hidden fields populated by tbl_change.php
  393. $(this).siblings('.nullify_code').val(),
  394. $(this).closest('tr').find('input:hidden').first().val(),
  395. $(this).siblings('.hashed_field').val(),
  396. $(this).siblings('.multi_edit').val()
  397. );
  398. });
  399. /**
  400. * Reset the auto_increment column to 0 when selecting any of the
  401. * insert options in submit_type-dropdown. Only perform the reset
  402. * when we are in edit-mode, and not in insert-mode(no previous value
  403. * available).
  404. */
  405. $('select[name="submit_type"]').on('change', function () {
  406. var thisElemSubmitTypeVal = $(this).val();
  407. var $table = $('table.insertRowTable');
  408. var auto_increment_column = $table.find('input[name^="auto_increment"]');
  409. auto_increment_column.each(function () {
  410. var $thisElemAIField = $(this);
  411. var thisElemName = $thisElemAIField.attr('name');
  412. var prev_value_field = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields_prev') + '"]');
  413. var value_field = $table.find('input[name="' + thisElemName.replace('auto_increment', 'fields') + '"]');
  414. var previous_value = $(prev_value_field).val();
  415. if (previous_value !== undefined) {
  416. if (thisElemSubmitTypeVal === 'insert'
  417. || thisElemSubmitTypeVal === 'insertignore'
  418. || thisElemSubmitTypeVal === 'showinsert'
  419. ) {
  420. $(value_field).val(0);
  421. } else {
  422. $(value_field).val(previous_value);
  423. }
  424. }
  425. });
  426. });
  427. /**
  428. * Handle ENTER key when press on Continue insert with field
  429. */
  430. $('#insert_rows').keypress(function (e) {
  431. var key = e.which;
  432. if (key === 13) {
  433. addNewContinueInsertionFiels(e);
  434. }
  435. });
  436. /**
  437. * Continue Insertion form
  438. */
  439. $(document).on('change', '#insert_rows', addNewContinueInsertionFiels);
  440. });
  441. function addNewContinueInsertionFiels (event) {
  442. event.preventDefault();
  443. /**
  444. * @var columnCount Number of number of columns table has.
  445. */
  446. var columnCount = $('table.insertRowTable:first').find('tr').has('input[name*=\'fields_name\']').length;
  447. /**
  448. * @var curr_rows Number of current insert rows already on page
  449. */
  450. var curr_rows = $('table.insertRowTable').length;
  451. /**
  452. * @var target_rows Number of rows the user wants
  453. */
  454. var target_rows = $('#insert_rows').val();
  455. // remove all datepickers
  456. $('input.datefield, input.datetimefield').each(function () {
  457. $(this).datepicker('destroy');
  458. });
  459. if (curr_rows < target_rows) {
  460. var tempIncrementIndex = function () {
  461. var $this_element = $(this);
  462. /**
  463. * Extract the index from the name attribute for all input/select fields and increment it
  464. * name is of format funcs[multi_edit][10][<long random string of alphanum chars>]
  465. */
  466. /**
  467. * @var this_name String containing name of the input/select elements
  468. */
  469. var this_name = $this_element.attr('name');
  470. /** split {@link this_name} at [10], so we have the parts that can be concatenated later */
  471. var name_parts = this_name.split(/\[\d+\]/);
  472. /** extract the [10] from {@link name_parts} */
  473. var old_row_index_string = this_name.match(/\[\d+\]/)[0];
  474. /** extract 10 - had to split into two steps to accomodate double digits */
  475. var old_row_index = parseInt(old_row_index_string.match(/\d+/)[0], 10);
  476. /** calculate next index i.e. 11 */
  477. new_row_index = old_row_index + 1;
  478. /** generate the new name i.e. funcs[multi_edit][11][foobarbaz] */
  479. var new_name = name_parts[0] + '[' + new_row_index + ']' + name_parts[1];
  480. var hashed_field = name_parts[1].match(/\[(.+)\]/)[1];
  481. $this_element.attr('name', new_name);
  482. /** If element is select[name*='funcs'], update id */
  483. if ($this_element.is('select[name*=\'funcs\']')) {
  484. var this_id = $this_element.attr('id');
  485. var id_parts = this_id.split(/\_/);
  486. var old_id_index = id_parts[1];
  487. var prevSelectedValue = $('#field_' + old_id_index + '_1').val();
  488. var new_id_index = parseInt(old_id_index) + columnCount;
  489. var new_id = 'field_' + new_id_index + '_1';
  490. $this_element.attr('id', new_id);
  491. $this_element.find('option').filter(function () {
  492. return $(this).text() === prevSelectedValue;
  493. }).attr('selected','selected');
  494. // If salt field is there then update its id.
  495. var nextSaltInput = $this_element.parent().next('td').next('td').find('input[name*=\'salt\']');
  496. if (nextSaltInput.length !== 0) {
  497. nextSaltInput.attr('id', 'salt_' + new_id);
  498. }
  499. }
  500. // handle input text fields and textareas
  501. if ($this_element.is('.textfield') || $this_element.is('.char') || $this_element.is('textarea')) {
  502. // do not remove the 'value' attribute for ENUM columns
  503. // special handling for radio fields after updating ids to unique - see below
  504. if ($this_element.closest('tr').find('span.column_type').html() !== 'enum') {
  505. $this_element.val($this_element.closest('tr').find('span.default_value').html());
  506. }
  507. $this_element
  508. .off('change')
  509. // Remove onchange attribute that was placed
  510. // by tbl_change.php; it refers to the wrong row index
  511. .attr('onchange', null)
  512. // Keep these values to be used when the element
  513. // will change
  514. .data('hashed_field', hashed_field)
  515. .data('new_row_index', new_row_index)
  516. .on('change', function () {
  517. var $changed_element = $(this);
  518. verificationsAfterFieldChange(
  519. $changed_element.data('hashed_field'),
  520. $changed_element.data('new_row_index'),
  521. $changed_element.closest('tr').find('span.column_type').html()
  522. );
  523. });
  524. }
  525. if ($this_element.is('.checkbox_null')) {
  526. $this_element
  527. // this event was bound earlier by jQuery but
  528. // to the original row, not the cloned one, so unbind()
  529. .off('click')
  530. // Keep these values to be used when the element
  531. // will be clicked
  532. .data('hashed_field', hashed_field)
  533. .data('new_row_index', new_row_index)
  534. .on('click', function () {
  535. var $changed_element = $(this);
  536. nullify(
  537. $changed_element.siblings('.nullify_code').val(),
  538. $this_element.closest('tr').find('input:hidden').first().val(),
  539. $changed_element.data('hashed_field'),
  540. '[multi_edit][' + $changed_element.data('new_row_index') + ']'
  541. );
  542. });
  543. }
  544. };
  545. var tempReplaceAnchor = function () {
  546. var $anchor = $(this);
  547. var new_value = 'rownumber=' + new_row_index;
  548. // needs improvement in case something else inside
  549. // the href contains this pattern
  550. var new_href = $anchor.attr('href').replace(/rownumber=\d+/, new_value);
  551. $anchor.attr('href', new_href);
  552. };
  553. while (curr_rows < target_rows) {
  554. /**
  555. * @var $last_row Object referring to the last row
  556. */
  557. var $last_row = $('#insertForm').find('.insertRowTable:last');
  558. // need to access this at more than one level
  559. // (also needs improvement because it should be calculated
  560. // just once per cloned row, not once per column)
  561. var new_row_index = 0;
  562. // Clone the insert tables
  563. $last_row
  564. .clone(true, true)
  565. .insertBefore('#actions_panel')
  566. .find('input[name*=multi_edit],select[name*=multi_edit],textarea[name*=multi_edit]')
  567. .each(tempIncrementIndex)
  568. .end()
  569. .find('.foreign_values_anchor')
  570. .each(tempReplaceAnchor);
  571. // Insert/Clone the ignore checkboxes
  572. if (curr_rows === 1) {
  573. $('<input id="insert_ignore_1" type="checkbox" name="insert_ignore_1" checked="checked" />')
  574. .insertBefore('table.insertRowTable:last')
  575. .after('<label for="insert_ignore_1">' + PMA_messages.strIgnore + '</label>');
  576. } else {
  577. /**
  578. * @var $last_checkbox Object reference to the last checkbox in #insertForm
  579. */
  580. var $last_checkbox = $('#insertForm').children('input:checkbox:last');
  581. /** name of {@link $last_checkbox} */
  582. var last_checkbox_name = $last_checkbox.attr('name');
  583. /** index of {@link $last_checkbox} */
  584. var last_checkbox_index = parseInt(last_checkbox_name.match(/\d+/), 10);
  585. /** name of new {@link $last_checkbox} */
  586. var new_name = last_checkbox_name.replace(/\d+/, last_checkbox_index + 1);
  587. $('<br/><div class="clearfloat"></div>')
  588. .insertBefore('table.insertRowTable:last');
  589. $last_checkbox
  590. .clone()
  591. .attr({ 'id': new_name, 'name': new_name })
  592. .prop('checked', true)
  593. .insertBefore('table.insertRowTable:last');
  594. $('label[for^=insert_ignore]:last')
  595. .clone()
  596. .attr('for', new_name)
  597. .insertBefore('table.insertRowTable:last');
  598. $('<br/>')
  599. .insertBefore('table.insertRowTable:last');
  600. }
  601. curr_rows++;
  602. }
  603. // recompute tabindex for text fields and other controls at footer;
  604. // IMO it's not really important to handle the tabindex for
  605. // function and Null
  606. var tabindex = 0;
  607. $('.textfield, .char, textarea')
  608. .each(function () {
  609. tabindex++;
  610. $(this).attr('tabindex', tabindex);
  611. // update the IDs of textfields to ensure that they are unique
  612. $(this).attr('id', 'field_' + tabindex + '_3');
  613. // special handling for radio fields after updating ids to unique
  614. if ($(this).closest('tr').find('span.column_type').html() === 'enum') {
  615. if ($(this).val() === $(this).closest('tr').find('span.default_value').html()) {
  616. $(this).prop('checked', true);
  617. } else {
  618. $(this).prop('checked', false);
  619. }
  620. }
  621. });
  622. $('.control_at_footer')
  623. .each(function () {
  624. tabindex++;
  625. $(this).attr('tabindex', tabindex);
  626. });
  627. } else if (curr_rows > target_rows) {
  628. /**
  629. * Displays alert if data loss possible on decrease
  630. * of rows.
  631. */
  632. var checkLock = jQuery.isEmptyObject(AJAX.lockedTargets);
  633. if (checkLock || confirm(PMA_messages.strConfirmRowChange) === true) {
  634. while (curr_rows > target_rows) {
  635. $('input[id^=insert_ignore]:last')
  636. .nextUntil('fieldset')
  637. .addBack()
  638. .remove();
  639. curr_rows--;
  640. }
  641. } else {
  642. document.getElementById('insert_rows').value = curr_rows;
  643. }
  644. }
  645. // Add all the required datepickers back
  646. addDateTimePicker();
  647. }
  648. function changeValueFieldType (elem, searchIndex) {
  649. var fieldsValue = $('select#fieldID_' + searchIndex);
  650. if (0 === fieldsValue.size()) {
  651. return;
  652. }
  653. var type = $(elem).val();
  654. if ('IN (...)' === type ||
  655. 'NOT IN (...)' === type ||
  656. 'BETWEEN' === type ||
  657. 'NOT BETWEEN' === type
  658. ) {
  659. $('#fieldID_' + searchIndex).attr('multiple', '');
  660. } else {
  661. $('#fieldID_' + searchIndex).removeAttr('multiple');
  662. }
  663. }