source: public/vendors/dataTable/SearchPanes-1.0.1/js/dataTables.searchPanes.js@ 7304c7f

develop
Last change on this file since 7304c7f was 7304c7f, checked in by beratkjufliju <kufliju@…>, 3 years ago

added user authentication, create & forgot password methods and blades

  • Property mode set to 100644
File size: 96.4 KB
Line 
1/*! SearchPanes 1.0.1
2 * 2019-2020 SpryMedia Ltd - datatables.net/license
3 */
4(function () {
5 'use strict';
6
7 var DataTable = $.fn.dataTable;
8 var SearchPane = /** @class */ (function () {
9 /**
10 * Creates the panes, sets up the search function
11 * @param paneSettings The settings for the searchPanes
12 * @param opts The options for the default features
13 * @param idx the index of the column for this pane
14 * @returns {object} the pane that has been created, including the table and the index of the pane
15 */
16 function SearchPane(paneSettings, opts, idx, layout, panesContainer, panes) {
17 var _this = this;
18 if (panes === void 0) { panes = null; }
19 // Check that the required version of DataTables is included
20 if (!DataTable || !DataTable.versionCheck || !DataTable.versionCheck('1.10.0')) {
21 throw new Error('SearchPane requires DataTables 1.10 or newer');
22 }
23 // Check that Select is included
24 if (!DataTable.select) {
25 throw new Error('SearchPane requires Select');
26 }
27 var table = new DataTable.Api(paneSettings);
28 this.classes = $.extend(true, {}, SearchPane.classes);
29 // Get options from user
30 this.c = $.extend(true, {}, SearchPane.defaults, opts);
31 this.customPaneSettings = panes;
32 this.s = {
33 cascadeRegen: false,
34 clearing: false,
35 colOpts: [],
36 deselect: false,
37 displayed: false,
38 dt: table,
39 dtPane: undefined,
40 filteringActive: false,
41 index: idx,
42 indexes: [],
43 lastSelect: false,
44 redraw: false,
45 rowData: {
46 arrayFilter: [],
47 arrayOriginal: [],
48 arrayTotals: [],
49 bins: {},
50 binsOriginal: {},
51 binsTotal: {},
52 filterMap: new Map()
53 },
54 searchFunction: undefined,
55 selectPresent: false,
56 updating: false
57 };
58 var rowLength = table.columns().eq(0).toArray().length;
59 this.colExists = this.s.index < rowLength;
60 // Add extra elements to DOM object including clear and hide buttons
61 this.c.layout = layout;
62 var layVal = parseInt(layout.split('-')[1], 10);
63 this.dom = {
64 buttonGroup: $('<div/>').addClass(this.classes.buttonGroup),
65 clear: $('<button type="button">&#215;</button>')
66 .addClass(this.classes.dull)
67 .addClass(this.classes.paneButton)
68 .addClass(this.classes.clearButton),
69 container: $('<div/>').addClass(this.classes.container).addClass(this.classes.layout +
70 (layVal < 7 ? layout : layout.split('-')[0] + '-6')),
71 countButton: $('<button type="button"></button>')
72 .addClass(this.classes.paneButton)
73 .addClass(this.classes.countButton),
74 dtP: $('<table><thead><tr><th>' +
75 (this.colExists
76 ? $(table.column(this.colExists ? this.s.index : 0).header()).text()
77 : this.customPaneSettings.header || 'Custom Pane') + '</th><th/></tr></thead></table>'),
78 lower: $('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
79 nameButton: $('<button type="button"></button>').addClass(this.classes.paneButton).addClass(this.classes.nameButton),
80 searchBox: $('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
81 searchButton: $('<button type = "button" class="' + this.classes.searchIcon + '"></button>')
82 .addClass(this.classes.paneButton),
83 searchCont: $('<div/>').addClass(this.classes.searchCont),
84 searchLabelCont: $('<div/>').addClass(this.classes.searchLabelCont),
85 topRow: $('<div/>').addClass(this.classes.topRow),
86 upper: $('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
87 };
88 this.s.displayed = false;
89 table = this.s.dt;
90 this.selections = [];
91 this.s.colOpts = this.colExists ? this._getOptions() : this._getBonusOptions();
92 var colOpts = this.s.colOpts;
93 var clear = $('<button type="button">X</button>').addClass(this.classes.paneButton);
94 $(clear).text(table.i18n('searchPanes.clearPane', 'X'));
95 this.dom.container.addClass(colOpts.className);
96 this.dom.container.addClass((this.customPaneSettings !== null && this.customPaneSettings.className !== undefined)
97 ? this.customPaneSettings.className
98 : '');
99 $(panesContainer).append(this.dom.container);
100 var tableNode = table.table(0).node();
101 // Custom search function for table
102 this.s.searchFunction = function (settings, searchData, dataIndex, origData) {
103 // If no data has been selected then show all
104 if (_this.selections.length === 0) {
105 return true;
106 }
107 if (settings.nTable !== tableNode) {
108 return true;
109 }
110 var filter = '';
111 if (_this.colExists) {
112 // Get the current filtered data
113 filter = searchData[_this.s.index];
114 if (colOpts.orthogonal.filter !== 'filter') {
115 // get the filter value from the map
116 filter = _this.s.rowData.filterMap.get(dataIndex);
117 if (filter instanceof $.fn.dataTable.Api) {
118 filter = filter.toArray();
119 }
120 }
121 }
122 return _this._search(filter, dataIndex);
123 };
124 $.fn.dataTable.ext.search.push(this.s.searchFunction);
125 // If the clear button for this pane is clicked clear the selections
126 if (this.c.clear) {
127 $(clear).on('click', function () {
128 var searches = _this.dom.container.find(_this.classes.search);
129 searches.each(function () {
130 $(this).val('');
131 $(this).trigger('input');
132 });
133 _this.clearPane();
134 });
135 }
136 // Sometimes the top row of the panes containing the search box and ordering buttons appears
137 // weird if the width of the panes is lower than expected, this fixes the design.
138 // Equally this may occur when the table is resized.
139 table.on('draw.dtsp', function () {
140 _this._adjustTopRow();
141 });
142 $(window).on('resize.dtsp', DataTable.util.throttle(function () {
143 _this._adjustTopRow();
144 }));
145 // When column-reorder is present and the columns are moved, it is necessary to
146 // reassign all of the panes indexes to the new index of the column.
147 table.on('column-reorder.dtsp', function (e, settings, details) {
148 _this.s.index = details.mapping[_this.s.index];
149 });
150 return this;
151 }
152 /**
153 * In the case of a rebuild there is potential for new data to have been included or removed
154 * so all of the rowData must be reset as a precaution.
155 */
156 SearchPane.prototype.clearData = function () {
157 this.s.rowData = {
158 arrayFilter: [],
159 arrayOriginal: [],
160 arrayTotals: [],
161 bins: {},
162 binsOriginal: {},
163 binsTotal: {},
164 filterMap: new Map()
165 };
166 };
167 /**
168 * Clear the selections in the pane
169 */
170 SearchPane.prototype.clearPane = function () {
171 // Deselect all rows which are selected and update the table and filter count.
172 this.s.dtPane.rows({ selected: true }).deselect();
173 this.updateTable();
174 return this;
175 };
176 /**
177 * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
178 */
179 SearchPane.prototype.destroy = function () {
180 $(this.s.dtPane).off('.dtsp');
181 $(this.s.dt).off('.dtsp');
182 $(this.dom.nameButton).off('.dtsp');
183 $(this.dom.countButton).off('.dtsp');
184 $(this.dom.clear).off('.dtsp');
185 $(this.dom.searchButton).off('.dtsp');
186 $(this.dom.container).remove();
187 var searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
188 while (searchIdx !== -1) {
189 $.fn.dataTable.ext.search.splice(searchIdx, 1);
190 searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
191 }
192 // If the datatables have been defined for the panes then also destroy these
193 if (this.s.dtPane !== undefined) {
194 this.s.dtPane.destroy();
195 }
196 };
197 /**
198 * Updates the number of filters that have been applied in the title
199 */
200 SearchPane.prototype.getPaneCount = function () {
201 return this.s.dtPane !== undefined ?
202 this.s.dtPane.rows({ selected: true }).data().toArray().length :
203 0;
204 };
205 /**
206 * Rebuilds the panes from the start having deleted the old ones
207 */
208 SearchPane.prototype.rebuildPane = function () {
209 this.clearData();
210 // When rebuilding strip all of the HTML Elements out of the container and start from scratch
211 if (this.s.dtPane !== undefined) {
212 this.s.dtPane.clear().destroy();
213 }
214 this.dom.container.removeClass(this.classes.hidden);
215 this.s.displayed = false;
216 this._buildPane();
217 return this;
218 };
219 /**
220 * removes the pane from the page and sets the displayed property to false.
221 */
222 SearchPane.prototype.removePane = function () {
223 this.s.displayed = false;
224 $(this.dom.container).hide();
225 };
226 /**
227 * Sets the cascadeRegen property of the pane. Accessible from above because as SearchPanes.ts deals with the rebuilds.
228 * @param val the boolean value that the cascadeRegen property is to be set to
229 */
230 SearchPane.prototype.setCascadeRegen = function (val) {
231 this.s.cascadeRegen = val;
232 };
233 /**
234 * This function allows the clearing property to be assigned. This is used when implementing cascadePane.
235 * In setting this to true for the clearing of the panes selection on the deselects it forces the pane to
236 * repopulate from the entire dataset not just the displayed values.
237 * @param val the boolean value which the clearing property is to be assigned
238 */
239 SearchPane.prototype.setClear = function (val) {
240 this.s.clearing = val;
241 };
242 /**
243 * Updates the values of all of the panes
244 * @param draw whether this has been triggered by a draw event or not
245 */
246 SearchPane.prototype.updatePane = function (draw) {
247 if (draw === void 0) { draw = false; }
248 this.s.updating = true;
249 this._updateCommon(draw);
250 this.s.updating = false;
251 };
252 /**
253 * Updates the panes if one of the options to do so has been set to true
254 * rather than the filtered message when using viewTotal.
255 */
256 SearchPane.prototype.updateTable = function () {
257 var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
258 this.selections = selectedRows;
259 this._searchExtras();
260 // If either of the options that effect how the panes are displayed are selected then update the Panes
261 if (this.c.cascadePanes || this.c.viewTotal) {
262 this.updatePane();
263 }
264 };
265 /**
266 * Takes in potentially undetected rows and adds them to the array if they are not yet featured
267 * @param filter the filter value of the potential row
268 * @param display the display value of the potential row
269 * @param sort the sort value of the potential row
270 * @param type the type value of the potential row
271 * @param arrayFilter the array to be populated
272 * @param bins the bins to be populated
273 */
274 SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
275 // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
276 if (Array.isArray(filter) || filter instanceof DataTable.Api) {
277 // Convert to an array so that we can work with it
278 if (filter instanceof DataTable.Api) {
279 filter = filter.toArray();
280 display = display.toArray();
281 }
282 if (filter.length === display.length) {
283 for (var i = 0; i < filter.length; i++) {
284 // If we haven't seen this row before add it
285 if (!bins[filter[i]]) {
286 bins[filter[i]] = 1;
287 arrayFilter.push({
288 display: display[i],
289 filter: filter[i],
290 sort: sort,
291 type: type
292 });
293 }
294 // Otherwise just increment the count
295 else {
296 bins[filter[i]]++;
297 }
298 }
299 return;
300 }
301 else {
302 throw new Error('display and filter not the same length');
303 }
304 }
305 // If the values were affected by othogonal data and are not an array then check if it is already present
306 else if (typeof this.s.colOpts.orthogonal === 'string') {
307 if (!bins[filter]) {
308 bins[filter] = 1;
309 arrayFilter.push({
310 display: display,
311 filter: filter,
312 sort: sort,
313 type: type
314 });
315 }
316 else {
317 bins[filter]++;
318 return;
319 }
320 }
321 // Otherwise we must just be adding an option
322 else {
323 arrayFilter.push({
324 display: display,
325 filter: filter,
326 sort: sort,
327 type: type
328 });
329 }
330 };
331 /**
332 * Adds a row to the panes table
333 * @param display the value to be displayed to the user
334 * @param filter the value to be filtered on when searchpanes is implemented
335 * @param shown the number of rows in the table that are currently visible matching this criteria
336 * @param total the total number of rows in the table that match this criteria
337 * @param sort the value to be sorted in the pane table
338 * @param type the value of which the type is to be derived from
339 */
340 SearchPane.prototype._addRow = function (display, filter, shown, total, sort, type) {
341 var index;
342 for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
343 var entry = _a[_i];
344 if (entry.filter === filter) {
345 index = entry.index;
346 }
347 }
348 if (index === undefined) {
349 index = this.s.indexes.length;
350 this.s.indexes.push({ filter: filter, index: index });
351 }
352 return this.s.dtPane.row.add({
353 display: display !== '' ? display : this.c.emptyMessage,
354 filter: filter,
355 index: index,
356 shown: shown,
357 sort: sort !== '' ? sort : this.c.emptyMessage,
358 total: total,
359 type: type
360 });
361 };
362 /**
363 * Adjusts the layout of the top row when the screen is resized
364 */
365 SearchPane.prototype._adjustTopRow = function () {
366 var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer);
367 var subRow1 = this.dom.container.find('.dtsp-subRow1');
368 var subRow2 = this.dom.container.find('.dtsp-subRow2');
369 var topRow = this.dom.container.find('.' + this.classes.topRow);
370 // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
371 // Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
372 if (($(subContainers[0]).width() < 252 || $(topRow[0]).width() < 252) && $(subContainers[0]).width() !== 0) {
373 $(subContainers[0]).addClass(this.classes.narrow);
374 $(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
375 $(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
376 }
377 else {
378 $(subContainers[0]).removeClass(this.classes.narrow);
379 $(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
380 $(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
381 }
382 };
383 /**
384 * Method to construct the actual pane.
385 */
386 SearchPane.prototype._buildPane = function () {
387 var _this = this;
388 // Aliases
389 this.selections = [];
390 var table = this.s.dt;
391 var column = table.column(this.colExists ? this.s.index : 0);
392 var colOpts = this.s.colOpts;
393 var rowData = this.s.rowData;
394 // Other Variables
395 var countMessage = table.i18n('searchPanes.count', '{total}');
396 var filteredMessage = table.i18n('searchPanes.countFiltered', '{shown} ({total})');
397 var loadedFilter = table.state.loaded();
398 // If it is not a custom pane in place
399 if (this.colExists) {
400 var idx = -1;
401 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
402 for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
403 if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
404 idx = i;
405 break;
406 }
407 }
408 }
409 // Perform checks that do not require populate pane to run
410 if ((colOpts.show === false
411 || (colOpts.show !== undefined && colOpts.show !== true)) &&
412 idx === -1) {
413 this.dom.container.addClass(this.classes.hidden);
414 this.s.displayed = false;
415 return false;
416 }
417 else if (colOpts.show === true || idx !== -1) {
418 this.s.displayed = true;
419 }
420 // Only run populatePane if the data has not been collected yet
421 if (rowData.arrayFilter.length === 0) {
422 this._populatePane();
423 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
424 // If the index is not found then no data has been added to the state for this pane,
425 // which will only occur if it has previously failed to meet the criteria to be
426 // displayed, therefore we can just hide it again here
427 if (idx !== -1) {
428 rowData.binsOriginal = loadedFilter.searchPanes.panes[idx].bins;
429 rowData.arrayOriginal = loadedFilter.searchPanes.panes[idx].arrayFilter;
430 }
431 else {
432 this.dom.container.addClass(this.classes.hidden);
433 this.s.displayed = false;
434 return;
435 }
436 }
437 else {
438 rowData.arrayOriginal = rowData.arrayFilter;
439 rowData.binsOriginal = rowData.bins;
440 }
441 }
442 var binLength = Object.keys(rowData.binsOriginal).length;
443 var uniqueRatio = this._uniqueRatio(binLength, table.rows()[0].length);
444 // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry for that pane
445 if (this.s.displayed === false && ((colOpts.show === undefined && colOpts.threshold === null ?
446 uniqueRatio > this.c.threshold :
447 uniqueRatio > colOpts.threshold)
448 || (colOpts.show !== true && binLength <= 1))) {
449 this.dom.container.addClass(this.classes.hidden);
450 this.s.displayed = false;
451 return;
452 }
453 // If the option viewTotal is true then find
454 // the total count for the whole table to display alongside the displayed count
455 if (this.c.viewTotal && rowData.arrayTotals.length === 0) {
456 this._detailsPane();
457 }
458 else {
459 rowData.binsTotal = rowData.bins;
460 }
461 this.dom.container.addClass(this.classes.show);
462 this.s.displayed = true;
463 }
464 else {
465 this.s.displayed = true;
466 }
467 // If the variance is accceptable then display the search pane
468 this._displayPane();
469 // Here, when the state is loaded if the data object on the original table is empty,
470 // then a state.clear() must have occurred, so delete all of the panes tables state objects too.
471 this.dom.dtP.on('stateLoadParams.dt', function (e, settings, data) {
472 if ($.isEmptyObject(table.state.loaded())) {
473 $.each(data, function (index, value) {
474 delete data[index];
475 });
476 }
477 });
478 // Declare the datatable for the pane
479 var errMode = $.fn.dataTable.ext.errMode;
480 $.fn.dataTable.ext.errMode = 'none';
481 var haveScroller = DataTable.Scroller;
482 this.s.dtPane = $(this.dom.dtP).DataTable($.extend(true, {
483 dom: 't',
484 columnDefs: [
485 {
486 className: 'dtsp-nameColumn',
487 data: 'display',
488 render: function (data, type, row) {
489 if (type === 'sort') {
490 return row.sort;
491 }
492 else if (type === 'type') {
493 return row.type;
494 }
495 var message;
496 _this.s.filteringActive && _this.c.viewTotal
497 ? message = filteredMessage.replace(/{total}/, row.total)
498 : message = countMessage.replace(/{total}/, row.total);
499 message = message.replace(/{shown}/, row.shown);
500 while (message.indexOf('{total}') !== -1) {
501 message = message.replace(/{total}/, row.total);
502 }
503 while (message.indexOf('{shown}') !== -1) {
504 message = message.replace(/{shown}/, row.shown);
505 }
506 // We are displaying the count in the same columne as the name of the search option.
507 // This is so that there is not need to call columns.adjust(), which in turn speeds up the code
508 var displayMessage = '';
509 var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
510 if (_this.c.hideCount || colOpts.hideCount) {
511 pill = '';
512 }
513 if (!_this.c.dataLength) {
514 displayMessage = '<span class="' + _this.classes.name + '">' + data + '</span>' + pill;
515 }
516 else if (data.length > _this.c.dataLength) {
517 displayMessage = '<span class="' + _this.classes.name + '">'
518 + data.substr(0, _this.c.dataLength) + '...'
519 + '</span>'
520 + pill;
521 }
522 else {
523 displayMessage = '<span class="' + _this.classes.name + '">' + data + '</span>' + pill;
524 }
525 return displayMessage;
526 },
527 targets: 0,
528 // Accessing the private datatables property to set type based on the original table.
529 // This is null if not defined by the user, meaning that automatic type detection would take place
530 type: table.settings()[0].aoColumns[this.s.index] !== undefined ?
531 table.settings()[0].aoColumns[this.s.index]._sManualType :
532 null
533 },
534 {
535 className: 'dtsp-countColumn ' + this.classes.badgePill,
536 data: 'total',
537 targets: 1,
538 visible: false
539 }
540 ],
541 deferRender: true,
542 info: false,
543 paging: haveScroller ? true : false,
544 scrollY: '200px',
545 scroller: haveScroller ? true : false,
546 select: true,
547 stateSave: table.settings()[0].oFeatures.bStateSave ? true : false
548 }, this.c.dtOpts, colOpts !== undefined ? colOpts.dtOpts : {}, (this.customPaneSettings !== null && this.customPaneSettings.dtOpts !== undefined)
549 ? this.customPaneSettings.dtOpts
550 : {}));
551 $(this.dom.dtP).addClass(this.classes.table);
552 // This is hacky but necessary for when datatables is generating the column titles automatically
553 $(this.dom.searchBox).attr('placeholder', colOpts.header !== undefined
554 ? colOpts.header
555 : this.colExists
556 ? table.settings()[0].aoColumns[this.s.index].sTitle
557 : this.customPaneSettings.header || 'Custom Pane');
558 // As the pane table is not in the document yet we must initialise select ourselves
559 $.fn.dataTable.select.init(this.s.dtPane);
560 $.fn.dataTable.ext.errMode = errMode;
561 // If it is not a custom pane
562 if (this.colExists) {
563 // On initialisation, do we need to set a filtering value from a
564 // saved state or init option?
565 var search = column.search();
566 search = search ? search.substr(1, search.length - 2).split('|') : [];
567 // Count the number of empty cells
568 var count_1 = 0;
569 rowData.arrayFilter.forEach(function (element) {
570 if (element.filter === '') {
571 count_1++;
572 }
573 });
574 // Add all of the search options to the pane
575 for (var i = 0, ien = rowData.arrayFilter.length; i < ien; i++) {
576 if (rowData.arrayFilter[i] && (rowData.bins[rowData.arrayFilter[i].filter] !== undefined || !this.c.cascadePanes)) {
577 var row = this._addRow(rowData.arrayFilter[i].display, rowData.arrayFilter[i].filter, rowData.bins[rowData.arrayFilter[i].filter], rowData.binsTotal[rowData.arrayFilter[i].filter], rowData.arrayFilter[i].sort, rowData.arrayFilter[i].type);
578 if (colOpts.preSelect !== undefined && colOpts.preSelect.indexOf(rowData.arrayFilter[i].filter) !== -1) {
579 row.select();
580 }
581 }
582 else {
583 this._addRow(this.c.emptyMessage, count_1, count_1, this.c.emptyMessage, this.c.emptyMessage, this.c.emptyMessage);
584 }
585 }
586 }
587 // If there are custom options set or it is a custom pane then get them
588 if (colOpts.options !== undefined ||
589 (this.customPaneSettings !== null && this.customPaneSettings.options !== undefined)) {
590 this._getComparisonRows();
591 }
592 DataTable.select.init(this.s.dtPane);
593 // Display the pane
594 this.s.dtPane.draw();
595 // When an item is selected on the pane, add these to the array which holds selected items.
596 // Custom search will perform.
597 this.s.dtPane.on('select.dtsp', function () {
598 clearTimeout(t0);
599 $(_this.dom.clear).removeClass(_this.classes.dull);
600 _this.s.selectPresent = true;
601 if (!_this.s.updating) {
602 _this._makeSelection();
603 }
604 _this.s.selectPresent = false;
605 });
606 // When saving the state store all of the selected rows for preselection next time around
607 this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
608 // If the data being passed in is empty then a state clear must have occured so clear the panes state as well
609 if ($.isEmptyObject(data)) {
610 _this.s.dtPane.state.clear();
611 return;
612 }
613 var selected = [];
614 var searchTerm;
615 var order;
616 var bins;
617 var arrayFilter;
618 // Get all of the data needed for the state save from the pane
619 if (_this.s.dtPane !== undefined) {
620 selected = _this.s.dtPane.rows({ selected: true }).data().map(function (item) { return item.filter.toString(); }).toArray();
621 searchTerm = $(_this.dom.searchBox).val();
622 order = _this.s.dtPane.order();
623 bins = rowData.binsOriginal;
624 arrayFilter = rowData.arrayOriginal;
625 }
626 if (data.searchPanes === undefined) {
627 data.searchPanes = {};
628 }
629 if (data.searchPanes.panes === undefined) {
630 data.searchPanes.panes = [];
631 }
632 // Add the panes data to the state object
633 data.searchPanes.panes.push({
634 arrayFilter: arrayFilter,
635 bins: bins,
636 id: _this.s.index,
637 order: order,
638 searchTerm: searchTerm,
639 selected: selected
640 });
641 });
642 // Reload the selection, searchbox entry and ordering from the previous state
643 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
644 if (!this.c.cascadePanes) {
645 this._reloadSelect(loadedFilter);
646 }
647 for (var _i = 0, _a = loadedFilter.searchPanes.panes; _i < _a.length; _i++) {
648 var pane = _a[_i];
649 if (pane.id === this.s.index) {
650 $(this.dom.searchBox).val(pane.searchTerm);
651 this.s.dt.order(pane.order);
652 }
653 }
654 }
655 this.s.dtPane.on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
656 originalEvent.stopPropagation();
657 });
658 // When the button to order by the name of the options is clicked then
659 // change the ordering to whatever it isn't currently
660 $(this.dom.nameButton).on('click.dtsp', function () {
661 var currentOrder = _this.s.dtPane.order()[0][1];
662 _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
663 });
664 // When the button to order by the number of entries in the column is clicked then
665 // change the ordering to whatever it isn't currently
666 $(this.dom.countButton).on('click.dtsp', function () {
667 var currentOrder = _this.s.dtPane.order()[0][1];
668 _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
669 });
670 // When the clear button is clicked reset the pane
671 $(this.dom.clear).on('click.dtsp', function () {
672 var searches = _this.dom.container.find('.' + _this.classes.search);
673 searches.each(function () {
674 // set the value of the search box to be an empty string and then search on that, effectively reseting
675 $(this).val('');
676 $(this).trigger('input');
677 });
678 _this.clearPane();
679 });
680 // When the search button is clicked then draw focus to the search box
681 $(this.dom.searchButton).on('click.dtsp', function () {
682 $(_this.dom.searchBox).focus();
683 });
684 // When a character is inputted into the searchbox search the pane for matching values.
685 // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
686 $(this.dom.searchBox).on('input.dtsp', function () {
687 _this.s.dtPane.search($(_this.dom.searchBox).val()).draw();
688 _this.s.dt.state.save();
689 });
690 // Declare timeout Variable
691 var t0;
692 // When an item is deselected on the pane, re add the currently selected items to the array
693 // which holds selected items. Custom search will be performed.
694 this.s.dtPane.on('deselect.dtsp', function () {
695 t0 = setTimeout(function () {
696 _this.s.deselect = true;
697 if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
698 $(_this.dom.clear).addClass(_this.classes.dull);
699 }
700 _this._makeSelection();
701 _this.s.deselect = false;
702 _this.s.dt.state.save();
703 }, 50);
704 });
705 // Make sure to save the state once the pane has been built
706 this.s.dt.state.save();
707 return true;
708 };
709 /**
710 * Update the array which holds the display and filter values for the table
711 */
712 SearchPane.prototype._detailsPane = function () {
713 var _this = this;
714 var table = this.s.dt;
715 this.s.rowData.arrayTotals = [];
716 this.s.rowData.binsTotal = {};
717 var settings = this.s.dt.settings()[0];
718 table.rows().every(function (rowIdx) {
719 _this._populatePaneArray(rowIdx, _this.s.rowData.arrayTotals, settings, _this.s.rowData.binsTotal);
720 });
721 };
722 /**
723 * Appends all of the HTML elements to their relevant parent Elements
724 */
725 SearchPane.prototype._displayPane = function () {
726 var container = this.dom.container;
727 var colOpts = this.s.colOpts;
728 var layVal = parseInt(this.c.layout.split('-')[1], 10);
729 // Empty everything to start again
730 $(this.dom.topRow).empty();
731 $(this.dom.dtP).empty();
732 $(this.dom.topRow).addClass(this.classes.topRow);
733 // If there are more than 3 columns defined then make there be a smaller gap between the panes
734 if (layVal > 3) {
735 $(this.dom.container).addClass(this.classes.smallGap);
736 }
737 $(this.dom.topRow).addClass(this.classes.subRowsContainer);
738 $(this.dom.upper).appendTo(this.dom.topRow);
739 $(this.dom.lower).appendTo(this.dom.topRow);
740 $(this.dom.searchCont).appendTo(this.dom.upper);
741 $(this.dom.buttonGroup).appendTo(this.dom.lower);
742 // If no selections have been made in the pane then disable the clear button
743 if (this.c.dtOpts.searching === false ||
744 (colOpts.dtOpts !== undefined &&
745 colOpts.dtOpts.searching === false) ||
746 (!this.c.controls || !colOpts.controls) ||
747 (this.customPaneSettings !== null &&
748 this.customPaneSettings.dtOpts !== undefined &&
749 this.customPaneSettings.dtOpts.searching !== undefined &&
750 !this.customPaneSettings.dtOpts.searching)) {
751 $(this.dom.searchBox).attr('disabled', 'disabled')
752 .removeClass(this.classes.paneInputButton)
753 .addClass(this.classes.disabledButton);
754 }
755 $(this.dom.searchBox).appendTo(this.dom.searchCont);
756 // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
757 this._searchContSetup();
758 // If the clear button is allowed to show then display it
759 if (this.c.clear && this.c.controls && colOpts.controls) {
760 $(this.dom.clear).appendTo(this.dom.buttonGroup);
761 }
762 if (this.c.orderable && colOpts.orderable && this.c.controls && colOpts.controls) {
763 $(this.dom.nameButton).appendTo(this.dom.buttonGroup);
764 }
765 // If the count column is hidden then don't display the ordering button for it
766 if (!this.c.hideCount &&
767 !colOpts.hideCount &&
768 this.c.orderable &&
769 colOpts.orderable &&
770 this.c.controls &&
771 colOpts.controls) {
772 $(this.dom.countButton).appendTo(this.dom.buttonGroup);
773 }
774 $(this.dom.topRow).prependTo(this.dom.container);
775 $(container).append(this.dom.dtP);
776 $(container).show();
777 };
778 /**
779 * Find the unique filter values in an array
780 * @param data empty array to populate with data which has not yet been found
781 * @param arrayFilter the array of all of the display and filter values for the table
782 */
783 SearchPane.prototype._findUnique = function (data, arrayFilter) {
784 var prev = [];
785 for (var _i = 0, arrayFilter_1 = arrayFilter; _i < arrayFilter_1.length; _i++) {
786 var filterEl = arrayFilter_1[_i];
787 // If the data has not already been processed then add it to the unique array and the previously processed array.
788 if (prev.indexOf(filterEl.filter) === -1) {
789 data.push({
790 display: filterEl.display,
791 filter: filterEl.filter,
792 sort: filterEl.sort,
793 type: filterEl.type
794 });
795 prev.push(filterEl.filter);
796 }
797 }
798 };
799 /**
800 * Gets the options for the row for the customPanes
801 * @returns {object} The options for the row extended to include the options from the user.
802 */
803 SearchPane.prototype._getBonusOptions = function () {
804 // We need to reset the thresholds as if they have a value in colOpts then that value will be used
805 var defaultMutator = {
806 orthogonal: {
807 threshold: null
808 },
809 threshold: null
810 };
811 return $.extend(true, {}, SearchPane.defaults, defaultMutator, this.c !== undefined ? this.c : {});
812 };
813 /**
814 * Adds the custom options to the pane
815 * @returns {Array} Returns the array of rows which have been added to the pane
816 */
817 SearchPane.prototype._getComparisonRows = function () {
818 var colOpts = this.s.colOpts;
819 // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
820 var options = colOpts.options !== undefined
821 ? colOpts.options
822 : this.customPaneSettings !== null && this.customPaneSettings.options !== undefined
823 ? this.customPaneSettings.options
824 : undefined;
825 if (options === undefined) {
826 return;
827 }
828 var tableVals = this.s.dt.rows({ search: 'applied' }).data().toArray();
829 var appRows = this.s.dt.rows({ search: 'applied' });
830 var tableValsTotal = this.s.dt.rows().data().toArray();
831 var allRows = this.s.dt.rows();
832 var rows = [];
833 // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
834 this.s.dtPane.clear();
835 for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
836 var comp = options_1[_i];
837 // Initialise the object which is to be placed in the row
838 var insert = comp.label !== '' ? comp.label : this.c.emptyMessage;
839 var comparisonObj = {
840 display: insert,
841 filter: typeof comp.value === 'function' ? comp.value : [],
842 shown: 0,
843 sort: insert,
844 total: 0,
845 type: insert
846 };
847 // If a custom function is in place
848 if (typeof comp.value === 'function') {
849 // Count the number of times the function evaluates to true for the data currently being displayed
850 for (var tVal = 0; tVal < tableVals.length; tVal++) {
851 if (comp.value.call(this.s.dt, tableVals[tVal], appRows[0][tVal])) {
852 comparisonObj.shown++;
853 }
854 }
855 // Count the number of times the function evaluates to true for the original data in the Table
856 for (var i = 0; i < tableValsTotal.length; i++) {
857 if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
858 comparisonObj.total++;
859 }
860 }
861 // Update the comparisonObj
862 if (typeof comparisonObj.filter !== 'function') {
863 comparisonObj.filter.push(comp.filter);
864 }
865 }
866 // If cascadePanes is not active or if it is and the comparisonObj should be shown then add it to the pane
867 if (!this.c.cascadePanes || (this.c.cascadePanes && comparisonObj.shown !== 0)) {
868 rows.push(this._addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.shown, comparisonObj.total, comparisonObj.sort, comparisonObj.type));
869 }
870 }
871 return rows;
872 };
873 /**
874 * Gets the options for the row for the customPanes
875 * @returns {object} The options for the row extended to include the options from the user.
876 */
877 SearchPane.prototype._getOptions = function () {
878 var table = this.s.dt;
879 // We need to reset the thresholds as if they have a value in colOpts then that value will be used
880 var defaultMutator = {
881 orthogonal: {
882 threshold: null
883 },
884 threshold: null
885 };
886 return $.extend(true, {}, SearchPane.defaults, defaultMutator, table.settings()[0].aoColumns[this.s.index].searchPanes);
887 };
888 /**
889 * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
890 * @param select Denotes whether a selection has been made or not
891 */
892 SearchPane.prototype._makeSelection = function () {
893 this.updateTable();
894 this.s.updating = true;
895 this.s.dt.draw();
896 this.s.updating = false;
897 };
898 /**
899 * Fill the array with the values that are currently being displayed in the table
900 */
901 SearchPane.prototype._populatePane = function () {
902 var table = this.s.dt;
903 this.s.rowData.arrayFilter = [];
904 this.s.rowData.bins = {};
905 var settings = this.s.dt.settings()[0];
906 // If cascadePanes or viewTotal are active it is necessary to get the data which is currently
907 // being displayed for their functionality.
908 var indexArray = (this.c.cascadePanes || this.c.viewTotal) && !this.s.clearing ?
909 table.rows({ search: 'applied' }).indexes() :
910 table.rows().indexes();
911 for (var _i = 0, indexArray_1 = indexArray; _i < indexArray_1.length; _i++) {
912 var index = indexArray_1[_i];
913 this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
914 }
915 };
916 /**
917 * Populates an array with all of the data for the table
918 * @param rowIdx The current row index to be compared
919 * @param arrayFilter The array that is to be populated with row Details
920 * @param bins The bins object that is to be populated with the row counts
921 */
922 SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
923 if (bins === void 0) { bins = this.s.rowData.bins; }
924 var colOpts = this.s.colOpts;
925 // Retrieve the rendered data from the cell using the fnGetCellData function
926 // rather than the cell().render API method for optimisation
927 if (typeof colOpts.orthogonal === 'string') {
928 var rendered = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal);
929 this.s.rowData.filterMap.set(rowIdx, rendered);
930 this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
931 }
932 else {
933 var filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.search);
934 this.s.rowData.filterMap.set(rowIdx, filter);
935 if (!bins[filter]) {
936 bins[filter] = 1;
937 this._addOption(filter, settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.display), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.sort), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.type), arrayFilter, bins);
938 }
939 else {
940 bins[filter]++;
941 return;
942 }
943 }
944 };
945 /**
946 * Reloads all of the previous selects into the panes
947 * @param loadedFilter The loaded filters from a previous state
948 */
949 SearchPane.prototype._reloadSelect = function (loadedFilter) {
950 // If the state was not saved don't selected any
951 if (loadedFilter === undefined) {
952 return;
953 }
954 var idx;
955 // For each pane, check that the loadedFilter list exists and is not null,
956 // find the id of each search item and set it to be selected.
957 for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
958 if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
959 idx = i;
960 break;
961 }
962 }
963 if (idx !== undefined) {
964 var table = this.s.dtPane;
965 var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
966 item.filter.toString() :
967 null; }).toArray();
968 for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
969 var filter = _a[_i];
970 var id = -1;
971 if (filter !== null) {
972 id = rows.indexOf(filter.toString());
973 }
974 if (id > -1) {
975 table.row(id).select();
976 this.s.dt.state.save();
977 }
978 }
979 }
980 };
981 /**
982 * This method decides whether a row should contribute to the pane or not
983 * @param filter the value that the row is to be filtered on
984 * @param dataIndex the row index
985 */
986 SearchPane.prototype._search = function (filter, dataIndex) {
987 var colOpts = this.s.colOpts;
988 var table = this.s.dt;
989 // For each item selected in the pane, check if it is available in the cell
990 for (var _i = 0, _a = this.selections; _i < _a.length; _i++) {
991 var colSelect = _a[_i];
992 // if the filter is an array then is the column present in it
993 if (Array.isArray(filter)) {
994 if (filter.indexOf(colSelect.filter) !== -1) {
995 return true;
996 }
997 }
998 // if the filter is a function then does it meet the criteria of that function or not
999 else if (typeof colSelect.filter === 'function') {
1000 if (colSelect.filter.call(table, table.row(dataIndex).data(), dataIndex)) {
1001 if (!this.s.redraw) {
1002 this.updatePane();
1003 }
1004 if (colOpts.combiner === 'or') {
1005 return true;
1006 }
1007 }
1008 // If the combiner is an "and" then we need to check against all possible selections
1009 // so if it fails here then the and is not met and return false
1010 else if (colOpts.combiner === 'and') {
1011 return false;
1012 }
1013 }
1014 // otherwise if the two filter values are equal then return true
1015 else if (filter === colSelect.filter) {
1016 return true;
1017 }
1018 }
1019 // If the combiner is an and then we need to check against all possible selections
1020 // so return true here if so because it would have returned false earlier if it had failed
1021 if (colOpts.combiner === 'and') {
1022 return true;
1023 }
1024 // Otherwise it hasn't matched with anything by this point so it must be false
1025 else {
1026 return false;
1027 }
1028 };
1029 /**
1030 * Creates the contents of the searchCont div
1031 *
1032 * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
1033 */
1034 SearchPane.prototype._searchContSetup = function () {
1035 if (this.c.controls && this.s.colOpts.controls) {
1036 $(this.dom.searchButton).appendTo(this.dom.searchLabelCont);
1037 }
1038 if (!(this.c.dtOpts.searching === false ||
1039 this.s.colOpts.dtOpts.searching === false ||
1040 (this.customPaneSettings !== null &&
1041 this.customPaneSettings.dtOpts !== undefined &&
1042 this.customPaneSettings.dtOpts.searching !== undefined &&
1043 !this.customPaneSettings.dtOpts.searching))) {
1044 $(this.dom.searchLabelCont).appendTo(this.dom.searchCont);
1045 }
1046 };
1047 /**
1048 * Adds outline to the pane when a selection has been made
1049 */
1050 SearchPane.prototype._searchExtras = function () {
1051 var updating = this.s.updating;
1052 this.s.updating = true;
1053 var filters = this.s.dtPane.rows({ selected: true }).data().pluck('filter').toArray();
1054 var nullIndex = filters.indexOf(this.c.emptyMessage);
1055 var container = $(this.s.dtPane.table().container());
1056 // If null index is found then search for empty cells as a filter.
1057 if (nullIndex > -1) {
1058 filters[nullIndex] = '';
1059 }
1060 // If a filter has been applied then outline the respective pane, remove it when it no longer is.
1061 if (filters.length > 0) {
1062 container.addClass(this.classes.selected);
1063 }
1064 else if (filters.length === 0) {
1065 container.removeClass(this.classes.selected);
1066 }
1067 this.s.updating = updating;
1068 };
1069 /**
1070 * Finds the ratio of the number of different options in the table to the number of rows
1071 * @param bins the number of different options in the table
1072 * @param rowCount the total number of rows in the table
1073 * @returns {number} returns the ratio
1074 */
1075 SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
1076 if (rowCount > 0) {
1077 return bins / rowCount;
1078 }
1079 else {
1080 return 1;
1081 }
1082 };
1083 /**
1084 * updates the options within the pane
1085 * @param draw a flag to define whether this has been called due to a draw event or not
1086 */
1087 SearchPane.prototype._updateCommon = function (draw) {
1088 if (draw === void 0) { draw = false; }
1089 // Update the panes if doing a deselect. if doing a select then
1090 // update all of the panes except for the one causing the change
1091 if (this.s.dtPane !== undefined &&
1092 ((!this.s.filteringActive || this.c.cascadePanes) || draw === true) &&
1093 (this.c.cascadePanes !== true || this.s.selectPresent !== true) && !this.s.lastSelect) {
1094 var colOpts = this.s.colOpts;
1095 var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
1096 var scrollTop = $(this.s.dtPane.table().node()).parent()[0].scrollTop;
1097 var rowData = this.s.rowData;
1098 // Clear the pane in preparation for adding the updated search options
1099 this.s.dtPane.clear();
1100 // If it is not a custom pane
1101 if (this.colExists) {
1102 // Only run populatePane if the data has not been collected yet
1103 if (rowData.arrayFilter.length === 0) {
1104 this._populatePane();
1105 }
1106 // If cascadePanes is active and the table has returned to its default state then
1107 // there is a need to update certain parts ofthe rowData.
1108 else if (this.c.cascadePanes
1109 && this.s.dt.rows().data().toArray().length === this.s.dt.rows({ search: 'applied' }).data().toArray().length) {
1110 rowData.arrayFilter = rowData.arrayOriginal;
1111 rowData.bins = rowData.binsOriginal;
1112 }
1113 // Otherwise if viewTotal or cascadePanes is active then the data from the table must be read.
1114 else if (this.c.viewTotal || this.c.cascadePanes) {
1115 this._populatePane();
1116 }
1117 // If the viewTotal option is selected then find the totals for the table
1118 if (this.c.viewTotal) {
1119 this._detailsPane();
1120 }
1121 else {
1122 rowData.binsTotal = rowData.bins;
1123 }
1124 if (this.c.viewTotal && !this.c.cascadePanes) {
1125 rowData.arrayFilter = rowData.arrayTotals;
1126 }
1127 var _loop_1 = function (dataP) {
1128 // If both view Total and cascadePanes have been selected and the count of the row is not 0 then add it to pane
1129 // Do this also if the viewTotal option has been selected and cascadePanes has not
1130 if (dataP && ((rowData.bins[dataP.filter] !== undefined && rowData.bins[dataP.filter] !== 0 && this_1.c.cascadePanes)
1131 || !this_1.c.cascadePanes
1132 || this_1.s.clearing)) {
1133 var row = this_1._addRow(dataP.display, dataP.filter, !this_1.c.viewTotal
1134 ? rowData.bins[dataP.filter]
1135 : rowData.bins[dataP.filter] !== undefined
1136 ? rowData.bins[dataP.filter]
1137 : 0, this_1.c.viewTotal
1138 ? String(rowData.binsTotal[dataP.filter])
1139 : rowData.bins[dataP.filter], dataP.sort, dataP.type);
1140 // Find out if the filter was selected in the previous search, if so select it and remove from array.
1141 var selectIndex = selected.findIndex(function (element) {
1142 return element.filter === dataP.filter;
1143 });
1144 if (selectIndex !== -1) {
1145 row.select();
1146 selected.splice(selectIndex, 1);
1147 }
1148 }
1149 };
1150 var this_1 = this;
1151 for (var _i = 0, _a = rowData.arrayFilter; _i < _a.length; _i++) {
1152 var dataP = _a[_i];
1153 _loop_1(dataP);
1154 }
1155 }
1156 if ((colOpts.searchPanes !== undefined && colOpts.searchPanes.options !== undefined) ||
1157 colOpts.options !== undefined ||
1158 (this.customPaneSettings !== null && this.customPaneSettings.options !== undefined)) {
1159 var rows = this._getComparisonRows();
1160 var _loop_2 = function (row) {
1161 var selectIndex = selected.findIndex(function (element) {
1162 if (element.display === row.data().display) {
1163 return true;
1164 }
1165 });
1166 if (selectIndex !== -1) {
1167 row.select();
1168 selected.splice(selectIndex, 1);
1169 }
1170 };
1171 for (var _b = 0, rows_1 = rows; _b < rows_1.length; _b++) {
1172 var row = rows_1[_b];
1173 _loop_2(row);
1174 }
1175 }
1176 // Add search options which were previously selected but whos results are no
1177 // longer present in the resulting data set.
1178 for (var _c = 0, selected_1 = selected; _c < selected_1.length; _c++) {
1179 var selectedEl = selected_1[_c];
1180 var row = this._addRow(selectedEl.display, selectedEl.filter, 0, this.c.viewTotal
1181 ? selectedEl.total
1182 : 0, selectedEl.filter, selectedEl.filter);
1183 row.select();
1184 }
1185 this.s.dtPane.draw();
1186 this.s.dtPane.table().node().parentNode.scrollTop = scrollTop;
1187 }
1188 };
1189 SearchPane.version = '1.0.1';
1190 SearchPane.classes = {
1191 buttonGroup: 'dtsp-buttonGroup',
1192 buttonSub: 'dtsp-buttonSub',
1193 clear: 'dtsp-clear',
1194 clearAll: 'dtsp-clearAll',
1195 clearButton: 'clearButton',
1196 container: 'dtsp-searchPane',
1197 countButton: 'dtsp-countButton',
1198 disabledButton: 'dtsp-disabledButton',
1199 dull: 'dtsp-dull',
1200 hidden: 'dtsp-hidden',
1201 hide: 'dtsp-hide',
1202 layout: 'dtsp-',
1203 name: 'dtsp-name',
1204 nameButton: 'dtsp-nameButton',
1205 narrow: 'dtsp-narrow',
1206 paneButton: 'dtsp-paneButton',
1207 paneInputButton: 'dtsp-paneInputButton',
1208 pill: 'dtsp-pill',
1209 search: 'dtsp-search',
1210 searchCont: 'dtsp-searchCont',
1211 searchIcon: 'dtsp-searchIcon',
1212 searchLabelCont: 'dtsp-searchButtonCont',
1213 selected: 'dtsp-selected',
1214 smallGap: 'dtsp-smallGap',
1215 subRow1: 'dtsp-subRow1',
1216 subRow2: 'dtsp-subRow2',
1217 subRowsContainer: 'dtsp-subRowsContainer',
1218 title: 'dtsp-title',
1219 topRow: 'dtsp-topRow'
1220 };
1221 // Define SearchPanes default options
1222 SearchPane.defaults = {
1223 cascadePanes: false,
1224 clear: true,
1225 combiner: 'or',
1226 controls: true,
1227 container: function (dt) {
1228 return dt.table().container();
1229 },
1230 dataLength: 30,
1231 dtOpts: {},
1232 emptyMessage: '<i>No Data</i>',
1233 hideCount: false,
1234 layout: 'columns-3',
1235 orderable: true,
1236 orthogonal: {
1237 display: 'display',
1238 hideCount: false,
1239 search: 'filter',
1240 show: undefined,
1241 sort: 'sort',
1242 threshold: 0.6,
1243 type: 'type'
1244 },
1245 preSelect: [],
1246 threshold: 0.6,
1247 viewTotal: false
1248 };
1249 return SearchPane;
1250 }());
1251
1252 var DataTable$1 = $.fn.dataTable;
1253 var SearchPanes = /** @class */ (function () {
1254 function SearchPanes(paneSettings, opts, fromInit) {
1255 var _this = this;
1256 if (fromInit === void 0) { fromInit = false; }
1257 this.regenerating = false;
1258 // Check that the required version of DataTables is included
1259 if (!DataTable$1 || !DataTable$1.versionCheck || !DataTable$1.versionCheck('1.10.0')) {
1260 throw new Error('SearchPane requires DataTables 1.10 or newer');
1261 }
1262 // Check that Select is included
1263 if (!DataTable$1.select) {
1264 throw new Error('SearchPane requires Select');
1265 }
1266 var table = new DataTable$1.Api(paneSettings);
1267 this.classes = $.extend(true, {}, SearchPanes.classes);
1268 // Get options from user
1269 this.c = $.extend(true, {}, SearchPanes.defaults, opts);
1270 // Add extra elements to DOM object including clear
1271 this.dom = {
1272 clearAll: $('<button type="button">Clear All</button>').addClass(this.classes.clearAll),
1273 container: $('<div/>').addClass(this.classes.panes).text(table.i18n('searchPanes.loadMessage', 'Loading Search Panes...')),
1274 emptyMessage: $('<div/>').addClass(this.classes.emptyMessage),
1275 options: $('<div/>').addClass(this.classes.container),
1276 panes: $('<div/>').addClass(this.classes.container),
1277 title: $('<div/>').addClass(this.classes.title),
1278 titleRow: $('<div/>').addClass(this.classes.titleRow),
1279 wrapper: $('<div/>')
1280 };
1281 this.s = {
1282 colOpts: [],
1283 dt: table,
1284 filterPane: -1,
1285 panes: [],
1286 selectionList: [],
1287 updating: false
1288 };
1289 table.settings()[0]._searchPanes = this;
1290 this.dom.clearAll.text(table.i18n('searchPanes.clearMessage', 'Clear All'));
1291 this._getState();
1292 if (this.s.dt.settings()[0]._bInitComplete || fromInit) {
1293 this._paneDeclare(table, paneSettings, opts);
1294 }
1295 else {
1296 table.on('preInit.dt', function () {
1297 _this._paneDeclare(table, paneSettings, opts);
1298 });
1299 }
1300 }
1301 /**
1302 * Clear the selections of all of the panes
1303 */
1304 SearchPanes.prototype.clearSelections = function () {
1305 // Load in all of the searchBoxes in the documents
1306 var searches = this.dom.container.find(this.classes.search);
1307 // For each searchBox set the input text to be empty and then trigger
1308 // an input on them so that they no longer filter the panes
1309 searches.each(function () {
1310 $(this).val('');
1311 $(this).trigger('input');
1312 });
1313 var returnArray = [];
1314 // For every pane, clear the selections in the pane
1315 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1316 var pane = _a[_i];
1317 if (pane.s.dtPane !== undefined) {
1318 returnArray.push(pane.clearPane());
1319 }
1320 }
1321 this.s.dt.draw();
1322 return returnArray;
1323 };
1324 /**
1325 * returns the container node for the searchPanes
1326 */
1327 SearchPanes.prototype.getNode = function () {
1328 return this.dom.container;
1329 };
1330 /**
1331 * rebuilds all of the panes
1332 */
1333 SearchPanes.prototype.rebuild = function (targetIdx) {
1334 if (targetIdx === void 0) { targetIdx = false; }
1335 $(this.dom.emptyMessage).remove();
1336 // As a rebuild from scratch is required, empty the searchpanes container.
1337 var returnArray = [];
1338 this.clearSelections();
1339 // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
1340 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1341 var pane = _a[_i];
1342 if (targetIdx !== false && pane.s.index !== targetIdx) {
1343 continue;
1344 }
1345 pane.clearData();
1346 returnArray.push(pane.rebuildPane());
1347 }
1348 // Attach panes, clear buttons, and title bar to the document
1349 this._updateFilterCount();
1350 this._attachPaneContainer();
1351 // If a single pane has been rebuilt then return only that pane
1352 if (returnArray.length === 1) {
1353 return returnArray[0];
1354 }
1355 // Otherwise return all of the panes that have been rebuilt
1356 else {
1357 return returnArray;
1358 }
1359 };
1360 /**
1361 * Redraws all of the panes
1362 */
1363 SearchPanes.prototype.redrawPanes = function () {
1364 var table = this.s.dt;
1365 // Only do this if the redraw isn't being triggered by the panes updating themselves
1366 if (!this.s.updating) {
1367 var filterActive = true;
1368 var filterPane = this.s.filterPane;
1369 // If the number of rows currently visible is equal to the number of rows in the table
1370 // then there can't be any filtering taking place
1371 if (table.rows({ search: 'applied' }).data().toArray().length === table.rows().data().toArray().length) {
1372 filterActive = false;
1373 }
1374 // Otherwise if viewTotal is active then it is necessary to determine which panes a select is present in.
1375 // If there is only one pane with a selection present then it should not show the filtered message as
1376 // more selections may be made in that pane.
1377 else if (this.c.viewTotal) {
1378 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1379 var pane = _a[_i];
1380 if (pane.s.dtPane !== undefined) {
1381 var selectLength = pane.s.dtPane.rows({ selected: true }).data().toArray().length;
1382 // If filterPane === -1 then a pane with a selection has not been found yet, so set filterPane to that panes index
1383 if (selectLength > 0 && filterPane === -1) {
1384 filterPane = pane.s.index;
1385 }
1386 // Then if another pane is found with a selection then set filterPane to null to
1387 // show that multiple panes have selections present
1388 else if (selectLength > 0) {
1389 filterPane = null;
1390 }
1391 }
1392 }
1393 }
1394 var deselectIdx = void 0;
1395 var newSelectionList = [];
1396 // Don't run this if it is due to the panes regenerating
1397 if (!this.regenerating) {
1398 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
1399 var pane = _c[_b];
1400 // Identify the pane where a selection or deselection has been made and add it to the list.
1401 if (pane.s.selectPresent) {
1402 this.s.selectionList.push({ index: pane.s.index, rows: pane.s.dtPane.rows({ selected: true }).data().toArray(), protect: false });
1403 table.state.save();
1404 break;
1405 }
1406 else if (pane.s.deselect) {
1407 deselectIdx = pane.s.index;
1408 var selectedData = pane.s.dtPane.rows({ selected: true }).data().toArray();
1409 if (selectedData.length > 0) {
1410 this.s.selectionList.push({ index: pane.s.index, rows: selectedData, protect: true });
1411 }
1412 }
1413 }
1414 if (this.s.selectionList.length > 0) {
1415 var last = this.s.selectionList[this.s.selectionList.length - 1].index;
1416 for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
1417 var pane = _e[_d];
1418 pane.s.lastSelect = (pane.s.index === last && this.s.selectionList.length === 1);
1419 }
1420 }
1421 // Remove selections from the list from the pane where a deselect has taken place
1422 for (var i = 0; i < this.s.selectionList.length; i++) {
1423 if (this.s.selectionList[i].index !== deselectIdx || this.s.selectionList[i].protect === true) {
1424 var further = false;
1425 // Find out if this selection is the last one in the list for that pane
1426 for (var j = i + 1; j < this.s.selectionList.length; j++) {
1427 if (this.s.selectionList[j].index === this.s.selectionList[i].index) {
1428 further = true;
1429 }
1430 }
1431 // If there are no selections for this pane in the list then just push this one
1432 if (!further) {
1433 newSelectionList.push(this.s.selectionList[i]);
1434 this.s.selectionList[i].protect = false;
1435 }
1436 }
1437 }
1438 // Update all of the panes to reflect the current state of the filters
1439 for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
1440 var pane = _g[_f];
1441 if (pane.s.dtPane !== undefined) {
1442 var tempFilter = true;
1443 pane.s.filteringActive = true;
1444 if ((filterPane !== -1 && filterPane !== null && filterPane === pane.s.index) || filterActive === false) {
1445 tempFilter = false;
1446 pane.s.filteringActive = false;
1447 }
1448 pane.updatePane(!tempFilter ? false : filterActive);
1449 }
1450 }
1451 // Update the label that shows how many filters are in place
1452 this._updateFilterCount();
1453 // If the length of the selections are different then some of them have been removed and a deselect has occured
1454 if (newSelectionList.length > 0 && newSelectionList.length < this.s.selectionList.length) {
1455 this._cascadeRegen(newSelectionList);
1456 var last = newSelectionList[newSelectionList.length - 1].index;
1457 for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
1458 var pane = _j[_h];
1459 pane.s.lastSelect = (pane.s.index === last && this.s.selectionList.length === 1);
1460 }
1461 }
1462 else if (newSelectionList.length > 0) {
1463 // Update all of the other panes as you would just making a normal selection
1464 for (var _k = 0, _l = this.s.panes; _k < _l.length; _k++) {
1465 var paneUpdate = _l[_k];
1466 if (paneUpdate.s.dtPane !== undefined) {
1467 var tempFilter = true;
1468 paneUpdate.s.filteringActive = true;
1469 if ((filterPane !== -1 && filterPane !== null && filterPane === paneUpdate.s.index) || filterActive === false) {
1470 tempFilter = false;
1471 paneUpdate.s.filteringActive = false;
1472 }
1473 paneUpdate.updatePane(!tempFilter ? tempFilter : filterActive);
1474 }
1475 }
1476 }
1477 }
1478 else {
1479 for (var _m = 0, _o = this.s.panes; _m < _o.length; _m++) {
1480 var pane = _o[_m];
1481 if (pane.s.dtPane !== undefined) {
1482 var tempFilter = true;
1483 pane.s.filteringActive = true;
1484 if ((filterPane !== -1 && filterPane !== null && filterPane === pane.s.index) || filterActive === false) {
1485 tempFilter = false;
1486 pane.s.filteringActive = false;
1487 }
1488 pane.updatePane(!tempFilter ? tempFilter : filterActive);
1489 }
1490 }
1491 // Update the label that shows how many filters are in place
1492 this._updateFilterCount();
1493 }
1494 if (!filterActive) {
1495 this.s.selectionList = [];
1496 }
1497 }
1498 };
1499 /**
1500 * Attach the panes, buttons and title to the document
1501 */
1502 SearchPanes.prototype._attach = function () {
1503 $(this.dom.container).removeClass(this.classes.hide);
1504 $(this.dom.titleRow).removeClass(this.classes.hide);
1505 $(this.dom.titleRow).remove();
1506 $(this.dom.title).appendTo(this.dom.titleRow);
1507 // If the clear button is permitted attach it
1508 if (this.c.clear) {
1509 $(this.dom.clearAll).appendTo(this.dom.titleRow);
1510 }
1511 $(this.dom.titleRow).appendTo(this.dom.container);
1512 // Attach the container for each individual pane to the overall container
1513 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1514 var pane = _a[_i];
1515 $(pane.dom.container).appendTo(this.dom.panes);
1516 }
1517 // Attach everything to the document
1518 $(this.dom.panes).appendTo(this.dom.container);
1519 if ($('div.' + this.classes.container).length === 0) {
1520 $(this.dom.container).prependTo(this.s.dt);
1521 }
1522 return this.dom.container;
1523 };
1524 /**
1525 * Attach the top row containing the filter count and clear all button
1526 */
1527 SearchPanes.prototype._attachExtras = function () {
1528 $(this.dom.container).removeClass(this.classes.hide);
1529 $(this.dom.titleRow).removeClass(this.classes.hide);
1530 $(this.dom.titleRow).remove();
1531 $(this.dom.title).appendTo(this.dom.titleRow);
1532 // If the clear button is permitted attach it
1533 if (this.c.clear) {
1534 $(this.dom.clearAll).appendTo(this.dom.titleRow);
1535 }
1536 $(this.dom.titleRow).appendTo(this.dom.container);
1537 return this.dom.container;
1538 };
1539 /**
1540 * If there are no panes to display then this method is called to either
1541 * display a message in their place or hide them completely.
1542 */
1543 SearchPanes.prototype._attachMessage = function () {
1544 // Create a message to display on the screen
1545 var message;
1546 try {
1547 message = this.s.dt.i18n('searchPanes.emptyPanes', 'No SearchPanes');
1548 }
1549 catch (error) {
1550 message = null;
1551 }
1552 // If the message is an empty string then searchPanes.emptyPanes is undefined,
1553 // therefore the pane container should be removed from the display
1554 if (message === null) {
1555 $(this.dom.container).addClass(this.classes.hide);
1556 $(this.dom.titleRow).removeClass(this.classes.hide);
1557 return;
1558 }
1559 else {
1560 $(this.dom.container).removeClass(this.classes.hide);
1561 $(this.dom.titleRow).addClass(this.classes.hide);
1562 }
1563 // Otherwise display the message
1564 $(this.dom.emptyMessage).text(message);
1565 this.dom.emptyMessage.appendTo(this.dom.container);
1566 return this.dom.container;
1567 };
1568 /**
1569 * Attaches the panes to the document and displays a message or hides if there are none
1570 */
1571 SearchPanes.prototype._attachPaneContainer = function () {
1572 // If a pane is to be displayed then attach the normal pane output
1573 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1574 var pane = _a[_i];
1575 if (pane.s.displayed === true) {
1576 return this._attach();
1577 }
1578 }
1579 // Otherwise attach the custom message or remove the container from the display
1580 return this._attachMessage();
1581 };
1582 /**
1583 * Prepares the panes for selections to be made when cascade is active and a deselect has occured
1584 * @param newSelectionList the list of selections which are to be made
1585 */
1586 SearchPanes.prototype._cascadeRegen = function (newSelectionList) {
1587 // Set this to true so that the actions taken do not cause this to run until it is finished
1588 this.regenerating = true;
1589 // If only one pane has been selected then take note of its index
1590 var solePane = -1;
1591 if (newSelectionList.length === 1) {
1592 solePane = newSelectionList[0].index;
1593 }
1594 // Let the pane know that a cascadeRegen is taking place to avoid unexpected behaviour
1595 // and clear all of the previous selections in the pane
1596 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1597 var pane = _a[_i];
1598 pane.setCascadeRegen(true);
1599 pane.setClear(true);
1600 // If this is the same as the pane with the only selection then pass it as a parameter into clearPane
1601 if ((pane.s.dtPane !== undefined && pane.s.index === solePane) || pane.s.dtPane !== undefined) {
1602 pane.clearPane();
1603 }
1604 pane.setClear(false);
1605 }
1606 // Remake Selections
1607 this._makeCascadeSelections(newSelectionList);
1608 // Set the selection list property to be the list without the selections from the deselect pane
1609 this.s.selectionList = newSelectionList;
1610 // The regeneration of selections is over so set it back to false
1611 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
1612 var pane = _c[_b];
1613 pane.setCascadeRegen(false);
1614 }
1615 this.regenerating = false;
1616 };
1617 /**
1618 * Attaches the message to the document but does not add any panes
1619 */
1620 SearchPanes.prototype._checkMessage = function () {
1621 // If a pane is to be displayed then attach the normal pane output
1622 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1623 var pane = _a[_i];
1624 if (pane.s.displayed === true) {
1625 return;
1626 }
1627 }
1628 // Otherwise attach the custom message or remove the container from the display
1629 return this._attachMessage();
1630 };
1631 /**
1632 * Gets the selection list from the previous state and stores it in the selectionList Property
1633 */
1634 SearchPanes.prototype._getState = function () {
1635 var loadedFilter = this.s.dt.state.loaded();
1636 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList !== undefined) {
1637 this.s.selectionList = loadedFilter.searchPanes.selectionList;
1638 }
1639 };
1640 /**
1641 * Makes all of the selections when cascade is active
1642 * @param newSelectionList the list of selections to be made, in the order they were originally selected
1643 */
1644 SearchPanes.prototype._makeCascadeSelections = function (newSelectionList) {
1645 // make selections in the order they were made previously, excluding those from the pane where a deselect was made
1646 for (var _i = 0, newSelectionList_1 = newSelectionList; _i < newSelectionList_1.length; _i++) {
1647 var selection = newSelectionList_1[_i];
1648 var _loop_1 = function (pane) {
1649 if (pane.s.index === selection.index && pane.s.dtPane !== undefined) {
1650 // if there are any selections currently in the pane then deselect them as we are about to make our new selections
1651 if (pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0 && pane.s.dtPane !== undefined) {
1652 pane.setClear(true);
1653 pane.clearPane();
1654 pane.setClear(false);
1655 }
1656 var _loop_2 = function (row) {
1657 pane.s.dtPane.rows().every(function (rowIdx) {
1658 if (pane.s.dtPane.row(rowIdx).data().filter === row.filter) {
1659 pane.s.dtPane.row(rowIdx).select();
1660 }
1661 });
1662 };
1663 // select every row in the pane that was selected previously
1664 for (var _i = 0, _a = selection.rows; _i < _a.length; _i++) {
1665 var row = _a[_i];
1666 _loop_2(row);
1667 }
1668 // Update the label that shows how many filters are in place
1669 this_1._updateFilterCount();
1670 }
1671 };
1672 var this_1 = this;
1673 // As the selections may have been made across the panes in a different order to the pane index we must identify
1674 // which pane has the index of the selection. This is also important for colreorder etc
1675 for (var _a = 0, _b = this.s.panes; _a < _b.length; _a++) {
1676 var pane = _b[_a];
1677 _loop_1(pane);
1678 }
1679 }
1680 // Make sure that the state is saved after all of these selections
1681 this.s.dt.state.save();
1682 };
1683 /**
1684 * Declares the instances of individual searchpanes dependant on the number of columns.
1685 * It is necessary to run this once preInit has completed otherwise no panes will be
1686 * created as the column count will be 0.
1687 * @param table the DataTable api for the parent table
1688 * @param paneSettings the settings passed into the constructor
1689 * @param opts the options passed into the constructor
1690 */
1691 SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
1692 var _this = this;
1693 // Create Panes
1694 table
1695 .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
1696 .eq(0)
1697 .each(function (idx) {
1698 _this.s.panes.push(new SearchPane(paneSettings, opts, idx, _this.c.layout, _this.dom.panes));
1699 });
1700 // If there is any extra custom panes defined then create panes for them too
1701 var rowLength = table.columns().eq(0).toArray().length;
1702 var paneLength = this.c.panes.length;
1703 for (var i = 0; i < paneLength; i++) {
1704 var id = rowLength + i;
1705 this.s.panes.push(new SearchPane(paneSettings, opts, id, this.c.layout, this.dom.panes, this.c.panes[i]));
1706 }
1707 // If this internal property is true then the DataTable has been initialised already
1708 if (this.s.dt.settings()[0]._bInitComplete) {
1709 this._paneStartup(table);
1710 }
1711 else {
1712 // Otherwise add the paneStartup function to the list of functions that are to be run when the table is initialised
1713 // This will garauntee that the panes are initialised before the init event and init Complete callback is fired
1714 this.s.dt.settings()[0].aoInitComplete.push({ fn: function () {
1715 _this._paneStartup(table);
1716 } });
1717 }
1718 };
1719 /**
1720 * Runs the start up functions for the panes to enable listeners and populate panes
1721 * @param table the DataTable api for the parent Table
1722 */
1723 SearchPanes.prototype._paneStartup = function (table) {
1724 var _this = this;
1725 // Magic number of 500 is a guess at what will be fast
1726 if (this.s.dt.page.info().recordsTotal <= 500) {
1727 this._startup(table);
1728 }
1729 else {
1730 setTimeout(function () {
1731 _this._startup(table);
1732 }, 100);
1733 }
1734 };
1735 /**
1736 * Initialises the tables previous/preset selections and initialises callbacks for events
1737 * @param table the parent table for which the searchPanes are being created
1738 */
1739 SearchPanes.prototype._startup = function (table) {
1740 var _this = this;
1741 $(this.dom.container).text('');
1742 // Attach clear button and title bar to the document
1743 this._attachExtras();
1744 $(this.dom.container).append(this.dom.panes);
1745 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1746 var pane = _a[_i];
1747 pane.rebuildPane();
1748 }
1749 this._updateFilterCount();
1750 this._checkMessage();
1751 // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
1752 table.on('draw.dtsps', function () {
1753 _this._updateFilterCount();
1754 if (_this.c.cascadePanes || _this.c.viewTotal) {
1755 _this.redrawPanes();
1756 }
1757 _this.s.filterPane = -1;
1758 });
1759 // Whenever a state save occurs store the selection list in the state object
1760 this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
1761 if (data.searchPanes === undefined) {
1762 data.searchPanes = {};
1763 }
1764 data.searchPanes.selectionList = _this.s.selectionList;
1765 });
1766 // If cascadePanes is active then make the previous selections in the order they were previously
1767 if (this.s.selectionList.length > 0 && this.c.cascadePanes) {
1768 this._cascadeRegen(this.s.selectionList);
1769 }
1770 // PreSelect any selections which have been defined using the preSelect option
1771 table
1772 .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
1773 .eq(0)
1774 .each(function (idx) {
1775 if (_this.s.panes[idx] !== undefined &&
1776 _this.s.panes[idx].s.dtPane !== undefined &&
1777 _this.s.panes[idx].s.colOpts.preSelect !== undefined) {
1778 var tableLength = _this.s.panes[idx].s.dtPane.rows().data().toArray().length;
1779 for (var i = 0; i < tableLength; i++) {
1780 if (_this.s.panes[idx].s.colOpts.preSelect.indexOf(_this.s.panes[idx].s.dtPane.cell(i, 0).data()) !== -1) {
1781 _this.s.panes[idx].s.dtPane.row(i).select();
1782 _this.s.panes[idx].updateTable();
1783 }
1784 }
1785 }
1786 });
1787 // Update the title bar to show how many filters have been selected
1788 this._updateFilterCount();
1789 // If the table is destroyed and restarted then clear the selections so that they do not persist.
1790 table.on('destroy.dtsps', function () {
1791 for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
1792 var pane = _a[_i];
1793 pane.destroy();
1794 }
1795 table.off('.dtsps');
1796 $(_this.dom.clearAll).off('.dtsps');
1797 $(_this.dom.container).remove();
1798 _this.clearSelections();
1799 });
1800 // When the clear All button has been pressed clear all of the selections in the panes
1801 if (this.c.clear) {
1802 $(this.dom.clearAll).on('click.dtsps', function () {
1803 _this.clearSelections();
1804 });
1805 }
1806 table.settings()[0]._searchPanes = this;
1807 };
1808 /**
1809 * Updates the number of filters that have been applied in the title
1810 */
1811 SearchPanes.prototype._updateFilterCount = function () {
1812 var filterCount = 0;
1813 // Add the number of all of the filters throughout the panes
1814 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
1815 var pane = _a[_i];
1816 if (pane.s.dtPane !== undefined) {
1817 filterCount += pane.getPaneCount();
1818 }
1819 }
1820 // Run the message through the internationalisation method to improve readability
1821 var message = this.s.dt.i18n('searchPanes.title', 'Filters Active - %d', filterCount);
1822 $(this.dom.title).text(message);
1823 };
1824 SearchPanes.version = '1.0.1';
1825 SearchPanes.classes = {
1826 clear: 'dtsp-clear',
1827 clearAll: 'dtsp-clearAll',
1828 container: 'dtsp-searchPanes',
1829 emptyMessage: 'dtsp-emptyMessage',
1830 hide: 'dtsp-hidden',
1831 panes: 'dtsp-panesContainer',
1832 search: 'dtsp-search',
1833 title: 'dtsp-title',
1834 titleRow: 'dtsp-titleRow'
1835 };
1836 // Define SearchPanes default options
1837 SearchPanes.defaults = {
1838 cascadePanes: false,
1839 clear: true,
1840 container: function (dt) {
1841 return dt.table().container();
1842 },
1843 columns: [],
1844 layout: 'columns-3',
1845 panes: [],
1846 viewTotal: false
1847 };
1848 return SearchPanes;
1849 }());
1850
1851 /*! SearchPanes 1.0.1
1852 * 2019-2020 SpryMedia Ltd - datatables.net/license
1853 */
1854 // DataTables extensions common UMD. Note that this allows for AMD, CommonJS
1855 // (with window and jQuery being allowed as parameters to the returned
1856 // function) or just default browser loading.
1857 (function (factory) {
1858 if (typeof define === 'function' && define.amd) {
1859 // AMD
1860 define(['jquery', 'datatables.net'], function ($) {
1861 return factory($, window, document);
1862 });
1863 }
1864 else if (typeof exports === 'object') {
1865 // CommonJS
1866 module.exports = function (root, $) {
1867 if (!root) {
1868 root = window;
1869 }
1870 if (!$ || !$.fn.dataTable) {
1871 $ = require('datatables.net')(root, $).$;
1872 }
1873 return factory($, root, root.document);
1874 };
1875 }
1876 else {
1877 // Browser - assume jQuery has already been loaded
1878 factory(window.jQuery, window, document);
1879 }
1880 }(function ($, window, document) {
1881 var DataTable = $.fn.dataTable;
1882 $.fn.dataTable.SearchPanes = SearchPanes;
1883 $.fn.DataTable.SearchPanes = SearchPanes;
1884 $.fn.dataTable.SearchPane = SearchPane;
1885 $.fn.DataTable.SearchPane = SearchPane;
1886 DataTable.Api.register('searchPanes.rebuild()', function () {
1887 return this.iterator('table', function () {
1888 if (this.searchPanes) {
1889 this.searchPanes.rebuild();
1890 }
1891 });
1892 });
1893 DataTable.Api.register('column().paneOptions()', function (options) {
1894 return this.iterator('column', function (idx) {
1895 var col = this.aoColumns[idx];
1896 if (!col.searchPanes) {
1897 col.searchPanes = {};
1898 }
1899 col.searchPanes.values = options;
1900 if (this.searchPanes) {
1901 this.searchPanes.rebuild();
1902 }
1903 });
1904 });
1905 var apiRegister = $.fn.dataTable.Api.register;
1906 apiRegister('searchPanes()', function () {
1907 return this;
1908 });
1909 apiRegister('searchPanes.clearSelections()', function () {
1910 var ctx = this.context[0];
1911 ctx._searchPanes.clearSelections();
1912 return this;
1913 });
1914 apiRegister('searchPanes.rebuildPane()', function (targetIdx) {
1915 var ctx = this.context[0];
1916 ctx._searchPanes.rebuild(targetIdx);
1917 return this;
1918 });
1919 apiRegister('searchPanes.container()', function () {
1920 var ctx = this.context[0];
1921 return ctx._searchPanes.getNode();
1922 });
1923 $.fn.dataTable.ext.buttons.searchPanesClear = {
1924 text: 'Clear Panes',
1925 action: function (e, dt, node, config) {
1926 dt.searchPanes.clearSelections();
1927 }
1928 };
1929 $.fn.dataTable.ext.buttons.searchPanes = {
1930 text: 'Search Panes',
1931 init: function (dt, node, config) {
1932 var panes = new $.fn.dataTable.SearchPanes(dt, {
1933 filterChanged: function (count) {
1934 dt.button(node).text(dt.i18n('searchPanes.collapse', { 0: 'SearchPanes', _: 'SearchPanes (%d)' }, count));
1935 }
1936 });
1937 var message = dt.i18n('searchPanes.collapse', 'SearchPanes');
1938 dt.button(node).text(message);
1939 config._panes = panes;
1940 },
1941 action: function (e, dt, node, config) {
1942 e.stopPropagation();
1943 this.popover(config._panes.getNode(), {
1944 align: 'dt-container'
1945 });
1946 config._panes.adjust();
1947 }
1948 };
1949 function _init(settings, fromPre) {
1950 if (fromPre === void 0) { fromPre = false; }
1951 var api = new DataTable.Api(settings);
1952 var opts = api.init().searchPanes || DataTable.defaults.searchPanes;
1953 var searchPanes = new SearchPanes(api, opts, fromPre);
1954 var node = searchPanes.getNode();
1955 return node;
1956 }
1957 // Attach a listener to the document which listens for DataTables initialisation
1958 // events so we can automatically initialise
1959 $(document).on('preInit.dt.dtsp', function (e, settings, json) {
1960 if (e.namespace !== 'dt') {
1961 return;
1962 }
1963 if (settings.oInit.searchPanes ||
1964 DataTable.defaults.searchPanes) {
1965 if (!settings._searchPanes) {
1966 _init(settings, true);
1967 }
1968 }
1969 });
1970 // DataTables `dom` feature option
1971 DataTable.ext.feature.push({
1972 cFeature: 'P',
1973 fnInit: _init
1974 });
1975 // DataTables 2 layout feature
1976 if (DataTable.ext.features) {
1977 DataTable.ext.features.register('searchPanes', _init);
1978 }
1979 }));
1980
1981}());
Note: See TracBrowser for help on using the repository browser.