Bug 24518: Fix IE11 partner filtering
[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     // Record the list of all options
327     var ill_partner_options = $('#partners > option');
328     $('#partner_filter').keyup(function() {
329         var needle = $('#partner_filter').val();
330         var regex = new RegExp(needle, 'i');
331         var filtered = [];
332         ill_partner_options.each(function() {
333             if (
334                 needle.length == 0 ||
335                 $(this).is(':selected') ||
336                 $(this).text().match(regex)
337             ) {
338                 filtered.push($(this));
339             }
340         });
341         $('#partners').empty().append(filtered);
342     });
343
344     // Display the modal containing request supplier metadata
345     $('#ill-request-display-metadata').on('click', function(e) {
346         e.preventDefault();
347         $('#dataPreview').modal({show:true});
348     });
349
350     // Allow us to chain Datatable render helpers together, so we
351     // can use our custom functions and render.text(), which
352     // provides us with data sanitization
353     $.fn.dataTable.render.multi = function(renderArray) {
354         return function(d, type, row, meta) {
355             for(var r = 0; r < renderArray.length; r++) {
356                 var toCall = renderArray[r].hasOwnProperty('display') ?
357                     renderArray[r].display :
358                     renderArray[r];
359                 d = toCall(d, type, row, meta);
360             }
361             return d;
362         }
363     }
364
365     // Get our data from the API and process it prior to passing
366     // it to datatables
367     var filterParam = prefilters ? '&' + prefilters : '';
368     // Only fire the request if we're on an appropriate page
369     if (
370         (
371             // ILL list requests page
372             window.location.href.match(/ill\/ill-requests\.pl/) &&
373             window.location.search.length == 0
374         ) ||
375         // Patron profile page
376         window.location.href.match(/members\/ill-requests\.pl/)
377     ) {
378         var ajax = $.ajax(
379             '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
380             + filterParam
381         ).done(function() {
382             var data = JSON.parse(ajax.responseText);
383             // Make a copy, we'll be removing columns next and need
384             // to be able to refer to data that has been removed
385             var dataCopy = $.extend(true, [], data);
386             // Expand columns that need it and create an array
387             // of all column names
388             $.each(dataCopy, function(k, row) {
389                 expandExpand(row);
390             });
391
392             // Assemble an array of column definitions for passing
393             // to datatables
394             var colData = [];
395             columns_settings.forEach(function(thisCol) {
396                 var colName = thisCol.columnname;
397                 // Create the base column object
398                 var colObj = $.extend({}, thisCol);
399                 colObj.name = colName;
400                 colObj.className = colName;
401                 colObj.defaultContent = '';
402
403                 // We may need to process the data going in this
404                 // column, so do it if necessary
405                 if (
406                     specialCols.hasOwnProperty(colName) &&
407                     specialCols[colName].hasOwnProperty('func')
408                 ) {
409                     var renderArray = [
410                         specialCols[colName].func
411                     ];
412                     if (!specialCols[colName].skipSanitize) {
413                         renderArray.push(
414                             $.fn.dataTable.render.text()
415                         );
416                     }
417
418                     colObj.render = $.fn.dataTable.render.multi(
419                         renderArray
420                     );
421                 } else {
422                     colObj.data = colName;
423                     colObj.render = $.fn.dataTable.render.text()
424                 }
425                 // Make sure properties that aren't present in the API
426                 // response are populated with null to avoid Datatables
427                 // choking on their absence
428                 dataCopy.forEach(function(thisData) {
429                     if (!thisData.hasOwnProperty(colName)) {
430                         thisData[colName] = null;
431                     }
432                 });
433                 colData.push(colObj);
434             });
435
436             // Initialise the datatable
437             table = KohaTable("ill-requests", {
438                 'aoColumnDefs': [
439                     { // Last column shouldn't be sortable or searchable
440                         'aTargets': [ 'actions' ],
441                         'bSortable': false,
442                         'bSearchable': false
443                     },
444                     { // When sorting 'placed', we want to use the
445                         // unformatted column
446                         'aTargets': [ 'placed_formatted'],
447                         'iDataSort': 14
448                     },
449                     { // When sorting 'updated', we want to use the
450                         // unformatted column
451                         'aTargets': [ 'updated_formatted'],
452                         'iDataSort': 16
453                     },
454                     { // When sorting 'completed', we want to use the
455                         // unformatted column
456                         'aTargets': [ 'completed_formatted'],
457                         'iDataSort': 19
458                     }
459                 ],
460                 'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
461                 'processing': true, // Display a message when manipulating
462                 'sPaginationType': "full_numbers", // Pagination display
463                 'deferRender': true, // Improve performance on big datasets
464                 'data': dataCopy,
465                 'columns': colData,
466                 'originalData': data, // Enable render functions to access
467                                         // our original data
468                 'initComplete': function() {
469
470                     // Prepare any filter elements that need it
471                     for (var el in filterable) {
472                         if (filterable.hasOwnProperty(el)) {
473                             if (filterable[el].hasOwnProperty('prep')) {
474                                 filterable[el].prep(dataCopy, data);
475                             }
476                             if (filterable[el].hasOwnProperty('listener')) {
477                                 filterable[el].listener();
478                             }
479                         }
480                     }
481
482                 }
483             }, columns_settings);
484
485             // Custom date range filtering
486             $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
487                 var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
488                 var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
489                 var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
490                 var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
491                 var rowPlaced = data[14] ? new Date(data[14]) : null;
492                 var rowModified = data[16] ? new Date(data[16]) : null;
493                 var placedPassed = true;
494                 var modifiedPassed = true;
495                 if (placedStart && rowPlaced && rowPlaced < placedStart) {
496                     placedPassed = false
497                 };
498                 if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
499                     placedPassed = false;
500                 }
501                 if (modifiedStart && rowModified && rowModified < modifiedStart) {
502                     modifiedPassed = false
503                 };
504                 if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
505                     modifiedPassed = false;
506                 }
507
508                 return placedPassed && modifiedPassed;
509
510             });
511
512         });
513     } //END if window.location.search.length == 0
514
515     var clearSearch = function() {
516         table.api().search('').columns().search('');
517         activeFilters = {};
518         for (var filter in filterable) {
519             if (
520                 filterable.hasOwnProperty(filter) &&
521                 filterable[filter].hasOwnProperty('clear')
522             ) {
523                 filterable[filter].clear();
524             }
525         }
526         table.api().draw();
527     };
528
529     // Apply any search filters, or clear any previous
530     // ones
531     $('#illfilter_form').submit(function(event) {
532         event.preventDefault();
533         table.api().search('').columns().search('');
534         for (var active in activeFilters) {
535             if (activeFilters.hasOwnProperty(active)) {
536                 activeFilters[active]();
537             }
538         }
539         table.api().draw();
540     });
541
542     // Clear all filters
543     $('#clear_search').click(function() {
544         clearSearch();
545     });
546
547 });