source: main/trunk/model-sites-dev/cambridge-museum/collect/waikato-independent/pre-import/EditableDatabaseTable/WebContent/scripts/jquery.dataTables.editable.js@ 34493

Last change on this file since 34493 was 34493, checked in by davidb, 4 years ago

Base project for providing jquery/jquery-ui controlled interface to editing a database table

File size: 23.2 KB
Line 
1 /*
2* File: jquery.dataTables.editable.js
3* Version: 1.1.5.
4* Author: Jovan Popovic
5*
6* Copyright 2010-2011 Jovan Popovic, all rights reserved.
7*
8* This source file is free software, under either the GPL v2 license or a
9* BSD style license, as supplied with this software.
10*
11* This source file is distributed in the hope that it will be useful, but
12* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13* or FITNESS FOR A PARTICULAR PURPOSE.
14*
15* Parameters:
16* @sUpdateURL String URL of the server-side page used for updating cell. Default value is "UpdateData".
17* @sAddURL String URL of the server-side page used for adding new row. Default value is "AddData".
18* @sDeleteURL String URL of the server-side page used to delete row by id. Default value is "DeleteData".
19* @fnShowError Function function(message, action){...} used to show error message. Action value can be "update", "add" or "delete".
20* @sAddNewRowFormId String Id of the form for adding new row. Default id is "formAddNewRow".
21* @sAddNewRowButtonId String Id of the button for adding new row. Default id is "btnAddNewRow".
22* @sAddNewRowOkButtonId String Id of the OK button placed in add new row dialog. Default value is "btnAddNewRowOk".
23* @sAddNewRowCancelButtonId String Id of the OK button placed in add new row dialog. Default value is "btnAddNewRowCancel".
24* @sDeleteRowButtonId String Id of the button for adding new row. Default id is "btnDeleteRow".
25* @sSelectedRowClass String Class that will be associated to the selected row. Default class is "row_selected".
26* @sReadOnlyCellClass String Class of the cells that should not be editable. Default value is "read_only".
27* @sAddDeleteToolbarSelector String Selector used to identify place where add and delete buttons should be placed. Default value is ".add_delete_toolbar".
28* @fnStartProcessingMode Function function(){...} called when AJAX call is started. Use this function to add "Please wait..." message when some button is pressed.
29* @fnEndProcessingMode Function function(){...} called when AJAX call is ended. Use this function to close "Please wait..." message.
30* @aoColumns Array Array of the JEditable settings that will be applied on the columns
31* @sAddHttpMethod String Method used for the Add AJAX request (default is 'POST')
32* @sDeleteHttpMethod String Method used for the Delete AJAX request (default is 'POST')
33* @fnOnDeleting Function function(tr, id){...} Function called before row is deleted.
34 tr isJQuery object encapsulating row that will be deleted
35 id is an id of the record that will be deleted.
36 returns true if plugin should continue with deleting row, false will abort delete.
37* @fnOnDeleted Function function(status){...} Function called after delete action. Status can be "success" or "failure"
38* @fnOnAdding Function function(){...} Function called before row is added.
39 returns true if plugin should continue with adding row, false will abort add.
40* @fnOnAdded Function function(status){...} Function called after delete action. Status can be "success" or "failure"
41* @fnOnEditing Function function(input){...} Function called before cell is updated.
42 input JQuery object wrapping the inut element used for editing value in the cell.
43 returns true if plugin should continue with sending AJAX request, false will abort update.
44* @fnOnEdited Function function(status){...} Function called after edit action. Status can be "success" or "failure"
45*/
46(function ($) {
47
48 $.fn.makeEditable = function (options) {
49
50 var iDisplayStart = 0;
51
52 ///Utility function used to determine id of the cell
53 //By default it is assumed that id is placed as an id attribute of <tr> that that surround the cell (<td> tag). E.g.:
54 //<tr id="17">
55 // <td>...</td><td>...</td><td>...</td><td>...</td>
56 //</tr>
57 function fnGetCellID(cell) {
58 return properties.fnGetRowID($(cell.parentNode));
59 }
60
61 ///Utility function used to set id of the new row
62 //It is assumed that id is placed as an id attribute of <tr> that that surround the cell (<td> tag). E.g.:
63 //<tr id="17">
64 // <td>...</td><td>...</td><td>...</td><td>...</td>
65 //</tr>
66 function _fnSetRowIDInAttribute(row, id) {
67 row.attr("id", id);
68 }
69
70 //Utility function used to get id of the row
71 //It is assumed that id is placed as an id attribute of <tr> that that surround the cell (<td> tag). E.g.:
72 //<tr id="17">
73 // <td>...</td><td>...</td><td>...</td><td>...</td>
74 //</tr>
75 function _fnGetRowIDFromAttribute(row) {
76 return row.attr("id");
77 }
78
79 //Utility function used to set id of the new row
80 //It is assumed that id is placed as an id attribute of <tr> that that surround the cell (<td> tag). E.g.:
81 //<tr>
82 // <td>17</td><td>...</td><td>...</td><td>...</td>
83 //</tr>
84 function _fnSetRowIDInFirstCell(row, id) {
85 $("td:first", row).html(id);
86 }
87
88 //Utility function used to get id of the row
89 //It is assumed that id is placed as an id attribute of <tr> that that surround the cell (<td> tag). E.g.:
90 //<tr>
91 // <td>17</td><td>...</td><td>...</td><td>...</td>
92 //</tr>
93 function _fnGetRowIDFromFirstCell(row) {
94 return $("td:first", row).html();
95 }
96
97 //Reference to the DataTable object
98 var oTable;
99 //Refences to the buttons used for manipulating table data
100 var oAddNewRowButton, oDeleteRowButton, oConfirmRowAddingButton, oCancelRowAddingButton;
101 //Reference to the form used for adding new data
102 var oAddNewRowForm;
103
104 //Plugin options
105 var properties;
106
107 /// Utility function that shows an error message
108 ///@param errorText - text that should be shown
109 ///@param action - action that was executed when error occured e.g. "update", "delete", or "add"
110 function fnShowError(errorText, action) {
111 alert(errorText);
112 }
113
114 //Utility function that put the table into the "Processing" state
115 function fnStartProcessingMode() {
116 if (oTable.fnSettings().oFeatures.bProcessing) {
117 $(".dataTables_processing").css('visibility', 'visible');
118 }
119 }
120
121 //Utility function that put the table in the normal state
122 function fnEndProcessingMode() {
123 if (oTable.fnSettings().oFeatures.bProcessing) {
124 $(".dataTables_processing").css('visibility', 'hidden');
125 }
126 }
127
128 var sOldValue, sNewCellValue, sNewCellDislayValue;
129 //Utility function used to apply editable plugin on table cells
130 function _fnApplyEditable(aoNodes) {
131 var oDefaultEditableSettings = {
132 event: 'dblclick',
133 "callback": function (sValue, settings) {
134 properties.fnEndProcessingMode();
135 if (sNewCellValue == sValue) {
136 var aPos = oTable.fnGetPosition(this);
137 oTable.fnUpdate(sNewCellDisplayValue, aPos[0], aPos[2]);
138 properties.fnOnEdited("success");
139 } else {
140 var aPos = oTable.fnGetPosition(this);
141 oTable.fnUpdate(sOldValue, aPos[0], aPos[2]);
142 properties.fnShowError(sValue, "update");
143 properties.fnOnEdited("failure");
144 }
145 _fnSetDisplayStart();
146
147 },
148 "onsubmit": function (settings, original) {
149 var input = $("input,select,textarea", this);
150 sOldValue = original.revert;
151 sNewCellValue = $("input,select,textarea", $(this)).val();
152 if (input.length == 1) {
153 var oEditElement = input[0];
154 if (oEditElement.nodeName.toLowerCase() == "select" || oEditElement.tagName.toLowerCase() == "select")
155 sNewCellDisplayValue = $("option:selected", oEditElement).text(); //For select list use selected text instead of value for displaying in table
156 else
157 sNewCellDisplayValue = sNewCellValue;
158 }
159
160 if (!properties.fnOnEditing(input))
161 return false;
162 var x = settings;
163 if (settings.cssclass != null) {
164 input.addClass(settings.cssclass);
165 if (!input.valid() || 0 == input.valid())
166 return false;
167 else
168 return true;
169 }
170 },
171 "submitdata": function (value, settings) {
172 iDisplayStart = _fnGetDisplayStart();
173 properties.fnStartProcessingMode();
174 var id = fnGetCellID(this);
175 var rowId = oTable.fnGetPosition(this)[0];
176 var columnPosition = oTable.fnGetPosition(this)[1];
177 var columnId = oTable.fnGetPosition(this)[2];
178 var sColumnName = oTable.fnSettings().aoColumns[columnId].sName;
179 if (sColumnName == null || sColumnName == "")
180 sColumnName = oTable.fnSettings().aoColumns[columnId].sTitle;
181 return {
182 "id": id,
183 "rowId": rowId,
184 "columnPosition": columnPosition,
185 "columnId": columnId,
186 "columnName": sColumnName
187 };
188 },
189 "onerror": function () {
190 properties.fnEndProcessingMode();
191 properties.fnShowError("Cell cannot be updated(Server error)", "update");
192 properties.fnOnEdited("failure");
193 },
194 "height": properties.height
195 };
196
197 var cells = null;
198 if (properties.aoColumns != null) {
199 for (var i = 0; i < properties.aoColumns.length; i++) {
200 if (properties.aoColumns[i] != null) {
201 cells = $("td:nth-child(" + (i + 1) + ")", aoNodes);
202 var oColumnSettings = oDefaultEditableSettings;
203 oColumnSettings = $.extend({}, properties.aoColumns[i], oDefaultEditableSettings);
204 cells.editable(properties.sUpdateURL, oColumnSettings);
205 }
206
207
208 }
209 } else {
210 cells = $('td:not(.' + properties.sReadOnlyCellClass + ')', aoNodes);
211 cells.editable(properties.sUpdateURL, oDefaultEditableSettings);
212
213 }
214
215 }
216
217 //Called when user confirm that he want to add new record
218 function _fnOnRowAdding(event) {
219 if (properties.fnOnAdding()) {
220 if (oAddNewRowForm.valid()) {
221 iDisplayStart = _fnGetDisplayStart();
222 properties.fnStartProcessingMode();
223 var params = oAddNewRowForm.serialize();
224 $.ajax({ 'url': properties.sAddURL,
225 'data': params,
226 'type': properties.sAddHttpMethod,
227 success: _fnOnRowAdded,
228 error: function (response) {
229 properties.fnEndProcessingMode();
230 properties.fnShowError(response.responseText, "add");
231 properties.fnOnAdded("failure");
232 }
233 });
234 }
235 }
236 event.stopPropagation();
237 event.preventDefault();
238 }
239
240 ///Event handler called when a new row is added and response is returned from server
241 function _fnOnRowAdded(data) {
242 properties.fnEndProcessingMode();
243
244 var iColumnCount = oTable.dataTableSettings[0].aoColumns.length;
245 var values = new Array();
246
247 $("input:text[rel],input:radio[rel][checked],input:hidden[rel],select[rel],textarea[rel]", oAddNewRowForm).each(function () {
248 var rel = $(this).attr("rel");
249 if (rel >= iColumnCount)
250 properties.fnShowError("In the add form is placed input element with the name '" + $(this).attr("name") + "' with the 'rel' attribute that must be less than a column count - " + iColumnCount, "add");
251 else{
252 if(this.nodeName.toLowerCase() == "select" || this.tagName.toLowerCase() == "select")
253 values[rel] = $("option:selected", this).text();
254 else
255 values[rel] = this.value;
256 }
257 });
258
259 //Add values from the form into the table
260 var rtn = oTable.fnAddData(values);
261 var oTRAdded = oTable.fnGetNodes(rtn);
262 //Apply editable plugin on the cells of the table
263 _fnApplyEditable(oTRAdded);
264 //add id returned by server page as an TR id attribute
265 properties.fnSetRowID($(oTRAdded), data);
266 //Close the dialog
267 oAddNewRowForm.dialog('close');
268 $(oAddNewRowForm)[0].reset();
269 $(".error", $(oAddNewRowForm)).html("");
270
271 _fnSetDisplayStart();
272 properties.fnOnAdded("success")
273 }
274
275 //Called when user cancels adding new record in the popup dialog
276 function _fnOnCancelRowAdding(event) {
277 //Close the dialog
278 oAddNewRowForm.dialog('close');
279 $(oAddNewRowForm)[0].reset();
280 $(".error", $(oAddNewRowForm)).html("");
281 event.stopPropagation();
282 event.preventDefault();
283 }
284
285
286
287
288
289 //Called when user deletes a row
290 function _fnOnRowDelete(event) {
291 iDisplayStart = _fnGetDisplayStart();
292 if ($('tr.' + properties.sSelectedRowClass + ' td', oTable).length == 0) {
293 oDeleteRowButton.attr("disabled", "true");
294 return;
295 }
296 var id = fnGetCellID($('tr.' + properties.sSelectedRowClass + ' td', oTable)[0]);
297 if (properties.fnOnDeleting($('tr.' + properties.sSelectedRowClass, oTable), id)) {
298 properties.fnStartProcessingMode();
299 $.ajax({ 'url': properties.sDeleteURL,
300 'type': properties.sDeleteHttpMethod,
301 'data': 'id=' + id,
302 "success": _fnOnRowDeleted,
303 "error": function (response) {
304 properties.fnEndProcessingMode();
305 properties.fnShowError(response.responseText, "delete");
306 properties.fnOnDeleted("failure");
307
308 }
309 });
310 }
311 }
312
313 //Called when record is deleted on the server
314 function _fnOnRowDeleted(response) {
315 properties.fnEndProcessingMode();
316 var oTRSelected = $('tr.' + properties.sSelectedRowClass, oTable)[0];
317 if (response == "ok" || response == "") {
318 oTable.fnDeleteRow(oTRSelected);
319 oDeleteRowButton.attr("disabled", "true");
320 _fnSetDisplayStart();
321 properties.fnOnDeleted("success");
322 }
323 else {
324 properties.fnShowError(response, "delete");
325 properties.fnOnDeleted("failure");
326 }
327 }
328
329 //Called before row is deleted
330 //Returning false will abort delete
331 /*
332 * Function called before row is deleted
333 * @param tr JQuery wrapped around the TR tag that will be deleted
334 * @param id id of the record that wil be deleted
335 * @return true if plugin should continue with deleting row, false will abort delete.
336 */
337 function fnOnDeleting(tr, id) {
338 return confirm("Are you sure that you want to delete this record?"); ;
339 }
340
341 /* Function called after delete action
342 * @param result string
343 * "success" if row is actually deleted
344 * "failure" if delete failed
345 * @return void
346 */
347 function fnOnDeleted(result) { }
348
349 function fnOnEditing(input) { return true; }
350 function fnOnEdited(result) { }
351
352 function fnOnAdding() { return true; }
353 function fnOnAdded(result) { }
354
355 var oSettings;
356 function _fnGetDisplayStart() {
357 return oSettings._iDisplayStart;
358 }
359
360 function _fnSetDisplayStart() {
361 if (oSettings.oFeatures.bServerSide === false) {
362 oSettings._iDisplayStart = iDisplayStart;
363 oSettings.oApi._fnCalculateEnd(oSettings);
364 //draw the 'current' page
365 oSettings.oApi._fnDraw(oSettings);
366 }
367 }
368
369
370 oTable = this;
371
372 var defaults = {
373
374 sUpdateURL: "UpdateData",
375 sAddURL: "AddData",
376 sDeleteURL: "DeleteData",
377 height: "14px",
378 sAddNewRowFormId: "formAddNewRow",
379 sAddNewRowButtonId: "btnAddNewRow",
380 sAddNewRowOkButtonId: "btnAddNewRowOk",
381 sAddNewRowCancelButtonId: "btnAddNewRowCancel",
382 sDeleteRowButtonId: "btnDeleteRow",
383 sSelectedRowClass: "row_selected",
384 sReadOnlyCellClass: "read_only",
385 sAddDeleteToolbarSelector: ".add_delete_toolbar",
386 fnShowError: fnShowError,
387 fnStartProcessingMode: fnStartProcessingMode,
388 fnEndProcessingMode: fnEndProcessingMode,
389 aoColumns: null,
390 fnOnDeleting: fnOnDeleting,
391 fnOnDeleted: fnOnDeleted,
392 fnOnAdding: fnOnAdding,
393 fnOnAdded: fnOnAdded,
394 fnOnEditing: fnOnEditing,
395 fnOnEdited: fnOnEdited,
396 sAddHttpMethod: 'POST',
397 sDeleteHttpMethod: 'POST',
398 fnGetRowID: _fnGetRowIDFromAttribute,
399 fnSetRowID: _fnSetRowIDInAttribute
400
401 };
402
403 properties = $.extend(defaults, options);
404 oSettings = oTable.fnSettings();
405
406 return this.each(function () {
407
408 if (oTable.fnSettings().sAjaxSource!=null) {
409 oTable.fnSettings().aoDrawCallback.push({
410 "fn": function () {
411 //Apply jEditable plugin on the table cells
412 _fnApplyEditable(oTable.fnGetNodes());
413 $(oTable.fnGetNodes()).each(function () {
414 var position = oTable.fnGetPosition(this);
415 var id = oTable.fnGetData(position)[0];
416 properties.fnSetRowID($(this), id);
417 }
418 );
419 },
420 "sName": "fnApplyEditable"
421 });
422
423 } else {
424 //Apply jEditable plugin on the table cells
425 _fnApplyEditable(oTable.fnGetNodes());
426 }
427
428 //Setup form to open in dialog
429 oAddNewRowForm = $("#" + properties.sAddNewRowFormId);
430 if (oAddNewRowForm.length != 0) {
431 oAddNewRowForm.dialog({ autoOpen: false });
432
433 //Add button click handler on the "Add new row" button
434 oAddNewRowButton = $("#" + properties.sAddNewRowButtonId);
435 if (oAddNewRowButton.length != 0) {
436 oAddNewRowButton.click(function () { oAddNewRowForm.dialog('open'); });
437 } else {
438 if ($(properties.sAddDeleteToolbarSelector).length == 0)
439 throw "Cannot find button for adding new row althogh form for adding new record is specified";
440 else
441 oAddNewRowButton = null;
442 }
443
444 oConfirmRowAddingButton = $("#" + properties.sAddNewRowOkButtonId, oAddNewRowForm);
445 if (oConfirmRowAddingButton.length == 0) {
446 oAddNewRowForm.append("<button id='" + properties.sAddNewRowOkButtonId + "'>Ok</button>");
447 oConfirmRowAddingButton = $("#" + properties.sAddNewRowOkButtonId, oAddNewRowForm);
448 }
449
450 //Add button click handler on the "Ok" button in the add new row dialog
451 oConfirmRowAddingButton.click(_fnOnRowAdding);
452
453 oCancelRowAddingButton = $("#" + properties.sAddNewRowCancelButtonId);
454 if (oCancelRowAddingButton.length == 0) {
455 oCancelRowAddingButton = oAddNewRowForm.append("<button id='" + properties.sAddNewRowCancelButtonId + "'>Cancel</button>");
456 oCancelRowAddingButton = $("#" + properties.sAddNewRowCancelButtonId);
457 }
458
459 oCancelRowAddingButton.click(_fnOnCancelRowAdding);
460 } else {
461 oAddNewRowForm = null;
462 }
463
464 //Set the click handler on the "Delete selected row" button
465 oDeleteRowButton = $('#' + properties.sDeleteRowButtonId);
466 if (oDeleteRowButton.length != 0)
467 oDeleteRowButton.click(_fnOnRowDelete);
468 else {
469 oDeleteRowButton = null;
470 }
471
472 //If an add and delete buttons does not exists but Add-delete toolbar is specificed
473 //Autogenerate these buttons
474 oAddDeleteToolbar = $(properties.sAddDeleteToolbarSelector);
475 if (oAddDeleteToolbar.length != 0) {
476 if (oAddNewRowButton == null && properties.sAddNewRowButtonId != ""
477 && oAddNewRowForm != null) {
478 oAddDeleteToolbar.append("<button id='" + properties.sAddNewRowButtonId + "' class='add_row'>Add</button>");
479 oAddNewRowButton = $("#" + properties.sAddNewRowButtonId);
480 oAddNewRowButton.click(function () { oAddNewRowForm.dialog('open'); });
481 }
482 if (oDeleteRowButton == null && properties.sDeleteRowButtonId != "") {
483 oAddDeleteToolbar.append("<button id='" + properties.sDeleteRowButtonId + "' class='delete_row'>Delete</button>");
484 oDeleteRowButton = $("#" + properties.sDeleteRowButtonId);
485 oDeleteRowButton.click(_fnOnRowDelete);
486 }
487 }
488
489 //If delete button exists disable it until some row is selected
490 if (oDeleteRowButton != null)
491 oDeleteRowButton.attr("disabled", "true");
492
493 //Set selected class on row that is clicked
494 $("tbody", oTable).click(function (event) {
495 if ($(event.target.parentNode).hasClass(properties.sSelectedRowClass)) {
496 $(event.target.parentNode).removeClass(properties.sSelectedRowClass);
497 if (oDeleteRowButton != null)
498 oDeleteRowButton.attr("disabled", "true");
499 } else {
500 $(oTable.fnSettings().aoData).each(function () {
501 $(this.nTr).removeClass(properties.sSelectedRowClass);
502 });
503 $(event.target.parentNode).addClass(properties.sSelectedRowClass);
504 if (oDeleteRowButton != null)
505 oDeleteRowButton.removeAttr("disabled");
506 }
507 });
508
509 });
510 };
511})(jQuery);
Note: See TracBrowser for help on using the repository browser.