· 8 years ago · Aug 17, 2018, 02:30 PM
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/**
8 * @description Inline editor for HTML tables compatible with Bootstrap
9 * @version 1.2.3
10 * @author Celso Marques
11 */
12
13if (typeof jQuery === 'undefined') {
14 throw new Error('Tabledit requires jQuery library.');
15}
16
17(function($) {
18 'use strict';
19
20 $.fn.Tabledit = function(options) {
21 if (!this.is('table')) {
22 throw new Error('Tabledit only works when applied to a table.');
23 }
24
25 var $table = this;
26
27 var defaults = {
28 url: window.location.href,
29 inputClass: 'form-control input-xs',
30 toolbarClass: 'btn-toolbar',
31 groupClass: 'btn-group btn-group-xs',
32 dangerClass: 'danger',
33 warningClass: 'warning',
34 mutedClass: 'text-muted',
35 eventType: 'click',
36 rowIdentifier: 'id',
37 hideIdentifier: false,
38 autoFocus: true,
39 editButton: true,
40 deleteButton: true,
41 saveButton: true,
42 restoreButton: true,
43 addButton: true,
44 buttons: {
45 edit: {
46 class: 'btn btn-xs btn-default',
47 html: '<span class="glyphicon glyphicon-pencil"></span>',
48 action: 'edit'
49 },
50 add: {
51 class: 'btn btn-xs btn-default',
52 html: '<span class="fa fa-plus"></span> Agregar',
53 action: 'add'
54 },
55 delete: {
56 class: 'btn btn-xs btn-default',
57 html: '<span class="glyphicon glyphicon-trash"></span>',
58 action: 'delete'
59 },
60 save: {
61 class: 'btn btn-xs btn-default',
62 html: '<span class="fa fa-check"></span>'
63 },
64 cancel: {
65 class: 'btn btn-xs btn-default',
66 html: '<span class="fa fa-times"></span>'
67 },
68 restore: {
69 class: 'btn btn-xs btn-warning',
70 html: 'Restaurar',
71 action: 'restore'
72 },
73 confirm: {
74 class: 'btn btn-xs btn-danger',
75 html: 'Confirmar'
76 }
77 },
78 onDraw: function() { return; },
79 onSuccess: function() { return; },
80 onFail: function() { return; },
81 onAlways: function() { return; },
82 onAjax: function() { return; }
83 };
84
85 var settings = $.extend(true, defaults, options);
86
87 var $lastEditedRow = 'undefined';
88 var $lastDeletedRow = 'undefined';
89 var $lastRestoredRow = 'undefined';
90
91 /**
92 * Draw Tabledit structure (identifier column, editable columns, toolbar column).
93 *
94 * @type {object}
95 */
96 var Draw = {
97 columns: {
98 identifier: function() {
99 // Hide identifier column.
100 if (settings.hideIdentifier) {
101 $table.find('th:nth-child(' + parseInt(settings.columns.identifier[0]) + 1 + '), tbody td:nth-child(' + parseInt(settings.columns.identifier[0]) + 1 + ')').hide();
102 }
103
104 var $td = $table.find('tbody td:nth-child(' + (parseInt(settings.columns.identifier[0]) + 1) + ')');
105
106 $td.each(function() {
107 // Create hidden input with row identifier.
108 var span = '<span class="tabledit-span tabledit-identifier">' + $(this).text() + '</span>';
109 var input = '<input class="tabledit-input tabledit-identifier" type="hidden" name="' + settings.columns.identifier[1] + '" value="' + $(this).text() + '" disabled>';
110
111 // Add elements to table cell.
112 $(this).html(span + input);
113
114 // Add attribute "id" to table row.
115 $(this).parent('tr').attr(settings.rowIdentifier, $(this).text());
116 });
117 },
118 editable: function() {
119 for (var i = 0; i < settings.columns.editable.length; i++) {
120 var $td = $table.find('tbody td:nth-child(' + (parseInt(settings.columns.editable[i][0]) + 1) + ')');
121
122 $td.each(function() {
123 // Get text of this cell.
124 var text = $(this).text();
125
126 // Add pointer as cursor.
127 if (!settings.editButton) {
128 $(this).css('cursor', 'pointer');
129 }
130
131 // Create span element.
132 var span = '<span class="tabledit-span">' + text + '</span>';
133
134 // Check if exists the third parameter of editable array.
135 if (typeof settings.columns.editable[i][2] !== 'undefined') {
136 // Create select element.
137 var input = '<select class="tabledit-input ' + settings.inputClass + '" name="' + settings.columns.editable[i][1] + '" style="display: none;" disabled>';
138
139 // Create options for select element.
140 $.each(jQuery.parseJSON(settings.columns.editable[i][2]), function(index, value) {
141 if (text === value) {
142 input += '<option value="' + index + '" selected>' + value + '</option>';
143 } else {
144 input += '<option value="' + index + '">' + value + '</option>';
145 }
146 });
147
148 // Create last piece of select element.
149 input += '</select>';
150 } else {
151 // Create text input element.
152 var input = '<input class="tabledit-input ' + settings.inputClass + '" type="text" name="' + settings.columns.editable[i][1] + '" value="' + $(this).text() + '" style="display: none;" disabled>';
153 }
154
155 // Add elements and class "view" to table cell.
156 $(this).html(span + input);
157 $(this).addClass('tabledit-view-mode');
158 });
159 }
160 },
161 toolbar: function() {
162 if (settings.editButton || settings.deleteButton) {
163 var editButton = '';
164 var deleteButton = '';
165 var saveButton = '';
166 var restoreButton = '';
167 var confirmButton = '';
168 var addButton = '';
169 var addCancelButton = '';
170
171 // Add toolbar column header if not exists.
172 if ($table.find('th.tabledit-toolbar-column').length === 0) {
173 $table.find('tr:first').append('<th class="tabledit-toolbar-column"></th>');
174 }
175 // Create Add Button
176 if (settings.addButton) {
177 addButton = '<button type="button" class="tabledit-add-button ' + settings.buttons.add.class + '" style="float: none;">' + settings.buttons.add.html + '</button>';
178 addCancelButton = '<button type="button" class="tabledit-cancel-button ' + settings.buttons.cancel.class + '" style="float: none;display: none;">' + settings.buttons.cancel.html + '</button>';
179 }
180 // Create edit button.
181 if (settings.editButton) {
182 editButton = '<button type="button" class="tabledit-edit-button ' + settings.buttons.edit.class + '" style="float: none;">' + settings.buttons.edit.html + '</button>';
183 }
184
185 // Create delete button.
186 if (settings.deleteButton) {
187 deleteButton = '<button type="button" class="tabledit-delete-button ' + settings.buttons.delete.class + '" style="float: none;">' + settings.buttons.delete.html + '</button>';
188 confirmButton = '<button type="button" class="tabledit-confirm-button ' + settings.buttons.confirm.class + '" style="display: none; float: none;">' + settings.buttons.confirm.html + '</button>';
189 }
190
191 // Create save button.
192 if (settings.editButton && settings.saveButton) {
193 saveButton = '<button type="button" class="tabledit-save-button ' + settings.buttons.save.class + '" style="display: none; float: none;">' + settings.buttons.save.html + '</button>';
194 }
195
196 // Create restore button.
197 if (settings.deleteButton && settings.restoreButton) {
198 restoreButton = '<button type="button" class="tabledit-restore-button ' + settings.buttons.restore.class + '" style="display: none; float: none;">' + settings.buttons.restore.html + '</button>';
199 }
200
201 var toolbar = '<div class="tabledit-toolbar ' + settings.toolbarClass + '" style="text-align: left;">\n\
202 <div class="' + settings.groupClass + '" style="float: none;">' + editButton + deleteButton + '</div>\n\
203 ' + saveButton + '\n\
204 ' + addCancelButton + '\n\
205 ' + confirmButton + '\n\
206 ' + restoreButton + '\n\
207 </div></div>';
208
209 var headerToolbar = '<div class="tabledit-toolbar ' + settings.toolbarClass + '" style="text-align: left;">\n\
210 <div class="' + settings.groupClass + ' pull-right" style="float: none;">' + addButton + '</div></div></div>';
211 // Add toolbar column cells.
212 $table.find('th.tabledit-toolbar-column').html(headerToolbar);
213 $table.find('tr:gt(0)').append('<td style="white-space: nowrap; width: 1%;">' + toolbar + '</td>');
214 }
215 }
216 }
217 };
218
219 /**
220 * Change to view mode or edit mode with table td element as parameter.
221 *
222 * @type object
223 */
224 var Mode = {
225 view: function(td) {
226 // Get table row.
227 var $tr = $(td).parent('tr');
228 // Disable identifier.
229 $(td).parent('tr').find('.tabledit-input.tabledit-identifier').prop('disabled', true);
230 // Hide and disable input element.
231 $(td).find('.tabledit-input').blur().hide().prop('disabled', true);
232 // Show span element.
233 $(td).find('.tabledit-span').show();
234 // Add "view" class and remove "edit" class in td element.
235 $(td).addClass('tabledit-view-mode').removeClass('tabledit-edit-mode');
236 // Update toolbar buttons.
237 if (settings.editButton) {
238 $tr.find('button.tabledit-save-button').hide();
239 $tr.find('button.tabledit-edit-button').removeClass('active').blur();
240 }
241 if (settings.addButton) {
242 $tr.find('button.tabledit-edit-button').show();
243 $tr.find('button.tabledit-delete-button').show();
244 $tr.find('button.tabledit-save-button').hide();
245 $tr.find('button.tabledit-cancel-button').hide();
246 }
247 },
248 edit: function(td) {
249 Delete.reset(td);
250 // Get table row.
251 var $tr = $(td).parent('tr');
252 // Enable identifier.
253 $tr.find('.tabledit-input.tabledit-identifier').prop('disabled', false);
254 // Hide span element.
255 $(td).find('.tabledit-span').hide();
256 // Get input element.
257 var $input = $(td).find('.tabledit-input');
258 // Enable and show input element.
259 $input.prop('disabled', false).show();
260 // Focus on input element.
261 if (settings.autoFocus) {
262 $input.focus();
263 }
264 // Add "edit" class and remove "view" class in td element.
265 $(td).addClass('tabledit-edit-mode').removeClass('tabledit-view-mode');
266 // Update toolbar buttons.
267 if (settings.editButton) {
268 $tr.find('button.tabledit-edit-button').addClass('active');
269 $tr.find('button.tabledit-save-button').show();
270 }
271 },
272 add: function (td) {
273 Delete.reset(td);
274 // Get table row.
275 var $tr = $(td).parent('tr');
276 // Enable identifier.
277 $tr.find('.tabledit-input.tabledit-identifier').prop('disabled', false);
278 // Hide span element.
279 $(td).find('.tabledit-span').hide();
280 // Get input element.
281 var $input = $(td).find('.tabledit-input');
282 // Enable and show input element.
283 $input.prop('disabled', false).show();
284 // Focus on input element.
285 if (settings.autoFocus) {
286 $input.focus();
287 }
288 // Add "edit" class and remove "view" class in td element.
289 $(td).addClass('tabledit-edit-mode').removeClass('tabledit-view-mode');
290 // Update toolbar buttons.
291 if (settings.addButton) {
292 $tr.find('button.tabledit-edit-button').hide();
293 $tr.find('button.tabledit-delete-button').hide();
294 $tr.find('button.tabledit-save-button').show();
295 $tr.find('button.tabledit-cancel-button').show();
296 }
297 }
298 };
299
300 /**
301 * Available actions for edit function, with table td element as parameter or set of td elements.
302 *
303 * @type object
304 */
305 var Edit = {
306 reset: function(td) {
307 $(td).each(function() {
308 // Get input element.
309 var $input = $(this).find('.tabledit-input');
310 // Get span text.
311 var text = $(this).find('.tabledit-span').text();
312 // Set input/select value with span text.
313 if ($input.is('select')) {
314 $input.find('option').filter(function() {
315 return $.trim($(this).text()) === text;
316 }).attr('selected', true);
317 } else {
318 $input.val(text);
319 }
320 // Change to view mode.
321 Mode.view(this);
322 });
323 },
324 remove: function (td) {
325 $(td).parent('tr').remove();
326 },
327 submit: function(td) {
328 // Send AJAX request to server.
329 var ajaxResult = ajax(settings.buttons.edit.action);
330
331 if (ajaxResult === false) {
332 return;
333 }
334
335 $(td).each(function() {
336 // Get input element.
337 var $input = $(this).find('.tabledit-input');
338 // Set span text with input/select new value.
339 if ($input.is('select')) {
340 $(this).find('.tabledit-span').text($input.find('option:selected').text());
341 } else {
342 $(this).find('.tabledit-span').text($input.val());
343 }
344 // Change to view mode.
345 Mode.view(this);
346 });
347
348 // Set last edited column and row.
349 $lastEditedRow = $(td).parent('tr');
350 },
351 addSubmit: function (td) {
352 // Send AJAX request to server.
353 var ajaxResult = ajax(settings.buttons.add.action);
354
355 if (ajaxResult === false) {
356 $(td).parent('tr').remove()
357 return;
358 }
359
360 $(td).each(function () {
361 // Get input element.
362 var $input = $(this).find('.tabledit-input');
363 // Set span text with input/select new value.
364 if ($input.is('select')) {
365 $(this).find('.tabledit-span').text($input.find('option:selected').text());
366 } else {
367 $(this).find('.tabledit-span').text($input.val());
368 }
369 // Change to view mode.
370 Mode.view(this);
371 });
372
373 // Set last edited column and row.
374 $lastEditedRow = $(td).parent('tr');
375 }
376 };
377
378 /**
379 * Available actions for delete function, with button as parameter.
380 *
381 * @type object
382 */
383 var Delete = {
384 reset: function(td) {
385 // Reset delete button to initial status.
386 $table.find('.tabledit-confirm-button').hide();
387 // Remove "active" class in delete button.
388 $table.find('.tabledit-delete-button').removeClass('active').blur();
389 },
390 submit: function(td) {
391 Delete.reset(td);
392 // Enable identifier hidden input.
393 $(td).parent('tr').find('input.tabledit-identifier').attr('disabled', false);
394 // Send AJAX request to server.
395 var ajaxResult = ajax(settings.buttons.delete.action);
396 // Disable identifier hidden input.
397 $(td).parents('tr').find('input.tabledit-identifier').attr('disabled', true);
398
399 if (ajaxResult === false) {
400 return;
401 }
402
403 // Add class "deleted" to row.
404 $(td).parent('tr').addClass('tabledit-deleted-row');
405 // Hide table row.
406 $(td).parent('tr').addClass(settings.mutedClass).find('.tabledit-toolbar button:not(.tabledit-restore-button)').attr('disabled', true);
407 // Show restore button.
408 $(td).find('.tabledit-restore-button').show();
409 // Set last deleted row.
410 $lastDeletedRow = $(td).parent('tr');
411 //delete row if not confirm button
412 if (!settings.restoreButton) {
413 $(td).parent('tr').remove();
414 }
415 },
416 confirm: function(td) {
417 // Reset all cells in edit mode.
418 $table.find('td.tabledit-edit-mode').each(function() {
419 Edit.reset(this);
420 });
421 // Add "active" class in delete button.
422 $(td).find('.tabledit-delete-button').addClass('active');
423 // Show confirm button.
424 $(td).find('.tabledit-confirm-button').show();
425 },
426 restore: function(td) {
427 // Enable identifier hidden input.
428 $(td).parent('tr').find('input.tabledit-identifier').attr('disabled', false);
429 // Send AJAX request to server.
430 var ajaxResult = ajax(settings.buttons.restore.action);
431 // Disable identifier hidden input.
432 $(td).parents('tr').find('input.tabledit-identifier').attr('disabled', true);
433
434 if (ajaxResult === false) {
435 return;
436 }
437
438 // Remove class "deleted" to row.
439 $(td).parent('tr').removeClass('tabledit-deleted-row');
440 // Hide table row.
441 $(td).parent('tr').removeClass(settings.mutedClass).find('.tabledit-toolbar button').attr('disabled', false);
442 // Hide restore button.
443 $(td).find('.tabledit-restore-button').hide();
444 // Set last restored row.
445 $lastRestoredRow = $(td).parent('tr');
446 }
447 };
448
449 /**
450 * Send AJAX request to server.
451 *
452 * @param {string} action
453 */
454 function ajax(action)
455 {
456 var serialize = $table.find('.tabledit-input').serialize() + '&action=' + action;
457
458 var result = settings.onAjax(action, serialize);
459
460 if (result === false) {
461 return false;
462 }
463
464 var jqXHR = $.post(settings.url, serialize, function(data, textStatus, jqXHR) {
465 if (action === settings.buttons.edit.action) {
466 $lastEditedRow.removeClass(settings.dangerClass).addClass(settings.warningClass);
467 setTimeout(function() {
468 //$lastEditedRow.removeClass(settings.warningClass);
469 $table.find('tr.' + settings.warningClass).removeClass(settings.warningClass);
470 }, 1400);
471 }
472
473 settings.onSuccess(data, textStatus, jqXHR);
474 }, 'json');
475
476 jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
477 if (action === settings.buttons.delete.action) {
478 $lastDeletedRow.removeClass(settings.mutedClass).addClass(settings.dangerClass);
479 $lastDeletedRow.find('.tabledit-toolbar button').attr('disabled', false);
480 $lastDeletedRow.find('.tabledit-toolbar .tabledit-restore-button').hide();
481 } else if (action === settings.buttons.edit.action) {
482 $lastEditedRow.addClass(settings.dangerClass);
483 }
484
485 settings.onFail(jqXHR, textStatus, errorThrown);
486 });
487
488 jqXHR.always(function() {
489 settings.onAlways();
490 });
491
492 return jqXHR;
493 }
494
495 Draw.columns.identifier();
496 Draw.columns.editable();
497 Draw.columns.toolbar();
498
499 settings.onDraw();
500
501 if (settings.deleteButton) {
502 /**
503 * Delete one row.
504 *
505 * @param {object} event
506 */
507 $table.on('click', 'button.tabledit-delete-button', function(event) {
508 if (event.handled !== true) {
509 event.preventDefault();
510
511 // Get current state before reset to view mode.
512 var activated = $(this).hasClass('active');
513
514 var $td = $(this).parents('td');
515
516 Delete.reset($td);
517
518 if (!activated) {
519 Delete.confirm($td);
520 }
521
522 event.handled = true;
523 }
524 });
525
526 /**
527 * Delete one row (confirm).
528 *
529 * @param {object} event
530 */
531 $table.on('click', 'button.tabledit-confirm-button', function(event) {
532 if (event.handled !== true) {
533 event.preventDefault();
534
535 var $td = $(this).parents('td');
536
537 Delete.submit($td);
538
539 event.handled = true;
540 }
541 });
542 }
543
544 if (settings.restoreButton) {
545 /**
546 * Restore one row.
547 *
548 * @param {object} event
549 */
550 $table.on('click', 'button.tabledit-restore-button', function(event) {
551 if (event.handled !== true) {
552 event.preventDefault();
553
554 Delete.restore($(this).parents('td'));
555
556 event.handled = true;
557 }
558 });
559 }
560
561 if (settings.addButton) {
562 /**
563 * Activate edit mode on all columns.
564 *
565 * @param {object} event
566 */
567 $table.on('click', 'button.tabledit-add-button', function (event) {
568 if (event.handled !== true) {
569 event.preventDefault();
570
571 var $button = $(this);
572
573 // Get current state before reset to view mode.
574 var activated = $button.hasClass('active');
575
576 if (!activated) {
577 // Change to edit mode for all columns in reverse way.
578 var tableditTableName = '#' + $table.attr('id');
579 var newID = parseInt($(tableditTableName + " tr:last").attr("id")) + 1;
580 var clone = $("table tr:last").clone();
581 $(".tabledit-span", clone).text("");
582 $(".tabledit-input", clone).val("");
583 clone.prependTo(tableditTableName);
584 $(tableditTableName + " tbody tr:first").attr("id", newID);
585 $($(tableditTableName + " tbody tr:first").find('td.tabledit-view-mode').get().reverse()).each(function () {
586 Mode.add(this);
587 });
588 }
589
590 event.handled = true;
591 }
592 });
593
594 /**
595 * Save edited row.
596 *
597 * @param {object} event
598 */
599 $table.on('click', 'button.tabledit-save-button', function (event) {
600 if (event.handled !== true) {
601 event.preventDefault();
602
603 // Submit and update all columns.
604 Edit.addSubmit($(this).parents('tr').find('td.tabledit-edit-mode'));
605
606 event.handled = true;
607 }
608 });
609 $table.on('click', 'button.tabledit-cancel-button', function (event) {
610 if (event.handled !== true) {
611 event.preventDefault();
612
613 // Submit and update all columns.
614 Edit.remove($(this).parents('tr').find('td.tabledit-edit-mode'));
615
616 event.handled = true;
617 }
618 });
619 }
620
621 if (settings.editButton) {
622 /**
623 * Activate edit mode on all columns.
624 *
625 * @param {object} event
626 */
627 $table.on('click', 'button.tabledit-edit-button', function(event) {
628 if (event.handled !== true) {
629 event.preventDefault();
630
631 var $button = $(this);
632
633 // Get current state before reset to view mode.
634 var activated = $button.hasClass('active');
635
636 // Change to view mode columns that are in edit mode.
637 Edit.reset($table.find('td.tabledit-edit-mode'));
638
639 if (!activated) {
640 // Change to edit mode for all columns in reverse way.
641 $($button.parents('tr').find('td.tabledit-view-mode').get().reverse()).each(function() {
642 Mode.edit(this);
643 });
644 }
645
646 event.handled = true;
647 }
648 });
649
650 /**
651 * Save edited row.
652 *
653 * @param {object} event
654 */
655 $table.on('click', 'button.tabledit-save-button', function(event) {
656 if (event.handled !== true) {
657 event.preventDefault();
658
659 // Submit and update all columns.
660 Edit.submit($(this).parents('tr').find('td.tabledit-edit-mode'));
661
662 event.handled = true;
663 }
664 });
665 } else {
666 /**
667 * Change to edit mode on table td element.
668 *
669 * @param {object} event
670 */
671 $table.on(settings.eventType, 'tr:not(.tabledit-deleted-row) td.tabledit-view-mode', function(event) {
672 if (event.handled !== true) {
673 event.preventDefault();
674
675 // Reset all td's in edit mode.
676 Edit.reset($table.find('td.tabledit-edit-mode'));
677
678 // Change to edit mode.
679 Mode.edit(this);
680
681 event.handled = true;
682 }
683 });
684
685 /**
686 * Change event when input is a select element.
687 */
688 $table.on('change', 'select.tabledit-input:visible', function() {
689 if (event.handled !== true) {
690 // Submit and update the column.
691 Edit.submit($(this).parent('td'));
692
693 event.handled = true;
694 }
695 });
696
697 /**
698 * Click event on document element.
699 *
700 * @param {object} event
701 */
702 $(document).on('click', function(event) {
703 var $editMode = $table.find('.tabledit-edit-mode');
704 // Reset visible edit mode column.
705 if (!$editMode.is(event.target) && $editMode.has(event.target).length === 0) {
706 Edit.reset($table.find('.tabledit-input:visible').parent('td'));
707 }
708 });
709 }
710
711 /**
712 * Keyup event on document element.
713 *
714 * @param {object} event
715 */
716 $(document).on('keyup', function(event) {
717 // Get input element with focus or confirmation button.
718 var $input = $table.find('.tabledit-input:visible');
719 var $button = $table.find('.tabledit-confirm-button');
720
721 if ($input.length > 0) {
722 var $td = $input.parents('td');
723 } else if ($button.length > 0) {
724 var $td = $button.parents('td');
725 } else {
726 return;
727 }
728
729 // Key?
730 switch (event.keyCode) {
731 case 9: // Tab.
732 if (!settings.editButton) {
733 Edit.submit($td);
734 Mode.edit($td.closest('td').next());
735 }
736 break;
737 case 13: // Enter.
738 Edit.submit($td);
739 break;
740 case 27: // Escape.
741 Edit.reset($td);
742 Delete.reset($td);
743 break;
744 }
745 });
746
747 return this;
748 };
749}(jQuery));