jquery.tabledit.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /*!
  2. * Tabledit v1.2.3 (https://github.com/markcell/jQuery-Tabledit)
  3. * Copyright (c) 2015 Celso Marques
  4. * Licensed under MIT (https://github.com/markcell/jQuery-Tabledit/blob/master/LICENSE)
  5. */
  6. /**
  7. * @description Inline editor for HTML tables compatible with Bootstrap
  8. * @version 1.2.3
  9. * @author Celso Marques
  10. */
  11. if (typeof jQuery === 'undefined') {
  12. throw new Error('Tabledit requires jQuery library.');
  13. }
  14. (function($) {
  15. 'use strict';
  16. $.fn.Tabledit = function(options) {
  17. if (!this.is('table')) {
  18. throw new Error('Tabledit only works when applied to a table.');
  19. }
  20. var $table = this;
  21. var defaults = {
  22. url: window.location.href,
  23. inputClass: 'form-control input-sm',
  24. toolbarClass: 'btn-toolbar',
  25. groupClass: 'btn-group btn-group-sm',
  26. dangerClass: 'danger',
  27. warningClass: 'warning',
  28. mutedClass: 'text-muted',
  29. eventType: 'click',
  30. rowIdentifier: 'id',
  31. hideIdentifier: false,
  32. autoFocus: true,
  33. editButton: true,
  34. deleteButton: true,
  35. saveButton: true,
  36. restoreButton: true,
  37. buttons: {
  38. edit: {
  39. class: 'btn btn-sm btn-default',
  40. html: '<span class="glyphicon glyphicon-pencil"></span>',
  41. action: 'edit'
  42. },
  43. delete: {
  44. class: 'btn btn-sm btn-default',
  45. html: '<span class="glyphicon glyphicon-trash"></span>',
  46. action: 'delete'
  47. },
  48. save: {
  49. class: 'btn btn-sm btn-success',
  50. html: 'Save'
  51. },
  52. restore: {
  53. class: 'btn btn-sm btn-warning',
  54. html: 'Restore',
  55. action: 'restore'
  56. },
  57. confirm: {
  58. class: 'btn btn-sm btn-danger',
  59. html: 'Confirm'
  60. }
  61. },
  62. onDraw: function() { return; },
  63. onSuccess: function() { return; },
  64. onFail: function() { return; },
  65. onAlways: function() { return; },
  66. onAjax: function() { return; }
  67. };
  68. var settings = $.extend(true, defaults, options);
  69. var $lastEditedRow = 'undefined';
  70. var $lastDeletedRow = 'undefined';
  71. var $lastRestoredRow = 'undefined';
  72. /**
  73. * Draw Tabledit structure (identifier column, editable columns, toolbar column).
  74. *
  75. * @type {object}
  76. */
  77. var Draw = {
  78. columns: {
  79. identifier: function() {
  80. // Hide identifier column.
  81. if (settings.hideIdentifier) {
  82. $table.find('th:nth-child(' + parseInt(settings.columns.identifier[0]) + 1 + '), tbody td:nth-child(' + parseInt(settings.columns.identifier[0]) + 1 + ')').hide();
  83. }
  84. var $td = $table.find('tbody td:nth-child(' + (parseInt(settings.columns.identifier[0]) + 1) + ')');
  85. $td.each(function() {
  86. // Create hidden input with row identifier.
  87. var span = '<span class="tabledit-span tabledit-identifier">' + $(this).text() + '</span>';
  88. var input = '<input class="tabledit-input tabledit-identifier" type="hidden" name="' + settings.columns.identifier[1] + '" value="' + $(this).text() + '" disabled>';
  89. // Add elements to table cell.
  90. $(this).html(span + input);
  91. // Add attribute "id" to table row.
  92. $(this).parent('tr').attr(settings.rowIdentifier, $(this).text());
  93. });
  94. },
  95. editable: function() {
  96. for (var i = 0; i < settings.columns.editable.length; i++) {
  97. var $td = $table.find('tbody td:nth-child(' + (parseInt(settings.columns.editable[i][0]) + 1) + ')');
  98. $td.each(function() {
  99. // Get text of this cell.
  100. var text = $(this).text();
  101. // Add pointer as cursor.
  102. if (!settings.editButton) {
  103. $(this).css('cursor', 'pointer');
  104. }
  105. // Create span element.
  106. var span = '<span class="tabledit-span">' + text + '</span>';
  107. // Check if exists the third parameter of editable array.
  108. if (typeof settings.columns.editable[i][2] !== 'undefined') {
  109. // Create select element.
  110. var input = '<select class="tabledit-input ' + settings.inputClass + '" name="' + settings.columns.editable[i][1] + '" style="display: none;" disabled>';
  111. // Create options for select element.
  112. $.each(jQuery.parseJSON(settings.columns.editable[i][2]), function(index, value) {
  113. if (text === value) {
  114. input += '<option value="' + index + '" selected>' + value + '</option>';
  115. } else {
  116. input += '<option value="' + index + '">' + value + '</option>';
  117. }
  118. });
  119. // Create last piece of select element.
  120. input += '</select>';
  121. } else {
  122. // Create text input element.
  123. var input = '<input class="tabledit-input ' + settings.inputClass + '" type="text" name="' + settings.columns.editable[i][1] + '" value="' + $(this).text() + '" style="display: none;" disabled>';
  124. }
  125. // Add elements and class "view" to table cell.
  126. $(this).html(span + input);
  127. $(this).addClass('tabledit-view-mode');
  128. });
  129. }
  130. },
  131. toolbar: function() {
  132. if (settings.editButton || settings.deleteButton) {
  133. var editButton = '';
  134. var deleteButton = '';
  135. var saveButton = '';
  136. var restoreButton = '';
  137. var confirmButton = '';
  138. // Add toolbar column header if not exists.
  139. if ($table.find('th.tabledit-toolbar-column').length === 0) {
  140. $table.find('tr:first').append('<th class="tabledit-toolbar-column"></th>');
  141. }
  142. // Create edit button.
  143. if (settings.editButton) {
  144. editButton = '<button type="button" class="tabledit-edit-button ' + settings.buttons.edit.class + '" style="float: none;">' + settings.buttons.edit.html + '</button>';
  145. }
  146. // Create delete button.
  147. if (settings.deleteButton) {
  148. deleteButton = '<button type="button" class="tabledit-delete-button ' + settings.buttons.delete.class + '" style="float: none;">' + settings.buttons.delete.html + '</button>';
  149. confirmButton = '<button type="button" class="tabledit-confirm-button ' + settings.buttons.confirm.class + '" style="display: none; float: none;">' + settings.buttons.confirm.html + '</button>';
  150. }
  151. // Create save button.
  152. if (settings.editButton && settings.saveButton) {
  153. saveButton = '<button type="button" class="tabledit-save-button ' + settings.buttons.save.class + '" style="display: none; float: none;">' + settings.buttons.save.html + '</button>';
  154. }
  155. // Create restore button.
  156. if (settings.deleteButton && settings.restoreButton) {
  157. restoreButton = '<button type="button" class="tabledit-restore-button ' + settings.buttons.restore.class + '" style="display: none; float: none;">' + settings.buttons.restore.html + '</button>';
  158. }
  159. var toolbar = '<div class="tabledit-toolbar ' + settings.toolbarClass + '" style="text-align: left;">\n\
  160. <div class="' + settings.groupClass + '" style="float: none;">' + editButton + deleteButton + '</div>\n\
  161. ' + saveButton + '\n\
  162. ' + confirmButton + '\n\
  163. ' + restoreButton + '\n\
  164. </div></div>';
  165. // Add toolbar column cells.
  166. $table.find('tbody>tr').append('<td style="white-space: nowrap; width: 1%;">' + toolbar + '</td>');
  167. }
  168. }
  169. }
  170. };
  171. /**
  172. * Change to view mode or edit mode with table td element as parameter.
  173. *
  174. * @type object
  175. */
  176. var Mode = {
  177. view: function(td) {
  178. // Get table row.
  179. var $tr = $(td).parent('tr');
  180. // Disable identifier.
  181. $(td).parent('tr').find('.tabledit-input.tabledit-identifier').prop('disabled', true);
  182. // Hide and disable input element.
  183. $(td).find('.tabledit-input').blur().hide().prop('disabled', true);
  184. // Show span element.
  185. $(td).find('.tabledit-span').show();
  186. // Add "view" class and remove "edit" class in td element.
  187. $(td).addClass('tabledit-view-mode').removeClass('tabledit-edit-mode');
  188. // Update toolbar buttons.
  189. if (settings.editButton) {
  190. $tr.find('button.tabledit-save-button').hide();
  191. $tr.find('button.tabledit-edit-button').removeClass('active').blur();
  192. }
  193. },
  194. edit: function(td) {
  195. Delete.reset(td);
  196. // Get table row.
  197. var $tr = $(td).parent('tr');
  198. // Enable identifier.
  199. $tr.find('.tabledit-input.tabledit-identifier').prop('disabled', false);
  200. // Hide span element.
  201. $(td).find('.tabledit-span').hide();
  202. // Get input element.
  203. var $input = $(td).find('.tabledit-input');
  204. // Enable and show input element.
  205. $input.prop('disabled', false).show();
  206. // Focus on input element.
  207. if (settings.autoFocus) {
  208. $input.focus();
  209. }
  210. // Add "edit" class and remove "view" class in td element.
  211. $(td).addClass('tabledit-edit-mode').removeClass('tabledit-view-mode');
  212. // Update toolbar buttons.
  213. if (settings.editButton) {
  214. $tr.find('button.tabledit-edit-button').addClass('active');
  215. $tr.find('button.tabledit-save-button').show();
  216. }
  217. }
  218. };
  219. /**
  220. * Available actions for edit function, with table td element as parameter or set of td elements.
  221. *
  222. * @type object
  223. */
  224. var Edit = {
  225. reset: function(td) {
  226. $(td).each(function() {
  227. // Get input element.
  228. var $input = $(this).find('.tabledit-input');
  229. // Get span text.
  230. var text = $(this).find('.tabledit-span').text();
  231. // Set input/select value with span text.
  232. if ($input.is('select')) {
  233. $input.find('option').filter(function() {
  234. return $.trim($(this).text()) === text;
  235. }).attr('selected', true);
  236. } else {
  237. $input.val(text);
  238. }
  239. // Change to view mode.
  240. Mode.view(this);
  241. });
  242. },
  243. submit: function(td) {
  244. // Send AJAX request to server.
  245. var ajaxResult = ajax(settings.buttons.edit.action);
  246. if (ajaxResult === false) {
  247. return;
  248. }
  249. $(td).each(function() {
  250. // Get input element.
  251. var $input = $(this).find('.tabledit-input');
  252. // Set span text with input/select new value.
  253. if ($input.is('select')) {
  254. $(this).find('.tabledit-span').text($input.find('option:selected').text());
  255. } else {
  256. $(this).find('.tabledit-span').text($input.val());
  257. }
  258. // Change to view mode.
  259. Mode.view(this);
  260. });
  261. // Set last edited column and row.
  262. $lastEditedRow = $(td).parent('tr');
  263. }
  264. };
  265. /**
  266. * Available actions for delete function, with button as parameter.
  267. *
  268. * @type object
  269. */
  270. var Delete = {
  271. reset: function(td) {
  272. // Reset delete button to initial status.
  273. $table.find('.tabledit-confirm-button').hide();
  274. // Remove "active" class in delete button.
  275. $table.find('.tabledit-delete-button').removeClass('active').blur();
  276. },
  277. submit: function(td) {
  278. Delete.reset(td);
  279. // Enable identifier hidden input.
  280. $(td).parent('tr').find('input.tabledit-identifier').attr('disabled', false);
  281. // Send AJAX request to server.
  282. var ajaxResult = ajax(settings.buttons.delete.action);
  283. // Disable identifier hidden input.
  284. $(td).parents('tr').find('input.tabledit-identifier').attr('disabled', true);
  285. if (ajaxResult === false) {
  286. return;
  287. }
  288. // Add class "deleted" to row.
  289. $(td).parent('tr').addClass('tabledit-deleted-row');
  290. // Hide table row.
  291. $(td).parent('tr').addClass(settings.mutedClass).find('.tabledit-toolbar button:not(.tabledit-restore-button)').attr('disabled', true);
  292. // Show restore button.
  293. $(td).find('.tabledit-restore-button').show();
  294. // Set last deleted row.
  295. $lastDeletedRow = $(td).parent('tr');
  296. },
  297. confirm: function(td) {
  298. // Reset all cells in edit mode.
  299. $table.find('td.tabledit-edit-mode').each(function() {
  300. Edit.reset(this);
  301. });
  302. // Add "active" class in delete button.
  303. $(td).find('.tabledit-delete-button').addClass('active');
  304. // Show confirm button.
  305. $(td).find('.tabledit-confirm-button').show();
  306. },
  307. restore: function(td) {
  308. // Enable identifier hidden input.
  309. $(td).parent('tr').find('input.tabledit-identifier').attr('disabled', false);
  310. // Send AJAX request to server.
  311. var ajaxResult = ajax(settings.buttons.restore.action);
  312. // Disable identifier hidden input.
  313. $(td).parents('tr').find('input.tabledit-identifier').attr('disabled', true);
  314. if (ajaxResult === false) {
  315. return;
  316. }
  317. // Remove class "deleted" to row.
  318. $(td).parent('tr').removeClass('tabledit-deleted-row');
  319. // Hide table row.
  320. $(td).parent('tr').removeClass(settings.mutedClass).find('.tabledit-toolbar button').attr('disabled', false);
  321. // Hide restore button.
  322. $(td).find('.tabledit-restore-button').hide();
  323. // Set last restored row.
  324. $lastRestoredRow = $(td).parent('tr');
  325. }
  326. };
  327. /**
  328. * Send AJAX request to server.
  329. *
  330. * @param {string} action
  331. */
  332. function ajax(action)
  333. {
  334. var serialize = $table.find('.tabledit-input').serialize() + '&action=' + action;
  335. var result = settings.onAjax(action, serialize);
  336. if (result === false) {
  337. return false;
  338. }
  339. var jqXHR = $.post(settings.url, serialize, function(data, textStatus, jqXHR) {
  340. if (action === settings.buttons.edit.action) {
  341. $lastEditedRow.removeClass(settings.dangerClass).addClass(settings.warningClass);
  342. setTimeout(function() {
  343. //$lastEditedRow.removeClass(settings.warningClass);
  344. $table.find('tr.' + settings.warningClass).removeClass(settings.warningClass);
  345. }, 1400);
  346. }
  347. settings.onSuccess(data, textStatus, jqXHR);
  348. }, 'json');
  349. jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
  350. if (action === settings.buttons.delete.action) {
  351. $lastDeletedRow.removeClass(settings.mutedClass).addClass(settings.dangerClass);
  352. $lastDeletedRow.find('.tabledit-toolbar button').attr('disabled', false);
  353. $lastDeletedRow.find('.tabledit-toolbar .tabledit-restore-button').hide();
  354. } else if (action === settings.buttons.edit.action) {
  355. $lastEditedRow.addClass(settings.dangerClass);
  356. }
  357. settings.onFail(jqXHR, textStatus, errorThrown);
  358. });
  359. jqXHR.always(function() {
  360. settings.onAlways();
  361. });
  362. return jqXHR;
  363. }
  364. Draw.columns.identifier();
  365. Draw.columns.editable();
  366. Draw.columns.toolbar();
  367. settings.onDraw();
  368. if (settings.deleteButton) {
  369. /**
  370. * Delete one row.
  371. *
  372. * @param {object} event
  373. */
  374. $table.on('click', 'button.tabledit-delete-button', function(event) {
  375. if (event.handled !== true) {
  376. event.preventDefault();
  377. // Get current state before reset to view mode.
  378. var activated = $(this).hasClass('active');
  379. var $td = $(this).parents('td');
  380. Delete.reset($td);
  381. if (!activated) {
  382. Delete.confirm($td);
  383. }
  384. event.handled = true;
  385. }
  386. });
  387. /**
  388. * Delete one row (confirm).
  389. *
  390. * @param {object} event
  391. */
  392. $table.on('click', 'button.tabledit-confirm-button', function(event) {
  393. if (event.handled !== true) {
  394. event.preventDefault();
  395. var $td = $(this).parents('td');
  396. Delete.submit($td);
  397. event.handled = true;
  398. }
  399. });
  400. }
  401. if (settings.restoreButton) {
  402. /**
  403. * Restore one row.
  404. *
  405. * @param {object} event
  406. */
  407. $table.on('click', 'button.tabledit-restore-button', function(event) {
  408. if (event.handled !== true) {
  409. event.preventDefault();
  410. Delete.restore($(this).parents('td'));
  411. event.handled = true;
  412. }
  413. });
  414. }
  415. if (settings.editButton) {
  416. /**
  417. * Activate edit mode on all columns.
  418. *
  419. * @param {object} event
  420. */
  421. $table.on('click', 'button.tabledit-edit-button', function(event) {
  422. if (event.handled !== true) {
  423. event.preventDefault();
  424. var $button = $(this);
  425. // Get current state before reset to view mode.
  426. var activated = $button.hasClass('active');
  427. // Change to view mode columns that are in edit mode.
  428. Edit.reset($table.find('td.tabledit-edit-mode'));
  429. if (!activated) {
  430. // Change to edit mode for all columns in reverse way.
  431. $($button.parents('tr').find('td.tabledit-view-mode').get().reverse()).each(function() {
  432. Mode.edit(this);
  433. });
  434. }
  435. event.handled = true;
  436. }
  437. });
  438. /**
  439. * Save edited row.
  440. *
  441. * @param {object} event
  442. */
  443. $table.on('click', 'button.tabledit-save-button', function(event) {
  444. if (event.handled !== true) {
  445. event.preventDefault();
  446. // Submit and update all columns.
  447. Edit.submit($(this).parents('tr').find('td.tabledit-edit-mode'));
  448. event.handled = true;
  449. }
  450. });
  451. } else {
  452. /**
  453. * Change to edit mode on table td element.
  454. *
  455. * @param {object} event
  456. */
  457. $table.on(settings.eventType, 'tr:not(.tabledit-deleted-row) td.tabledit-view-mode', function(event) {
  458. if (event.handled !== true) {
  459. event.preventDefault();
  460. // Reset all td's in edit mode.
  461. Edit.reset($table.find('td.tabledit-edit-mode'));
  462. // Change to edit mode.
  463. Mode.edit(this);
  464. event.handled = true;
  465. }
  466. });
  467. /**
  468. * Change event when input is a select element.
  469. */
  470. $table.on('change', 'select.tabledit-input:visible', function() {
  471. if (event.handled !== true) {
  472. // Submit and update the column.
  473. Edit.submit($(this).parent('td'));
  474. event.handled = true;
  475. }
  476. });
  477. /**
  478. * Click event on document element.
  479. *
  480. * @param {object} event
  481. */
  482. $(document).on('click', function(event) {
  483. var $editMode = $table.find('.tabledit-edit-mode');
  484. // Reset visible edit mode column.
  485. if (!$editMode.is(event.target) && $editMode.has(event.target).length === 0) {
  486. Edit.reset($table.find('.tabledit-input:visible').parent('td'));
  487. }
  488. });
  489. }
  490. /**
  491. * Keyup event on document element.
  492. *
  493. * @param {object} event
  494. */
  495. $(document).on('keyup', function(event) {
  496. // Get input element with focus or confirmation button.
  497. var $input = $table.find('.tabledit-input:visible');
  498. var $button = $table.find('.tabledit-confirm-button');
  499. if ($input.length > 0) {
  500. var $td = $input.parents('td');
  501. } else if ($button.length > 0) {
  502. var $td = $button.parents('td');
  503. } else {
  504. return;
  505. }
  506. // Key?
  507. switch (event.keyCode) {
  508. case 9: // Tab.
  509. if (!settings.editButton) {
  510. Edit.submit($td);
  511. Mode.edit($td.closest('td').next());
  512. }
  513. break;
  514. case 13: // Enter.
  515. Edit.submit($td);
  516. break;
  517. case 27: // Escape.
  518. Edit.reset($td);
  519. Delete.reset($td);
  520. break;
  521. }
  522. });
  523. return this;
  524. };
  525. }(jQuery));