Bug 23529: (QA follow-up) Fix closing bracket, add label
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / ill-list-table.js
1 $(document).ready(function() {
2
3     // Illview Datatable setup
4
5     var table;
6
7     // Filters that are active
8     var activeFilters = {};
9
10     // Get any prefilters
11     var prefilters = $('table#ill-requests').data('prefilters');
12
13     // Fields we need to expand (flatten)
14     var expand = [
15         'metadata',
16         'patron',
17         'library'
18     ];
19
20     // Expanded fields
21     // This is auto populated
22     var expanded = {};
23
24     // Filterable columns
25     var filterable = {
26         status: {
27             prep: function(tableData, oData) {
28                 var uniques = {};
29                 tableData.forEach(function(row) {
30                     var resolvedName;
31                     if (row.status_alias) {
32                         resolvedName = row.status_alias.lib;
33                     } else {
34                         resolvedName = getStatusName(
35                             oData[0].capabilities[row.status].name,
36                             row
37                         );
38                     }
39                     uniques[resolvedName] = 1
40                 });
41                 Object.keys(uniques).sort().forEach(function(unique) {
42                     $('#illfilter_status').append(
43                         '<option value="' + unique  +
44                         '">' + unique +  '</option>'
45                     );
46                 });
47             },
48             listener: function() {
49                 var me = 'status';
50                 $('#illfilter_status').change(function() {
51                     var sel = $('#illfilter_status option:selected').val();
52                     if (sel && sel.length > 0) {
53                         activeFilters[me] = function() {
54                             table.api().column(13).search(sel);
55                         }
56                     } else {
57                         if (activeFilters.hasOwnProperty(me)) {
58                             delete activeFilters[me];
59                         }
60                     }
61                 });
62             },
63             clear: function() {
64                 $('#illfilter_status').val('');
65             }
66         },
67         pickupBranch: {
68             prep: function(tableData, oData) {
69                 var uniques = {};
70                 tableData.forEach(function(row) {
71                     uniques[row.library_branchname] = 1
72                 });
73                 Object.keys(uniques).sort().forEach(function(unique) {
74                     $('#illfilter_branchname').append(
75                         '<option value="' + unique  +
76                         '">' + unique +  '</option>'
77                     );
78                 });
79             },
80             listener: function() {
81                 var me = 'pickupBranch';
82                 $('#illfilter_branchname').change(function() {
83                     var sel = $('#illfilter_branchname option:selected').val();
84                     if (sel && sel.length > 0) {
85                         activeFilters[me] = function() {
86                             table.api().column(12).search(sel);
87                         }
88                     } else {
89                         if (activeFilters.hasOwnProperty(me)) {
90                             delete activeFilters[me];
91                         }
92                     }
93                 });
94             },
95             clear: function() {
96                 $('#illfilter_branchname').val('');
97             }
98         },
99         patron: {
100             listener: function() {
101                 var me = 'patron';
102                 $('#illfilter_patron').change(function() {
103                     var val = $('#illfilter_patron').val();
104                     if (val && val.length > 0) {
105                         activeFilters[me] = function() {
106                             table.api().column(10).search(val);
107                         }
108                     } else {
109                         if (activeFilters.hasOwnProperty(me)) {
110                             delete activeFilters[me];
111                         }
112                     }
113                 });
114             },
115             clear: function() {
116                 $('#illfilter_patron').val('');
117             }
118         },
119         dateModified: {
120             clear: function() {
121                 $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
122             }
123         },
124         datePlaced: {
125             clear: function() {
126                 $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
127             }
128         }
129     }; //END Filterable columns
130
131     // Expand any fields we're expanding
132     var expandExpand = function(row) {
133         expand.forEach(function(thisExpand) {
134             if (row.hasOwnProperty(thisExpand)) {
135                 if (!expanded.hasOwnProperty(thisExpand)) {
136                     expanded[thisExpand] = [];
137                 }
138                 var expandObj = row[thisExpand];
139                 Object.keys(expandObj).forEach(
140                     function(thisExpandCol) {
141                         var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
142                         // Keep a list of fields that have been expanded
143                         // so we can create toggle links for them
144                         if (expanded[thisExpand].indexOf(expColName) == -1) {
145                             expanded[thisExpand].push(expColName);
146                         }
147                         expandObj[expColName] =
148                             expandObj[thisExpandCol];
149                         delete expandObj[thisExpandCol];
150                     }
151                 );
152                 $.extend(true, row, expandObj);
153                 delete row[thisExpand];
154             }
155         });
156     };
157     //END Expand
158
159     // Strip the expand prefix if it exists, we do this for display
160     var stripPrefix = function(value) {
161         expand.forEach(function(thisExpand) {
162             var regex = new RegExp(thisExpand + '_', 'g');
163             value = value.replace(regex, '');
164         });
165         return value;
166     };
167
168     // Our 'render' function for borrowerlink
169     var createPatronLink = function(data, type, row) {
170         var patronLink = '<a title="' + ill_borrower_details + '" ' +
171             'href="/cgi-bin/koha/members/moremember.pl?' +
172             'borrowernumber='+row.borrowernumber+'">';
173         if ( row.patron_firstname ) {
174             patronLink = patronLink + row.patron_firstname + ' ';
175         }
176         patronLink = patronLink + row.patron_surname +
177             ' (' + row.patron_cardnumber + ')' + '</a>';
178         return patronLink;
179     };
180
181     // Our 'render' function for biblio_id
182     var createBiblioLink = function(data, type, row) {
183         return (row.biblio_id) ?
184             '<a title="' + ill_biblio_details + '" ' +
185             'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
186             row.biblio_id + '">' +
187             row.biblio_id +
188             '</a>' : '';
189     };
190
191     // Our 'render' function for title
192     var createTitle = function(data, type, row) {
193         return (
194             row.hasOwnProperty('metadata_container_title') &&
195             row.metadata_container_title
196         ) ? row.metadata_container_title : row.metadata_title;
197     };
198
199     // Render function for request ID
200     var createRequestId = function(data, type, row) {
201         return row.id_prefix + row.illrequest_id;
202     };
203
204     // Render function for type
205     var createType = function(data, type, row) {
206         if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
207             if (row.hasOwnProperty('medium') && row.medium) {
208                 row.metadata_Type = row.medium;
209             } else {
210                 row.metadata_Type = null;
211             }
212         }
213         return row.metadata_Type;
214     };
215
216     // Render function for request status
217     var createStatus = function(data, type, row, meta) {
218         if (row.status_alias) {
219             return row.status_alias.lib
220                 ? row.status_alias.lib
221                 : row.status_alias.authorised_value;
222         } else {
223             var origData = meta.settings.oInit.originalData;
224             if (origData.length > 0) {
225                 var status_name = meta.settings.oInit.originalData[0].capabilities[
226                     row.status
227                 ].name;
228                 return getStatusName(status_name, row);
229             } else {
230                 return '';
231             }
232         }
233     };
234
235     var getStatusName = function(origName, row) {
236         switch( origName ) {
237             case "New request":
238                 return ill_statuses.new;
239             case "Requested":
240                 return ill_statuses.req;
241             case "Requested from partners":
242                 var statStr = ill_statuses.genreq;
243                 if (
244                     row.hasOwnProperty('requested_partners') &&
245                     row.requested_partners &&
246                     row.requested_partners.length > 0
247                 ) {
248                     statStr += ' (' + row.requested_partners + ')';
249                 }
250                 return statStr;
251             case "Request reverted":
252                 return ill_statuses.rev;
253             case "Queued request":
254                 return ill_statuses.que;
255             case "Cancellation requested":
256                 return ill_statuses.canc;
257             case "Completed":
258                 return ill_statuses.comp;
259             case "Delete request":
260                 return ill_statuses.del;
261             default:
262                 return origName;
263         }
264     };
265
266     // Render function for creating a row's action link
267     var createActionLink = function(data, type, row) {
268         return '<a class="btn btn-default btn-sm" ' +
269             'href="/cgi-bin/koha/ill/ill-requests.pl?' +
270             'method=illview&amp;illrequest_id=' +
271             row.illrequest_id +
272             '">' + ill_manage + '</a>';
273     };
274
275     // Columns that require special treatment
276     var specialCols = {
277         action: {
278             func: createActionLink,
279             skipSanitize: true
280         },
281         illrequest_id: {
282             func: createRequestId
283         },
284         status: {
285             func: createStatus
286         },
287         biblio_id: {
288             name: ill_columns.biblio_id,
289             func: createBiblioLink,
290             skipSanitize: true
291         },
292         metadata_title: {
293             func: createTitle
294         },
295         metadata_Type: {
296             func: createType
297         },
298         updated: {
299             name: ill_columns.updated
300         },
301         patron: {
302             skipSanitize: true,
303             func: createPatronLink
304         }
305     };
306
307     // Display the modal containing request supplier metadata
308     $('#ill-request-display-log').on('click', function(e) {
309         e.preventDefault();
310         $('#requestLog').modal({show:true});
311     });
312
313     // Toggle request attributes in Illview
314     $('#toggle_requestattributes').on('click', function(e) {
315         e.preventDefault();
316         $('#requestattributes').toggleClass('content_hidden');
317     });
318
319     // Toggle new comment form in Illview
320     $('#toggle_addcomment').on('click', function(e) {
321         e.preventDefault();
322         $('#addcomment').toggleClass('content_hidden');
323     });
324
325     // Filter partner list
326     $('#partner_filter').keyup(function() {
327         var needle = $('#partner_filter').val();
328         $('#partners > option').each(function() {
329             var regex = new RegExp(needle, 'i');
330             if (
331                 needle.length == 0 ||
332                 $(this).is(':selected') ||
333                 $(this).text().match(regex)
334             ) {
335                 $(this).show();
336             } else {
337                 $(this).hide();
338             }
339         });
340     });
341
342     // Display the modal containing request supplier metadata
343     $('#ill-request-display-metadata').on('click', function(e) {
344         e.preventDefault();
345         $('#dataPreview').modal({show:true});
346     });
347
348     // Allow us to chain Datatable render helpers together, so we
349     // can use our custom functions and render.text(), which
350     // provides us with data sanitization
351     $.fn.dataTable.render.multi = function(renderArray) {
352         return function(d, type, row, meta) {
353             for(var r = 0; r < renderArray.length; r++) {
354                 var toCall = renderArray[r].hasOwnProperty('display') ?
355                     renderArray[r].display :
356                     renderArray[r];
357                 d = toCall(d, type, row, meta);
358             }
359             return d;
360         }
361     }
362
363     // Get our data from the API and process it prior to passing
364     // it to datatables
365     var filterParam = prefilters ? '&' + prefilters : '';
366     // Only fire the request if we're on the ILL list page
367     if (window.location.search.length == 0) {
368         var ajax = $.ajax(
369             '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
370             + filterParam
371         ).done(function() {
372             var data = JSON.parse(ajax.responseText);
373             // Make a copy, we'll be removing columns next and need
374             // to be able to refer to data that has been removed
375             var dataCopy = $.extend(true, [], data);
376             // Expand columns that need it and create an array
377             // of all column names
378             $.each(dataCopy, function(k, row) {
379                 expandExpand(row);
380             });
381
382             // Assemble an array of column definitions for passing
383             // to datatables
384             var colData = [];
385             columns_settings.forEach(function(thisCol) {
386                 var colName = thisCol.columnname;
387                 // Create the base column object
388                 var colObj = $.extend({}, thisCol);
389                 colObj.name = colName;
390                 colObj.className = colName;
391                 colObj.defaultContent = '';
392
393                 // We may need to process the data going in this
394                 // column, so do it if necessary
395                 if (
396                     specialCols.hasOwnProperty(colName) &&
397                     specialCols[colName].hasOwnProperty('func')
398                 ) {
399                     var renderArray = [
400                         specialCols[colName].func
401                     ];
402                     if (!specialCols[colName].skipSanitize) {
403                         renderArray.push(
404                             $.fn.dataTable.render.text()
405                         );
406                     }
407
408                     colObj.render = $.fn.dataTable.render.multi(
409                         renderArray
410                     );
411                 } else {
412                     colObj.data = colName;
413                     colObj.render = $.fn.dataTable.render.text()
414                 }
415                 // Make sure properties that aren't present in the API
416                 // response are populated with null to avoid Datatables
417                 // choking on their absence
418                 dataCopy.forEach(function(thisData) {
419                     if (!thisData.hasOwnProperty(colName)) {
420                         thisData[colName] = null;
421                     }
422                 });
423                 colData.push(colObj);
424             });
425
426             // Initialise the datatable
427             table = KohaTable("ill-requests", {
428                 'aoColumnDefs': [
429                     { // Last column shouldn't be sortable or searchable
430                         'aTargets': [ 'actions' ],
431                         'bSortable': false,
432                         'bSearchable': false
433                     },
434                     { // When sorting 'placed', we want to use the
435                         // unformatted column
436                         'aTargets': [ 'placed_formatted'],
437                         'iDataSort': 14
438                     },
439                     { // When sorting 'updated', we want to use the
440                         // unformatted column
441                         'aTargets': [ 'updated_formatted'],
442                         'iDataSort': 16
443                     },
444                     { // When sorting 'completed', we want to use the
445                         // unformatted column
446                         'aTargets': [ 'completed_formatted'],
447                         'iDataSort': 19
448                     }
449                 ],
450                 'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
451                 'processing': true, // Display a message when manipulating
452                 'sPaginationType': "full_numbers", // Pagination display
453                 'deferRender': true, // Improve performance on big datasets
454                 'data': dataCopy,
455                 'columns': colData,
456                 'originalData': data, // Enable render functions to access
457                                         // our original data
458                 'initComplete': function() {
459
460                     // Prepare any filter elements that need it
461                     for (var el in filterable) {
462                         if (filterable.hasOwnProperty(el)) {
463                             if (filterable[el].hasOwnProperty('prep')) {
464                                 filterable[el].prep(dataCopy, data);
465                             }
466                             if (filterable[el].hasOwnProperty('listener')) {
467                                 filterable[el].listener();
468                             }
469                         }
470                     }
471
472                 }
473             }, columns_settings);
474
475             // Custom date range filtering
476             $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
477                 var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
478                 var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
479                 var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
480                 var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
481                 var rowPlaced = data[14] ? new Date(data[14]) : null;
482                 var rowModified = data[16] ? new Date(data[16]) : null;
483                 var placedPassed = true;
484                 var modifiedPassed = true;
485                 if (placedStart && rowPlaced && rowPlaced < placedStart) {
486                     placedPassed = false
487                 };
488                 if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
489                     placedPassed = false;
490                 }
491                 if (modifiedStart && rowModified && rowModified < modifiedStart) {
492                     modifiedPassed = false
493                 };
494                 if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
495                     modifiedPassed = false;
496                 }
497
498                 return placedPassed && modifiedPassed;
499
500             });
501
502         });
503     } //END if window.location.search.length == 0
504
505     var clearSearch = function() {
506         table.api().search('').columns().search('');
507         activeFilters = {};
508         for (var filter in filterable) {
509             if (
510                 filterable.hasOwnProperty(filter) &&
511                 filterable[filter].hasOwnProperty('clear')
512             ) {
513                 filterable[filter].clear();
514             }
515         }
516         table.api().draw();
517     };
518
519     // Apply any search filters, or clear any previous
520     // ones
521     $('#illfilter_form').submit(function(event) {
522         event.preventDefault();
523         table.api().search('').columns().search('');
524         for (var active in activeFilters) {
525             if (activeFilters.hasOwnProperty(active)) {
526                 activeFilters[active]();
527             }
528         }
529         table.api().draw();
530     });
531
532     // Clear all filters
533     $('#clear_search').click(function() {
534         clearSearch();
535     });
536
537 });