Bug 28983: Use Flatpickr on various pages
[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                             row.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         keyword: {
120             listener: function () {
121                 var me = 'keyword';
122                 $('#illfilter_keyword').change(function () {
123                     var val = $('#illfilter_keyword').val();
124                     if (val && val.length > 0) {
125                         activeFilters[me] = function () {
126                             table.api().search(val);
127                         }
128                     } else {
129                         if (activeFilters.hasOwnProperty(me)) {
130                             delete activeFilters[me];
131                         }
132                     }
133                 });
134             },
135             clear: function () {
136                 $('#illfilter_keyword').val('');
137             }
138         },
139         dateModified: {
140             clear: function() {
141                 $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
142             }
143         },
144         datePlaced: {
145             clear: function() {
146                 $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
147             }
148         }
149     }; //END Filterable columns
150
151     // Expand any fields we're expanding
152     var expandExpand = function(row) {
153         expand.forEach(function(thisExpand) {
154             if (row.hasOwnProperty(thisExpand)) {
155                 if (!expanded.hasOwnProperty(thisExpand)) {
156                     expanded[thisExpand] = [];
157                 }
158                 var expandObj = row[thisExpand];
159                 Object.keys(expandObj).forEach(
160                     function(thisExpandCol) {
161                         var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
162                         // Keep a list of fields that have been expanded
163                         // so we can create toggle links for them
164                         if (expanded[thisExpand].indexOf(expColName) == -1) {
165                             expanded[thisExpand].push(expColName);
166                         }
167                         expandObj[expColName] =
168                             expandObj[thisExpandCol];
169                         delete expandObj[thisExpandCol];
170                     }
171                 );
172                 $.extend(true, row, expandObj);
173                 delete row[thisExpand];
174             }
175         });
176     };
177     //END Expand
178
179     // Strip the expand prefix if it exists, we do this for display
180     var stripPrefix = function(value) {
181         expand.forEach(function(thisExpand) {
182             var regex = new RegExp(thisExpand + '_', 'g');
183             value = value.replace(regex, '');
184         });
185         return value;
186     };
187
188     // Our 'render' function for borrowerlink
189     var createPatronLink = function(data, type, row) {
190         var patronLink = '<a title="' + ill_borrower_details + '" ' +
191             'href="/cgi-bin/koha/members/moremember.pl?' +
192             'borrowernumber='+row.borrowernumber+'">';
193         if ( row.patron_firstname ) {
194             patronLink = patronLink + row.patron_firstname + ' ';
195         }
196         patronLink = patronLink + row.patron_surname +
197             ' (' + row.patron_cardnumber + ')' + '</a>';
198         return patronLink;
199     };
200
201     // Our 'render' function for biblio_id
202     var createBiblioLink = function(data, type, row) {
203         return (row.biblio_id) ?
204             '<a title="' + ill_biblio_details + '" ' +
205             'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
206             row.biblio_id + '">' +
207             row.biblio_id +
208             '</a>' : '';
209     };
210
211     // Our 'render' function for title
212     var createTitle = function(data, type, row) {
213         return (
214             row.hasOwnProperty('metadata_container_title') &&
215             row.metadata_container_title
216         ) ? row.metadata_container_title : row.metadata_title;
217     };
218
219     // Render function for request ID
220     var createRequestId = function(data, type, row) {
221         return row.id_prefix + row.illrequest_id;
222     };
223
224     // Render function for type
225     var createType = function(data, type, row) {
226         if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
227             if (row.hasOwnProperty('medium') && row.medium) {
228                 row.metadata_Type = row.medium;
229             } else {
230                 row.metadata_Type = null;
231             }
232         }
233         return row.metadata_Type;
234     };
235
236     // Render function for request status
237     var createStatus = function(data, type, row, meta) {
238         if (row.status_alias) {
239             return row.status_alias.lib
240                 ? row.status_alias.lib
241                 : row.status_alias.authorised_value;
242         } else {
243             var status_name = row.capabilities[row.status].name;
244             return getStatusName(status_name, row);
245         }
246     };
247
248     var getStatusName = function(origName, row) {
249         switch( origName ) {
250             case "New request":
251                 return ill_statuses.new;
252             case "Requested":
253                 return ill_statuses.req;
254             case "Requested from partners":
255                 var statStr = ill_statuses.genreq;
256                 if (
257                     row.hasOwnProperty('requested_partners') &&
258                     row.requested_partners &&
259                     row.requested_partners.length > 0
260                 ) {
261                     statStr += ' (' + row.requested_partners + ')';
262                 }
263                 return statStr;
264             case "Request reverted":
265                 return ill_statuses.rev;
266             case "Queued request":
267                 return ill_statuses.que;
268             case "Cancellation requested":
269                 return ill_statuses.canc;
270             case "Completed":
271                 return ill_statuses.comp;
272             case "Delete request":
273                 return ill_statuses.del;
274             default:
275                 return origName;
276         }
277     };
278
279     // Render function for creating a row's action link
280     var createActionLink = function(data, type, row) {
281         return '<a class="btn btn-default btn-sm" ' +
282             'href="/cgi-bin/koha/ill/ill-requests.pl?' +
283             'method=illview&amp;illrequest_id=' +
284             row.illrequest_id +
285             '">' + ill_manage + '</a>';
286     };
287
288     // Columns that require special treatment
289     var specialCols = {
290         action: {
291             func: createActionLink,
292             skipSanitize: true
293         },
294         illrequest_id: {
295             func: createRequestId
296         },
297         status: {
298             func: createStatus
299         },
300         biblio_id: {
301             name: ill_columns.biblio_id,
302             func: createBiblioLink,
303             skipSanitize: true
304         },
305         metadata_title: {
306             func: createTitle
307         },
308         metadata_Type: {
309             func: createType
310         },
311         updated: {
312             name: ill_columns.updated
313         },
314         patron: {
315             skipSanitize: true,
316             func: createPatronLink
317         }
318     };
319
320     // Display the modal containing request supplier metadata
321     $('#ill-request-display-log').on('click', function(e) {
322         e.preventDefault();
323         $('#requestLog').modal({show:true});
324     });
325
326     // Toggle request attributes in Illview
327     $('#toggle_requestattributes').on('click', function(e) {
328         e.preventDefault();
329         $('#requestattributes').toggleClass('content_hidden');
330     });
331
332     // Toggle new comment form in Illview
333     $('#toggle_addcomment').on('click', function(e) {
334         e.preventDefault();
335         $('#addcomment').toggleClass('content_hidden');
336     });
337
338     var illfilter_dateplaced_start = $("#illfilter_dateplaced_start").flatpickr({
339         onClose: function( selectedDates, dateText, instance) {
340             validate_date( selectedDates, instance );
341             illfilter_dateplaced_end.set('minDate', selectedDates[0]);
342         }
343     });
344
345     var illfilter_dateplaced_end = $("#illfilter_dateplaced_end").flatpickr({
346         onClose: function( selectedDates, dateText, instance) {
347             validate_date( selectedDates, instance );
348         },
349     });
350
351     var illfilter_datemodified_start = $("#illfilter_datemodified_start").flatpickr({
352         onClose: function( selectedDates, dateText, instance) {
353             validate_date( selectedDates, instance );
354             illfilter_datemodified_end.set('minDate', selectedDates[0]);
355         }
356     });
357
358     var illfilter_datemodified_end = $("#illfilter_datemodified_end").flatpickr({
359         onClose: function( selectedDates, dateText, instance) {
360             validate_date( selectedDates, instance );
361         },
362     });
363
364     // Filter partner list
365     // Record the list of all options
366     var ill_partner_options = $('#partners > option');
367     $('#partner_filter').keyup(function() {
368         var needle = $('#partner_filter').val();
369         var regex = new RegExp(needle, 'i');
370         var filtered = [];
371         ill_partner_options.each(function() {
372             if (
373                 needle.length == 0 ||
374                 $(this).is(':selected') ||
375                 $(this).text().match(regex)
376             ) {
377                 filtered.push($(this));
378             }
379         });
380         $('#partners').empty().append(filtered);
381     });
382
383     // Display the modal containing request supplier metadata
384     $('#ill-request-display-metadata').on('click', function(e) {
385         e.preventDefault();
386         $('#dataPreview').modal({show:true});
387     });
388
389     // Allow us to chain Datatable render helpers together, so we
390     // can use our custom functions and render.text(), which
391     // provides us with data sanitization
392     $.fn.dataTable.render.multi = function(renderArray) {
393         return function(d, type, row, meta) {
394             for(var r = 0; r < renderArray.length; r++) {
395                 var toCall = renderArray[r].hasOwnProperty('display') ?
396                     renderArray[r].display :
397                     renderArray[r];
398                 d = toCall(d, type, row, meta);
399             }
400             return d;
401         }
402     }
403
404     // Get our data from the API and process it prior to passing
405     // it to datatables
406     var filterParam = prefilters ? '&' + prefilters : '';
407     // Only fire the request if we're on an appropriate page
408     if (
409         (
410             // ILL list requests page
411             window.location.href.match(/ill\/ill-requests\.pl/) &&
412             window.location.search.length == 0
413         ) ||
414         // Patron profile page
415         window.location.href.match(/members\/ill-requests\.pl/)
416     ) {
417         var ajax = $.ajax(
418             '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
419             + filterParam
420         ).done(function() {
421             var data = JSON.parse(ajax.responseText);
422             // Make a copy, we'll be removing columns next and need
423             // to be able to refer to data that has been removed
424             var dataCopy = $.extend(true, [], data);
425             // Expand columns that need it and create an array
426             // of all column names
427             $.each(dataCopy, function(k, row) {
428                 expandExpand(row);
429             });
430
431             // Assemble an array of column definitions for passing
432             // to datatables
433             var colData = [];
434             columns_settings.forEach(function(thisCol) {
435                 var colName = thisCol.columnname;
436                 // Create the base column object
437                 var colObj = $.extend({}, thisCol);
438                 colObj.name = colName;
439                 colObj.className = colName;
440                 colObj.defaultContent = '';
441
442                 // We may need to process the data going in this
443                 // column, so do it if necessary
444                 if (
445                     specialCols.hasOwnProperty(colName) &&
446                     specialCols[colName].hasOwnProperty('func')
447                 ) {
448                     var renderArray = [
449                         specialCols[colName].func
450                     ];
451                     if (!specialCols[colName].skipSanitize) {
452                         renderArray.push(
453                             $.fn.dataTable.render.text()
454                         );
455                     }
456
457                     colObj.render = $.fn.dataTable.render.multi(
458                         renderArray
459                     );
460                 } else {
461                     colObj.data = colName;
462                     colObj.render = $.fn.dataTable.render.text()
463                 }
464                 // Make sure properties that aren't present in the API
465                 // response are populated with null to avoid Datatables
466                 // choking on their absence
467                 dataCopy.forEach(function(thisData) {
468                     if (!thisData.hasOwnProperty(colName)) {
469                         thisData[colName] = null;
470                     }
471                 });
472                 colData.push(colObj);
473             });
474
475             // Initialise the datatable
476             table = KohaTable("ill-requests", {
477                 'aoColumnDefs': [
478                     { // Last column shouldn't be sortable or searchable
479                         'aTargets': [ 'actions' ],
480                         'bSortable': false,
481                         'bSearchable': false
482                     },
483                     { // When sorting 'placed', we want to use the
484                         // unformatted column
485                         'aTargets': [ 'placed_formatted'],
486                         'iDataSort': 14
487                     },
488                     { // When sorting 'updated', we want to use the
489                         // unformatted column
490                         'aTargets': [ 'updated_formatted'],
491                         'iDataSort': 16
492                     },
493                     { // When sorting 'completed', we want to use the
494                         // unformatted column
495                         'aTargets': [ 'completed_formatted'],
496                         'iDataSort': 19
497                     }
498                 ],
499                 'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
500                 'processing': true, // Display a message when manipulating
501                 'sPaginationType': "full_numbers", // Pagination display
502                 'deferRender': true, // Improve performance on big datasets
503                 'data': dataCopy,
504                 "dom": '<"top pager"<"table_entries"ilp><"table_controls"B>>tr<"bottom pager"ip>',
505                 'columns': colData,
506                 'originalData': data, // Enable render functions to access
507                                         // our original data
508                 'initComplete': function() {
509
510                     // Prepare any filter elements that need it
511                     for (var el in filterable) {
512                         if (filterable.hasOwnProperty(el)) {
513                             if (filterable[el].hasOwnProperty('prep')) {
514                                 filterable[el].prep(dataCopy, data);
515                             }
516                             if (filterable[el].hasOwnProperty('listener')) {
517                                 filterable[el].listener();
518                             }
519                         }
520                     }
521
522                 }
523             }, columns_settings);
524
525             // Custom date range filtering
526             $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
527                 var placedStart = illfilter_dateplaced_start.selectedDates[0];
528                 var placedEnd = illfilter_dateplaced_end.selectedDates[0];
529                 var modifiedStart = illfilter_datemodified_start.selectedDates[0];
530                 var modifiedEnd = illfilter_datemodified_end.selectedDates[0];
531                 var rowPlaced = data[14] ? new Date(data[14]) : null;
532                 var rowModified = data[16] ? new Date(data[16]) : null;
533                 var placedPassed = true;
534                 var modifiedPassed = true;
535                 if (placedStart && rowPlaced && rowPlaced < placedStart) {
536                     placedPassed = false
537                 };
538                 if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
539                     placedPassed = false;
540                 }
541                 if (modifiedStart && rowModified && rowModified < modifiedStart) {
542                     modifiedPassed = false
543                 };
544                 if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
545                     modifiedPassed = false;
546                 }
547
548                 return placedPassed && modifiedPassed;
549
550             });
551
552         });
553     } //END if window.location.search.length == 0
554
555     var clearSearch = function() {
556         table.api().search('').columns().search('');
557         activeFilters = {};
558         for (var filter in filterable) {
559             if (
560                 filterable.hasOwnProperty(filter) &&
561                 filterable[filter].hasOwnProperty('clear')
562             ) {
563                 filterable[filter].clear();
564             }
565         }
566         table.api().draw();
567     };
568
569     // Apply any search filters, or clear any previous
570     // ones
571     $('#illfilter_form').submit(function(event) {
572         event.preventDefault();
573         table.api().search('').columns().search('');
574         for (var active in activeFilters) {
575             if (activeFilters.hasOwnProperty(active)) {
576                 activeFilters[active]();
577             }
578         }
579         table.api().draw();
580     });
581
582     // Clear all filters
583     $('#clear_search').click(function() {
584         clearSearch();
585     });
586
587 });