25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

3172 satır
154KB

  1. /*! SearchPanes 1.4.0
  2. * 2019-2020 SpryMedia Ltd - datatables.net/license
  3. */
  4. (function () {
  5. 'use strict';
  6. var $;
  7. var dataTable;
  8. function setJQuery(jq) {
  9. $ = jq;
  10. dataTable = jq.fn.dataTable;
  11. }
  12. var SearchPane = /** @class */ (function () {
  13. /**
  14. * Creates the panes, sets up the search function
  15. *
  16. * @param paneSettings The settings for the searchPanes
  17. * @param opts The options for the default features
  18. * @param idx the index of the column for this pane
  19. * @returns {object} the pane that has been created, including the table and the index of the pane
  20. */
  21. function SearchPane(paneSettings, opts, idx, layout, panesContainer, panes) {
  22. var _this = this;
  23. if (panes === void 0) { panes = null; }
  24. // Check that the required version of DataTables is included
  25. if (!dataTable || !dataTable.versionCheck || !dataTable.versionCheck('1.10.0')) {
  26. throw new Error('SearchPane requires DataTables 1.10 or newer');
  27. }
  28. // Check that Select is included
  29. // eslint-disable-next-line no-extra-parens
  30. if (!dataTable.select) {
  31. throw new Error('SearchPane requires Select');
  32. }
  33. var table = new dataTable.Api(paneSettings);
  34. this.classes = $.extend(true, {}, SearchPane.classes);
  35. // Get options from user
  36. this.c = $.extend(true, {}, SearchPane.defaults, opts);
  37. if (opts !== undefined && opts.hideCount !== undefined && opts.viewCount === undefined) {
  38. this.c.viewCount = !this.c.hideCount;
  39. }
  40. this.customPaneSettings = panes;
  41. this.s = {
  42. cascadeRegen: false,
  43. clearing: false,
  44. colOpts: [],
  45. deselect: false,
  46. displayed: false,
  47. dt: table,
  48. dtPane: undefined,
  49. filteringActive: false,
  50. firstSet: true,
  51. forceViewTotal: false,
  52. index: idx,
  53. indexes: [],
  54. lastCascade: false,
  55. lastSelect: false,
  56. listSet: false,
  57. name: undefined,
  58. redraw: false,
  59. rowData: {
  60. arrayFilter: [],
  61. arrayOriginal: [],
  62. arrayTotals: [],
  63. bins: {},
  64. binsOriginal: {},
  65. binsTotal: {},
  66. filterMap: new Map(),
  67. totalOptions: 0
  68. },
  69. scrollTop: 0,
  70. searchFunction: undefined,
  71. selectPresent: false,
  72. serverSelect: [],
  73. serverSelecting: false,
  74. showFiltered: false,
  75. tableLength: null,
  76. updating: false
  77. };
  78. var rowLength = table.columns().eq(0).toArray().length;
  79. this.colExists = this.s.index < rowLength;
  80. // Add extra elements to DOM object including clear and hide buttons
  81. this.c.layout = layout;
  82. var layVal = parseInt(layout.split('-')[1], 10);
  83. this.dom = {
  84. buttonGroup: $('<div/>').addClass(this.classes.buttonGroup),
  85. clear: $('<button type="button">&#215;</button>')
  86. .addClass(this.classes.disabledButton)
  87. .attr('disabled', 'true')
  88. .addClass(this.classes.paneButton)
  89. .addClass(this.classes.clearButton),
  90. collapseButton: $('<button type="button"><span class="dtsp-caret">&#x5e;</span></button>')
  91. .addClass(this.classes.paneButton)
  92. .addClass(this.classes.collapseButton),
  93. container: $('<div/>')
  94. .addClass(this.classes.container)
  95. .addClass(this.classes.layout +
  96. (layVal < 10 ? layout : layout.split('-')[0] + '-9')),
  97. countButton: $('<button type="button"></button>')
  98. .addClass(this.classes.paneButton)
  99. .addClass(this.classes.countButton),
  100. dtP: $('<table><thead><tr><th>' +
  101. (this.colExists
  102. ? $(table.column(this.colExists ? this.s.index : 0).header()).text()
  103. : this.customPaneSettings.header || 'Custom Pane') + '</th><th/></tr></thead></table>'),
  104. lower: $('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
  105. nameButton: $('<button type="button"></button>')
  106. .addClass(this.classes.paneButton)
  107. .addClass(this.classes.nameButton),
  108. panesContainer: panesContainer,
  109. searchBox: $('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
  110. searchButton: $('<button type = "button" class="' + this.classes.searchIcon + '"></button>')
  111. .addClass(this.classes.paneButton),
  112. searchCont: $('<div/>').addClass(this.classes.searchCont),
  113. searchLabelCont: $('<div/>').addClass(this.classes.searchLabelCont),
  114. topRow: $('<div/>').addClass(this.classes.topRow),
  115. upper: $('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
  116. };
  117. this.s.displayed = false;
  118. table = this.s.dt;
  119. this.selections = [];
  120. this.s.colOpts = this.colExists ? this._getOptions() : this._getBonusOptions();
  121. var colOpts = this.s.colOpts;
  122. var clear = $('<button type="button">X</button>').addClass(this.classes.paneButton);
  123. clear.text(table.i18n('searchPanes.clearPane', this.c.i18n.clearPane));
  124. this.dom.container.addClass(colOpts.className);
  125. this.dom.container.addClass(this.customPaneSettings !== null && this.customPaneSettings.className !== undefined
  126. ? this.customPaneSettings.className
  127. : '');
  128. // Set the value of name incase ordering is desired
  129. if (this.s.colOpts.name !== undefined) {
  130. this.s.name = this.s.colOpts.name;
  131. }
  132. else if (this.customPaneSettings !== null && this.customPaneSettings.name !== undefined) {
  133. this.s.name = this.customPaneSettings.name;
  134. }
  135. else {
  136. this.s.name = this.colExists ?
  137. $(table.column(this.s.index).header()).text() :
  138. this.customPaneSettings.header || 'Custom Pane';
  139. }
  140. $(panesContainer).append(this.dom.container);
  141. var tableNode = table.table(0).node();
  142. // Custom search function for table
  143. this.s.searchFunction = function (settings, searchData, dataIndex, origData) {
  144. // If no data has been selected then show all
  145. if (_this.selections.length === 0) {
  146. return true;
  147. }
  148. if (settings.nTable !== tableNode) {
  149. return true;
  150. }
  151. var filter = null;
  152. if (_this.colExists) {
  153. // Get the current filtered data
  154. filter = searchData[_this.s.index];
  155. if (colOpts.orthogonal.filter !== 'filter') {
  156. // get the filter value from the map
  157. filter = _this.s.rowData.filterMap.get(dataIndex);
  158. if (filter instanceof $.fn.dataTable.Api) {
  159. // eslint-disable-next-line no-extra-parens
  160. filter = filter.toArray();
  161. }
  162. }
  163. }
  164. return _this._search(filter, dataIndex);
  165. };
  166. $.fn.dataTable.ext.search.push(this.s.searchFunction);
  167. // If the clear button for this pane is clicked clear the selections
  168. if (this.c.clear) {
  169. clear.on('click', function () {
  170. var searches = _this.dom.container.find('.' + _this.classes.search.replace(/\s+/g, '.'));
  171. searches.each(function () {
  172. $(this).val('');
  173. $(this).trigger('input');
  174. });
  175. _this.clearPane();
  176. });
  177. }
  178. // Sometimes the top row of the panes containing the search box and ordering buttons appears
  179. // weird if the width of the panes is lower than expected, this fixes the design.
  180. // Equally this may occur when the table is resized.
  181. table.on('draw.dtsp', function () {
  182. _this.adjustTopRow();
  183. });
  184. table.on('buttons-action', function () {
  185. _this.adjustTopRow();
  186. });
  187. // When column-reorder is present and the columns are moved, it is necessary to
  188. // reassign all of the panes indexes to the new index of the column.
  189. table.on('column-reorder.dtsp', function (e, settings, details) {
  190. _this.s.index = details.mapping[_this.s.index];
  191. });
  192. return this;
  193. }
  194. /**
  195. * Adds a row to the panes table
  196. *
  197. * @param display the value to be displayed to the user
  198. * @param filter the value to be filtered on when searchpanes is implemented
  199. * @param shown the number of rows in the table that are currently visible matching this criteria
  200. * @param total the total number of rows in the table that match this criteria
  201. * @param sort the value to be sorted in the pane table
  202. * @param type the value of which the type is to be derived from
  203. */
  204. SearchPane.prototype.addRow = function (display, filter, shown, total, sort, type, className) {
  205. var index;
  206. for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
  207. var entry = _a[_i];
  208. if (entry.filter === filter) {
  209. index = entry.index;
  210. }
  211. }
  212. if (index === undefined) {
  213. index = this.s.indexes.length;
  214. this.s.indexes.push({ filter: filter, index: index });
  215. }
  216. return this.s.dtPane.row.add({
  217. className: className,
  218. display: display !== '' ?
  219. display :
  220. this.emptyMessage(),
  221. filter: filter,
  222. index: index,
  223. shown: shown,
  224. sort: sort,
  225. total: total,
  226. type: type
  227. });
  228. };
  229. /**
  230. * Adjusts the layout of the top row when the screen is resized
  231. */
  232. SearchPane.prototype.adjustTopRow = function () {
  233. var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer.replace(/\s+/g, '.'));
  234. var subRow1 = this.dom.container.find('.' + this.classes.subRow1.replace(/\s+/g, '.'));
  235. var subRow2 = this.dom.container.find('.' + this.classes.subRow2.replace(/\s+/g, '.'));
  236. var topRow = this.dom.container.find('.' + this.classes.topRow.replace(/\s+/g, '.'));
  237. // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
  238. // Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
  239. if (($(subContainers[0]).width() < 252 || $(topRow[0]).width() < 252) && $(subContainers[0]).width() !== 0) {
  240. $(subContainers[0]).addClass(this.classes.narrow);
  241. $(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
  242. $(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
  243. }
  244. else {
  245. $(subContainers[0]).removeClass(this.classes.narrow);
  246. $(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
  247. $(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
  248. }
  249. };
  250. /**
  251. * In the case of a rebuild there is potential for new data to have been included or removed
  252. * so all of the rowData must be reset as a precaution.
  253. */
  254. SearchPane.prototype.clearData = function () {
  255. this.s.rowData = {
  256. arrayFilter: [],
  257. arrayOriginal: [],
  258. arrayTotals: [],
  259. bins: {},
  260. binsOriginal: {},
  261. binsTotal: {},
  262. filterMap: new Map(),
  263. totalOptions: 0
  264. };
  265. };
  266. /**
  267. * Clear the selections in the pane
  268. */
  269. SearchPane.prototype.clearPane = function () {
  270. // Deselect all rows which are selected and update the table and filter count.
  271. this.s.dtPane.rows({ selected: true }).deselect();
  272. this.updateTable();
  273. return this;
  274. };
  275. /**
  276. * Collapses the pane so that only the header is displayed
  277. */
  278. SearchPane.prototype.collapse = function () {
  279. var _this = this;
  280. if (!this.s.displayed ||
  281. (!this.c.collapse && this.s.colOpts.collapse !== true ||
  282. this.s.colOpts.collapse === false)) {
  283. return;
  284. }
  285. this.dom.collapseButton.addClass(this.classes.rotated);
  286. $(this.s.dtPane.table().container()).addClass(this.classes.hidden);
  287. this.dom.topRow.addClass(this.classes.bordered);
  288. this.dom.countButton.addClass(this.classes.disabledButton);
  289. this.dom.nameButton.addClass(this.classes.disabledButton);
  290. this.dom.searchButton.addClass(this.classes.disabledButton);
  291. this.dom.topRow.one('click', function () {
  292. _this.show();
  293. });
  294. };
  295. /**
  296. * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
  297. */
  298. SearchPane.prototype.destroy = function () {
  299. if (this.s.dtPane !== undefined) {
  300. this.s.dtPane.off('.dtsp');
  301. }
  302. this.dom.nameButton.off('.dtsp');
  303. this.dom.collapseButton.off('.dtsp');
  304. this.dom.countButton.off('.dtsp');
  305. this.dom.clear.off('.dtsp');
  306. this.dom.searchButton.off('.dtsp');
  307. this.dom.container.remove();
  308. var searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
  309. while (searchIdx !== -1) {
  310. $.fn.dataTable.ext.search.splice(searchIdx, 1);
  311. searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
  312. }
  313. // If the datatables have been defined for the panes then also destroy these
  314. if (this.s.dtPane !== undefined) {
  315. this.s.dtPane.destroy();
  316. }
  317. this.s.listSet = false;
  318. };
  319. /**
  320. * Getting the legacy message is a little complex due a legacy parameter
  321. */
  322. SearchPane.prototype.emptyMessage = function () {
  323. var def = this.c.i18n.emptyMessage;
  324. // Legacy parameter support
  325. if (this.c.emptyMessage) {
  326. def = this.c.emptyMessage;
  327. }
  328. // Override per column
  329. if (this.s.colOpts.emptyMessage !== false && this.s.colOpts.emptyMessage !== null) {
  330. def = this.s.colOpts.emptyMessage;
  331. }
  332. return this.s.dt.i18n('searchPanes.emptyMessage', def);
  333. };
  334. /**
  335. * Updates the number of filters that have been applied in the title
  336. */
  337. SearchPane.prototype.getPaneCount = function () {
  338. return this.s.dtPane !== undefined ?
  339. this.s.dtPane.rows({ selected: true }).data().toArray().length :
  340. 0;
  341. };
  342. /**
  343. * Rebuilds the panes from the start having deleted the old ones
  344. *
  345. * @param? last boolean to indicate if this is the last pane a selection was made in
  346. * @param? dataIn data to be used in buildPane
  347. * @param? init Whether this is the initial draw or not
  348. * @param? maintainSelection Whether the current selections are to be maintained over rebuild
  349. */
  350. SearchPane.prototype.rebuildPane = function (last, dataIn, init, maintainSelection) {
  351. if (last === void 0) { last = false; }
  352. if (dataIn === void 0) { dataIn = null; }
  353. if (init === void 0) { init = null; }
  354. if (maintainSelection === void 0) { maintainSelection = false; }
  355. this.clearData();
  356. var selectedRows = [];
  357. this.s.serverSelect = [];
  358. var prevEl = null;
  359. // When rebuilding strip all of the HTML Elements out of the container and start from scratch
  360. if (this.s.dtPane !== undefined) {
  361. if (maintainSelection) {
  362. if (!this.s.dt.page.info().serverSide) {
  363. selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
  364. }
  365. else {
  366. this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
  367. }
  368. }
  369. this.s.dtPane.clear().destroy();
  370. prevEl = this.dom.container.prev();
  371. this.destroy();
  372. this.s.dtPane = undefined;
  373. $.fn.dataTable.ext.search.push(this.s.searchFunction);
  374. }
  375. this.dom.container.removeClass(this.classes.hidden);
  376. this.s.displayed = false;
  377. this._buildPane(!this.s.dt.page.info().serverSide ?
  378. selectedRows :
  379. this.s.serverSelect, last, dataIn, init, prevEl);
  380. return this;
  381. };
  382. /**
  383. * removes the pane from the page and sets the displayed property to false.
  384. */
  385. SearchPane.prototype.removePane = function () {
  386. this.s.displayed = false;
  387. this.dom.container.hide();
  388. };
  389. /**
  390. * Resizes the pane based on the layout that is passed in
  391. *
  392. * @param layout the layout to be applied to this pane
  393. */
  394. SearchPane.prototype.resize = function (layout) {
  395. this.c.layout = layout;
  396. var layVal = parseInt(layout.split('-')[1], 10);
  397. this.dom.container
  398. .removeClass()
  399. .addClass(this.classes.container)
  400. .addClass(this.classes.layout +
  401. (layVal < 10 ? layout : layout.split('-')[0] + '-9'))
  402. .addClass(this.s.colOpts.className)
  403. .addClass(this.customPaneSettings !== null && this.customPaneSettings.className !== undefined
  404. ? this.customPaneSettings.className
  405. : '')
  406. .addClass(this.classes.show);
  407. this.adjustTopRow();
  408. };
  409. /**
  410. * Sets the cascadeRegen property of the pane. Accessible from above because as SearchPanes.ts
  411. * deals with the rebuilds.
  412. *
  413. * @param val the boolean value that the cascadeRegen property is to be set to
  414. */
  415. SearchPane.prototype.setCascadeRegen = function (val) {
  416. this.s.cascadeRegen = val;
  417. };
  418. /**
  419. * This function allows the clearing property to be assigned. This is used when implementing cascadePane.
  420. * In setting this to true for the clearing of the panes selection on the deselects it forces the pane to
  421. * repopulate from the entire dataset not just the displayed values.
  422. *
  423. * @param val the boolean value which the clearing property is to be assigned
  424. */
  425. SearchPane.prototype.setClear = function (val) {
  426. this.s.clearing = val;
  427. };
  428. /**
  429. * Expands the pane from the collapsed state
  430. */
  431. SearchPane.prototype.show = function () {
  432. if (!this.s.displayed) {
  433. return;
  434. }
  435. this.dom.collapseButton.removeClass(this.classes.rotated);
  436. $(this.s.dtPane.table().container()).removeClass(this.classes.hidden);
  437. this.dom.topRow.removeClass(this.classes.bordered);
  438. this.dom.countButton.removeClass(this.classes.disabledButton);
  439. this.dom.nameButton.removeClass(this.classes.disabledButton);
  440. this.dom.searchButton.removeClass(this.classes.disabledButton);
  441. };
  442. /**
  443. * Updates the values of all of the panes
  444. *
  445. * @param draw whether this has been triggered by a draw event or not
  446. */
  447. SearchPane.prototype.updatePane = function (draw) {
  448. if (draw === void 0) { draw = false; }
  449. this.s.updating = true;
  450. this._updateCommon(draw);
  451. this.s.updating = false;
  452. };
  453. /**
  454. * Updates the panes if one of the options to do so has been set to true
  455. * rather than the filtered message when using viewTotal.
  456. */
  457. SearchPane.prototype.updateTable = function () {
  458. var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
  459. this.selections = selectedRows;
  460. this._searchExtras();
  461. // If either of the options that effect how the panes are displayed are selected then update the Panes
  462. if (this.c.cascadePanes || this.c.viewTotal) {
  463. this.updatePane();
  464. }
  465. };
  466. /**
  467. * Sets the listeners for the pane.
  468. *
  469. * Having it in it's own function makes it easier to only set them once
  470. */
  471. SearchPane.prototype._setListeners = function () {
  472. var _this = this;
  473. var rowData = this.s.rowData;
  474. var t0;
  475. // When an item is selected on the pane, add these to the array which holds selected items.
  476. // Custom search will perform.
  477. this.s.dtPane.off('select.dtsp');
  478. this.s.dtPane.on('select.dtsp', function () {
  479. clearTimeout(t0);
  480. if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
  481. if (!_this.s.serverSelecting) {
  482. _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
  483. _this.s.scrollTop = $(_this.s.dtPane.table().node()).parent()[0].scrollTop;
  484. _this.s.selectPresent = true;
  485. _this.s.dt.draw(false);
  486. }
  487. }
  488. else if (!_this.s.updating) {
  489. _this.s.selectPresent = true;
  490. _this._makeSelection();
  491. _this.s.selectPresent = false;
  492. }
  493. _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  494. });
  495. // When an item is deselected on the pane, re add the currently selected items to the array
  496. // which holds selected items. Custom search will be performed.
  497. this.s.dtPane.off('deselect.dtsp');
  498. this.s.dtPane.on('deselect.dtsp', function () {
  499. t0 = setTimeout(function () {
  500. _this.s.scrollTop = $(_this.s.dtPane.table().node()).parent()[0].scrollTop;
  501. if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
  502. if (!_this.s.serverSelecting) {
  503. _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
  504. _this.s.deselect = true;
  505. _this.s.dt.draw(false);
  506. }
  507. }
  508. else {
  509. _this.s.deselect = true;
  510. _this._makeSelection();
  511. _this.s.deselect = false;
  512. }
  513. if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
  514. _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  515. }
  516. }, 50);
  517. });
  518. // If we attempty to turn off this event then it will ruin behaviour in other panes
  519. // so need to make sure that it is only done once
  520. if (this.s.firstSet) {
  521. this.s.firstSet = false;
  522. // When saving the state store all of the selected rows for preselection next time around
  523. this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
  524. // If the data being passed in is empty then state clear must have occured
  525. // so clear the panes state as well
  526. if ($.isEmptyObject(data)) {
  527. _this.s.dtPane.state.clear();
  528. return;
  529. }
  530. var selected = [];
  531. var searchTerm;
  532. var order;
  533. var bins;
  534. var arrayFilter;
  535. var collapsed;
  536. // Get all of the data needed for the state save from the pane
  537. if (_this.s.dtPane !== undefined) {
  538. selected = _this.s.dtPane
  539. .rows({ selected: true })
  540. .data()
  541. .map(function (item) { return item.filter.toString(); })
  542. .toArray();
  543. searchTerm = _this.dom.searchBox.val();
  544. order = _this.s.dtPane.order();
  545. bins = rowData.binsOriginal;
  546. arrayFilter = rowData.arrayOriginal;
  547. collapsed = _this.dom.collapseButton.hasClass(_this.classes.rotated);
  548. }
  549. if (data.searchPanes === undefined) {
  550. data.searchPanes = {};
  551. }
  552. if (data.searchPanes.panes === undefined) {
  553. data.searchPanes.panes = [];
  554. }
  555. for (var i = 0; i < data.searchPanes.panes.length; i++) {
  556. if (data.searchPanes.panes[i].id === _this.s.index) {
  557. data.searchPanes.panes.splice(i, 1);
  558. i--;
  559. }
  560. }
  561. // Add the panes data to the state object
  562. data.searchPanes.panes.push({
  563. arrayFilter: arrayFilter,
  564. bins: bins,
  565. collapsed: collapsed,
  566. id: _this.s.index,
  567. order: order,
  568. searchTerm: searchTerm,
  569. selected: selected
  570. });
  571. });
  572. }
  573. this.s.dtPane.off('user-select.dtsp');
  574. this.s.dtPane.on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
  575. originalEvent.stopPropagation();
  576. });
  577. this.s.dtPane.off('draw.dtsp');
  578. this.s.dtPane.on('draw.dtsp', function () {
  579. _this.adjustTopRow();
  580. });
  581. // When the button to order by the name of the options is clicked then
  582. // change the ordering to whatever it isn't currently
  583. this.dom.nameButton.off('click.dtsp');
  584. this.dom.nameButton.on('click.dtsp', function () {
  585. var currentOrder = _this.s.dtPane.order()[0][1];
  586. _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
  587. // This state save is required so that the ordering of the panes is maintained
  588. _this.s.dt.state.save();
  589. });
  590. // When the button to order by the number of entries in the column is clicked then
  591. // change the ordering to whatever it isn't currently
  592. this.dom.countButton.off('click.dtsp');
  593. this.dom.countButton.on('click.dtsp', function () {
  594. var currentOrder = _this.s.dtPane.order()[0][1];
  595. _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
  596. // This state save is required so that the ordering of the panes is maintained
  597. _this.s.dt.state.save();
  598. });
  599. // When the button to order by the number of entries in the column is clicked then
  600. // change the ordering to whatever it isn't currently
  601. this.dom.collapseButton.off('click.dtsp');
  602. this.dom.collapseButton.on('click.dtsp', function (e) {
  603. e.stopPropagation();
  604. var container = $(_this.s.dtPane.table().container());
  605. // Toggle the classes
  606. _this.dom.collapseButton.toggleClass(_this.classes.rotated);
  607. container.toggleClass(_this.classes.hidden);
  608. _this.dom.topRow.toggleClass(_this.classes.bordered);
  609. _this.dom.countButton.toggleClass(_this.classes.disabledButton);
  610. _this.dom.nameButton.toggleClass(_this.classes.disabledButton);
  611. _this.dom.searchButton.toggleClass(_this.classes.disabledButton);
  612. if (container.hasClass(_this.classes.hidden)) {
  613. _this.dom.topRow.on('click', function () { return _this.dom.collapseButton.click(); });
  614. }
  615. else {
  616. _this.dom.topRow.off('click');
  617. }
  618. _this.s.dt.state.save();
  619. return;
  620. });
  621. // When the clear button is clicked reset the pane
  622. this.dom.clear.off('click.dtsp');
  623. this.dom.clear.on('click.dtsp', function () {
  624. var searches = _this.dom.container.find('.' + _this.classes.search.replace(/ /g, '.'));
  625. searches.each(function () {
  626. // set the value of the search box to be an empty string and then search on that, effectively reseting
  627. $(this).val('');
  628. $(this).trigger('input');
  629. });
  630. _this.clearPane();
  631. });
  632. // When the search button is clicked then draw focus to the search box
  633. this.dom.searchButton.off('click.dtsp');
  634. this.dom.searchButton.on('click.dtsp', function () {
  635. _this.dom.searchBox.focus();
  636. });
  637. // When a character is inputted into the searchbox search the pane for matching values.
  638. // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
  639. this.dom.searchBox.off('click.dtsp');
  640. this.dom.searchBox.on('input.dtsp', function () {
  641. var searchval = _this.dom.searchBox.val();
  642. _this.s.dtPane.search(searchval).draw();
  643. if (typeof searchval === 'string' &&
  644. (searchval.length > 0 ||
  645. searchval.length === 0 && _this.s.dtPane.rows({ selected: true }).data().toArray().length > 0)) {
  646. _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  647. }
  648. else {
  649. _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  650. }
  651. // This state save is required so that the searching on the panes is maintained
  652. _this.s.dt.state.save();
  653. });
  654. return true;
  655. };
  656. /**
  657. * Takes in potentially undetected rows and adds them to the array if they are not yet featured
  658. *
  659. * @param filter the filter value of the potential row
  660. * @param display the display value of the potential row
  661. * @param sort the sort value of the potential row
  662. * @param type the type value of the potential row
  663. * @param arrayFilter the array to be populated
  664. * @param bins the bins to be populated
  665. */
  666. SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
  667. // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
  668. if (Array.isArray(filter) || filter instanceof dataTable.Api) {
  669. // Convert to an array so that we can work with it
  670. if (filter instanceof dataTable.Api) {
  671. filter = filter.toArray();
  672. display = display.toArray();
  673. }
  674. if (filter.length === display.length) {
  675. for (var i = 0; i < filter.length; i++) {
  676. // If we haven't seen this row before add it
  677. if (!bins[filter[i]]) {
  678. bins[filter[i]] = 1;
  679. arrayFilter.push({
  680. display: display[i],
  681. filter: filter[i],
  682. sort: sort[i],
  683. type: type[i]
  684. });
  685. }
  686. // Otherwise just increment the count
  687. else {
  688. bins[filter[i]]++;
  689. }
  690. this.s.rowData.totalOptions++;
  691. }
  692. return;
  693. }
  694. else {
  695. throw new Error('display and filter not the same length');
  696. }
  697. }
  698. // If the values were affected by othogonal data and are not an array then check if it is already present
  699. else if (typeof this.s.colOpts.orthogonal === 'string') {
  700. if (!bins[filter]) {
  701. bins[filter] = 1;
  702. arrayFilter.push({
  703. display: display,
  704. filter: filter,
  705. sort: sort,
  706. type: type
  707. });
  708. this.s.rowData.totalOptions++;
  709. }
  710. else {
  711. bins[filter]++;
  712. this.s.rowData.totalOptions++;
  713. return;
  714. }
  715. }
  716. // Otherwise we must just be adding an option
  717. else {
  718. arrayFilter.push({
  719. display: display,
  720. filter: filter,
  721. sort: sort,
  722. type: type
  723. });
  724. }
  725. };
  726. /**
  727. * Method to construct the actual pane.
  728. *
  729. * @param selectedRows previously selected Rows to be reselected
  730. * @last boolean to indicate whether this pane was the last one to have a selection made
  731. */
  732. SearchPane.prototype._buildPane = function (selectedRows, last, dataIn, init, prevEl) {
  733. var _this = this;
  734. if (selectedRows === void 0) { selectedRows = []; }
  735. if (last === void 0) { last = false; }
  736. if (dataIn === void 0) { dataIn = null; }
  737. if (init === void 0) { init = null; }
  738. if (prevEl === void 0) { prevEl = null; }
  739. // Aliases
  740. this.selections = [];
  741. var table = this.s.dt;
  742. var column = table.column(this.colExists ? this.s.index : 0);
  743. var colOpts = this.s.colOpts;
  744. var rowData = this.s.rowData;
  745. // Other Variables
  746. var countMessage = table.i18n('searchPanes.count', this.c.i18n.count);
  747. var filteredMessage = table.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
  748. var loadedFilter = table.state.loaded();
  749. // If the listeners have not been set yet then using the latest state may result in funny errors
  750. if (this.s.listSet) {
  751. loadedFilter = table.state();
  752. }
  753. // If it is not a custom pane in place
  754. if (this.colExists) {
  755. var idx = -1;
  756. if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
  757. for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
  758. if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
  759. idx = i;
  760. break;
  761. }
  762. }
  763. }
  764. // Perform checks that do not require populate pane to run
  765. if ((colOpts.show === false ||
  766. colOpts.show !== undefined && colOpts.show !== true) &&
  767. idx === -1) {
  768. this.dom.container.addClass(this.classes.hidden);
  769. this.s.displayed = false;
  770. return false;
  771. }
  772. else if (colOpts.show === true || idx !== -1) {
  773. this.s.displayed = true;
  774. }
  775. if (!this.s.dt.page.info().serverSide &&
  776. (dataIn === null ||
  777. dataIn.searchPanes === null ||
  778. dataIn.searchPanes.options === null)) {
  779. // Only run populatePane if the data has not been collected yet
  780. if (rowData.arrayFilter.length === 0) {
  781. this._populatePane(last);
  782. this.s.rowData.totalOptions = 0;
  783. this._detailsPane();
  784. rowData.arrayOriginal = rowData.arrayTotals;
  785. rowData.binsOriginal = rowData.binsTotal;
  786. }
  787. var binLength = Object.keys(rowData.binsOriginal).length;
  788. var uniqueRatio = this._uniqueRatio(binLength, table.rows()[0].length);
  789. // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry
  790. // for that pane
  791. if (this.s.displayed === false &&
  792. ((colOpts.show === undefined && colOpts.threshold === null ?
  793. uniqueRatio > this.c.threshold :
  794. uniqueRatio > colOpts.threshold) ||
  795. colOpts.show !== true && binLength <= 1)) {
  796. this.dom.container.addClass(this.classes.hidden);
  797. this.s.displayed = false;
  798. return;
  799. }
  800. // If the option viewTotal is true then find
  801. // the total count for the whole table to display alongside the displayed count
  802. if (this.c.viewTotal && rowData.arrayTotals.length === 0) {
  803. this.s.rowData.totalOptions = 0;
  804. this._detailsPane();
  805. }
  806. else {
  807. rowData.binsTotal = rowData.bins;
  808. }
  809. this.dom.container.addClass(this.classes.show);
  810. this.s.displayed = true;
  811. }
  812. else if (dataIn !== null && dataIn.searchPanes !== null && dataIn.searchPanes.options !== null) {
  813. if (dataIn.tableLength !== undefined) {
  814. this.s.tableLength = dataIn.tableLength;
  815. this.s.rowData.totalOptions = this.s.tableLength;
  816. }
  817. else if (this.s.tableLength === null || table.rows()[0].length > this.s.tableLength) {
  818. this.s.tableLength = table.rows()[0].length;
  819. this.s.rowData.totalOptions = this.s.tableLength;
  820. }
  821. var colTitle = table.column(this.s.index).dataSrc();
  822. if (dataIn.searchPanes.options[colTitle] !== undefined) {
  823. for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
  824. var dataPoint = _a[_i];
  825. this.s.rowData.arrayFilter.push({
  826. display: dataPoint.label,
  827. filter: dataPoint.value,
  828. sort: dataPoint.label,
  829. type: dataPoint.label
  830. });
  831. this.s.rowData.bins[dataPoint.value] = this.c.viewTotal || this.c.cascadePanes ?
  832. dataPoint.count :
  833. dataPoint.total;
  834. this.s.rowData.binsTotal[dataPoint.value] = dataPoint.total;
  835. }
  836. }
  837. var binLength = Object.keys(rowData.binsTotal).length;
  838. var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
  839. // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
  840. if (this.s.displayed === false &&
  841. ((colOpts.show === undefined && colOpts.threshold === null ?
  842. uniqueRatio > this.c.threshold :
  843. uniqueRatio > colOpts.threshold) ||
  844. colOpts.show !== true && binLength <= 1)) {
  845. this.dom.container.addClass(this.classes.hidden);
  846. this.s.displayed = false;
  847. return;
  848. }
  849. this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
  850. this.s.rowData.binsOriginal = this.s.rowData.bins;
  851. this.s.displayed = true;
  852. }
  853. }
  854. else {
  855. this.s.displayed = true;
  856. }
  857. // If the variance is accceptable then display the search pane
  858. this._displayPane();
  859. if (!this.s.listSet) {
  860. // Here, when the state is loaded if the data object on the original table is empty,
  861. // then a state.clear() must have occurred, so delete all of the panes tables state objects too.
  862. this.dom.dtP.on('stateLoadParams.dt', function (e, settings, data) {
  863. if ($.isEmptyObject(table.state.loaded())) {
  864. $.each(data, function (index, value) {
  865. delete data[index];
  866. });
  867. }
  868. });
  869. }
  870. // Add the container to the document in its original location
  871. if (prevEl !== null && this.dom.panesContainer.has(prevEl).length > 0) {
  872. this.dom.container.insertAfter(prevEl);
  873. }
  874. else {
  875. this.dom.panesContainer.prepend(this.dom.container);
  876. }
  877. // Declare the datatable for the pane
  878. var errMode = $.fn.dataTable.ext.errMode;
  879. $.fn.dataTable.ext.errMode = 'none';
  880. // eslint-disable-next-line no-extra-parens
  881. var haveScroller = dataTable.Scroller;
  882. this.s.dtPane = this.dom.dtP.DataTable($.extend(true, {
  883. columnDefs: [
  884. {
  885. className: 'dtsp-nameColumn',
  886. data: 'display',
  887. render: function (data, type, row) {
  888. if (type === 'sort') {
  889. return row.sort;
  890. }
  891. else if (type === 'type') {
  892. return row.type;
  893. }
  894. var message;
  895. message =
  896. (_this.s.filteringActive || _this.s.showFiltered) && _this.c.viewTotal ||
  897. _this.c.viewTotal && _this.s.forceViewTotal ?
  898. filteredMessage.replace(/{total}/, row.total) :
  899. countMessage.replace(/{total}/, row.total);
  900. message = message.replace(/{shown}/, row.shown);
  901. while (message.includes('{total}')) {
  902. message = message.replace(/{total}/, row.total);
  903. }
  904. while (message.includes('{shown}')) {
  905. message = message.replace(/{shown}/, row.shown);
  906. }
  907. // We are displaying the count in the same columne as the name of the search option.
  908. // This is so that there is not need to call columns.adjust()
  909. // which in turn speeds up the code
  910. var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
  911. if (!_this.c.viewCount || !colOpts.viewCount) {
  912. pill = '';
  913. }
  914. if (type === 'filter') {
  915. return typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
  916. data.replace(/<[^>]*>/g, '') :
  917. data;
  918. }
  919. return '<div class="' + _this.classes.nameCont + '"><span title="' +
  920. (typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
  921. data.replace(/<[^>]*>/g, '') :
  922. data) +
  923. '" class="' + _this.classes.name + '">' +
  924. data + '</span>' +
  925. pill + '</div>';
  926. },
  927. targets: 0,
  928. // Accessing the private datatables property to set type based on the original table.
  929. // This is null if not defined by the user, meaning that automatic type detection
  930. // would take place
  931. type: table.settings()[0].aoColumns[this.s.index] !== undefined ?
  932. table.settings()[0].aoColumns[this.s.index]._sManualType :
  933. null
  934. },
  935. {
  936. className: 'dtsp-countColumn ' + this.classes.badgePill,
  937. data: 'shown',
  938. orderData: [1, 2],
  939. searchable: false,
  940. targets: 1,
  941. visible: false
  942. },
  943. {
  944. data: 'total',
  945. searchable: false,
  946. targets: 2,
  947. visible: false
  948. }
  949. ],
  950. deferRender: true,
  951. dom: 't',
  952. info: false,
  953. language: this.s.dt.settings()[0].oLanguage,
  954. paging: haveScroller ? true : false,
  955. scrollX: false,
  956. scrollY: '200px',
  957. scroller: haveScroller ? true : false,
  958. select: true,
  959. stateSave: table.settings()[0].oFeatures.bStateSave ? true : false
  960. }, this.c.dtOpts, colOpts !== undefined ? colOpts.dtOpts : {}, this.s.colOpts.options !== undefined || !this.colExists ?
  961. {
  962. createdRow: function (row, data, dataIndex) {
  963. $(row).addClass(data.className);
  964. }
  965. } :
  966. undefined, this.customPaneSettings !== null && this.customPaneSettings.dtOpts !== undefined ?
  967. this.customPaneSettings.dtOpts :
  968. {}, $.fn.dataTable.versionCheck('2')
  969. ? {
  970. layout: {
  971. bottomLeft: null,
  972. bottomRight: null,
  973. topLeft: null,
  974. topRight: null
  975. }
  976. }
  977. : {}));
  978. this.dom.dtP.addClass(this.classes.table);
  979. // Getting column titles is a little messy
  980. var headerText = 'Custom Pane';
  981. if (this.customPaneSettings && this.customPaneSettings.header) {
  982. headerText = this.customPaneSettings.header;
  983. }
  984. else if (colOpts.header) {
  985. headerText = colOpts.header;
  986. }
  987. else if (this.colExists) {
  988. headerText = $.fn.dataTable.versionCheck('2')
  989. ? table.column(this.s.index).title()
  990. : table.settings()[0].aoColumns[this.s.index].sTitle;
  991. }
  992. this.dom.searchBox.attr('placeholder', headerText);
  993. // As the pane table is not in the document yet we must initialise select ourselves
  994. // eslint-disable-next-line no-extra-parens
  995. $.fn.dataTable.select.init(this.s.dtPane);
  996. $.fn.dataTable.ext.errMode = errMode;
  997. // If it is not a custom pane
  998. if (this.colExists) {
  999. // On initialisation, do we need to set a filtering value from a
  1000. // saved state or init option?
  1001. var search = column.search();
  1002. search = search ? search.substr(1, search.length - 2).split('|') : [];
  1003. // Count the number of empty cells
  1004. var count_1 = 0;
  1005. rowData.arrayFilter.forEach(function (element) {
  1006. if (element.filter === '') {
  1007. count_1++;
  1008. }
  1009. });
  1010. // Add all of the search options to the pane
  1011. for (var i = 0, ien = rowData.arrayFilter.length; i < ien; i++) {
  1012. var selected = false;
  1013. for (var _b = 0, _c = this.s.serverSelect; _b < _c.length; _b++) {
  1014. var option = _c[_b];
  1015. if (option.filter === rowData.arrayFilter[i].filter) {
  1016. selected = true;
  1017. }
  1018. }
  1019. if (this.s.dt.page.info().serverSide &&
  1020. (!this.c.cascadePanes ||
  1021. this.c.cascadePanes && rowData.bins[rowData.arrayFilter[i].filter] !== 0 ||
  1022. this.c.cascadePanes && init !== null ||
  1023. selected)) {
  1024. var row = this.addRow(rowData.arrayFilter[i].display, rowData.arrayFilter[i].filter, init ?
  1025. rowData.binsTotal[rowData.arrayFilter[i].filter] :
  1026. rowData.bins[rowData.arrayFilter[i].filter], this.c.viewTotal || init
  1027. ? String(rowData.binsTotal[rowData.arrayFilter[i].filter])
  1028. : rowData.bins[rowData.arrayFilter[i].filter], rowData.arrayFilter[i].sort, rowData.arrayFilter[i].type);
  1029. for (var _d = 0, _e = this.s.serverSelect; _d < _e.length; _d++) {
  1030. var option = _e[_d];
  1031. if (option.filter === rowData.arrayFilter[i].filter) {
  1032. this.s.serverSelecting = true;
  1033. row.select();
  1034. this.s.serverSelecting = false;
  1035. }
  1036. }
  1037. }
  1038. else if (!this.s.dt.page.info().serverSide &&
  1039. rowData.arrayFilter[i] &&
  1040. (rowData.bins[rowData.arrayFilter[i].filter] !== undefined || !this.c.cascadePanes)) {
  1041. 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);
  1042. }
  1043. else if (!this.s.dt.page.info().serverSide) {
  1044. // Just pass an empty string as the message will be calculated based on that in addRow()
  1045. this.addRow('', count_1, count_1, '', '', '');
  1046. }
  1047. }
  1048. }
  1049. // eslint-disable-next-line no-extra-parens
  1050. dataTable.select.init(this.s.dtPane);
  1051. // If there are custom options set or it is a custom pane then get them
  1052. if (colOpts.options !== undefined ||
  1053. this.customPaneSettings !== null && this.customPaneSettings.options !== undefined) {
  1054. this._getComparisonRows();
  1055. }
  1056. // Display the pane
  1057. this.s.dtPane.draw();
  1058. this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
  1059. this.adjustTopRow();
  1060. if (!this.s.listSet) {
  1061. this._setListeners();
  1062. this.s.listSet = true;
  1063. }
  1064. for (var _f = 0, selectedRows_1 = selectedRows; _f < selectedRows_1.length; _f++) {
  1065. var selection = selectedRows_1[_f];
  1066. if (selection !== undefined) {
  1067. for (var _g = 0, _h = this.s.dtPane.rows().indexes().toArray(); _g < _h.length; _g++) {
  1068. var row = _h[_g];
  1069. if (this.s.dtPane.row(row).data() !== undefined &&
  1070. selection.filter === this.s.dtPane.row(row).data().filter) {
  1071. // If this is happening when serverSide processing is happening then
  1072. // different behaviour is needed
  1073. if (this.s.dt.page.info().serverSide) {
  1074. this.s.serverSelecting = true;
  1075. this.s.dtPane.row(row).select();
  1076. this.s.serverSelecting = false;
  1077. }
  1078. else {
  1079. this.s.dtPane.row(row).select();
  1080. }
  1081. }
  1082. }
  1083. }
  1084. }
  1085. // If SSP and the table is ready, apply the search for the pane
  1086. if (this.s.dt.page.info().serverSide) {
  1087. this.s.dtPane.search(this.dom.searchBox.val()).draw();
  1088. }
  1089. if ((this.c.initCollapsed && this.s.colOpts.initCollapsed !== false ||
  1090. this.s.colOpts.initCollapsed) &&
  1091. (this.c.collapse && this.s.colOpts.collapse !== false ||
  1092. this.s.colOpts.collapse)) {
  1093. this.collapse();
  1094. }
  1095. // Reload the selection, searchbox entry and ordering from the previous state
  1096. // Need to check here if SSP that this is the first draw, otherwise it will infinite loop
  1097. if (loadedFilter &&
  1098. loadedFilter.searchPanes &&
  1099. loadedFilter.searchPanes.panes &&
  1100. (dataIn === null ||
  1101. dataIn.draw === 1)) {
  1102. if (!this.c.cascadePanes) {
  1103. this._reloadSelect(loadedFilter);
  1104. }
  1105. for (var _j = 0, _k = loadedFilter.searchPanes.panes; _j < _k.length; _j++) {
  1106. var pane = _k[_j];
  1107. if (pane.id === this.s.index) {
  1108. // Save some time by only triggering an input if there is a value
  1109. if (pane.searchTerm && pane.searchTerm.length > 0) {
  1110. this.dom.searchBox.val(pane.searchTerm);
  1111. this.dom.searchBox.trigger('input');
  1112. }
  1113. this.s.dtPane.order(pane.order).draw();
  1114. // Is the pane to be hidden or shown?
  1115. if (pane.collapsed) {
  1116. this.collapse();
  1117. }
  1118. else {
  1119. this.show();
  1120. }
  1121. }
  1122. }
  1123. }
  1124. return true;
  1125. };
  1126. /**
  1127. * Update the array which holds the display and filter values for the table
  1128. */
  1129. SearchPane.prototype._detailsPane = function () {
  1130. var table = this.s.dt;
  1131. this.s.rowData.arrayTotals = [];
  1132. this.s.rowData.binsTotal = {};
  1133. var settings = this.s.dt.settings()[0];
  1134. var indexArray = table.rows().indexes();
  1135. if (!this.s.dt.page.info().serverSide) {
  1136. for (var _i = 0, indexArray_1 = indexArray; _i < indexArray_1.length; _i++) {
  1137. var rowIdx = indexArray_1[_i];
  1138. this._populatePaneArray(rowIdx, this.s.rowData.arrayTotals, settings, this.s.rowData.binsTotal);
  1139. }
  1140. }
  1141. };
  1142. /**
  1143. * Appends all of the HTML elements to their relevant parent Elements
  1144. */
  1145. SearchPane.prototype._displayPane = function () {
  1146. var container = this.dom.container;
  1147. var colOpts = this.s.colOpts;
  1148. var layVal = parseInt(this.c.layout.split('-')[1], 10);
  1149. // Empty everything to start again
  1150. this.dom.topRow.empty();
  1151. this.dom.dtP.empty();
  1152. this.dom.topRow.addClass(this.classes.topRow);
  1153. // If there are more than 3 columns defined then make there be a smaller gap between the panes
  1154. if (layVal > 3) {
  1155. this.dom.container.addClass(this.classes.smallGap);
  1156. }
  1157. this.dom.topRow.addClass(this.classes.subRowsContainer);
  1158. this.dom.upper.appendTo(this.dom.topRow);
  1159. this.dom.lower.appendTo(this.dom.topRow);
  1160. this.dom.searchCont.appendTo(this.dom.upper);
  1161. this.dom.buttonGroup.appendTo(this.dom.lower);
  1162. // If no selections have been made in the pane then disable the clear button
  1163. if (this.c.dtOpts.searching === false ||
  1164. colOpts.dtOpts !== undefined && colOpts.dtOpts.searching === false ||
  1165. (!this.c.controls || !colOpts.controls) ||
  1166. this.customPaneSettings !== null &&
  1167. this.customPaneSettings.dtOpts !== undefined &&
  1168. this.customPaneSettings.dtOpts.searching !== undefined &&
  1169. !this.customPaneSettings.dtOpts.searching) {
  1170. this.dom.searchBox
  1171. .removeClass(this.classes.paneInputButton)
  1172. .addClass(this.classes.disabledButton)
  1173. .attr('disabled', 'true');
  1174. }
  1175. this.dom.searchBox.appendTo(this.dom.searchCont);
  1176. // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
  1177. this._searchContSetup();
  1178. // If the clear button is allowed to show then display it
  1179. if (this.c.clear && this.c.controls && colOpts.controls) {
  1180. this.dom.clear.appendTo(this.dom.buttonGroup);
  1181. }
  1182. if (this.c.orderable && colOpts.orderable && this.c.controls && colOpts.controls) {
  1183. this.dom.nameButton.appendTo(this.dom.buttonGroup);
  1184. }
  1185. // If the count column is hidden then don't display the ordering button for it
  1186. if (this.c.viewCount &&
  1187. colOpts.viewCount &&
  1188. this.c.orderable &&
  1189. colOpts.orderable &&
  1190. this.c.controls &&
  1191. colOpts.controls) {
  1192. this.dom.countButton.appendTo(this.dom.buttonGroup);
  1193. }
  1194. if ((this.c.collapse && this.s.colOpts.collapse !== false ||
  1195. this.s.colOpts.collapse) &&
  1196. this.c.controls && colOpts.controls) {
  1197. this.dom.collapseButton.appendTo(this.dom.buttonGroup);
  1198. }
  1199. this.dom.topRow.prependTo(this.dom.container);
  1200. container.append(this.dom.dtP);
  1201. container.show();
  1202. };
  1203. /**
  1204. * Gets the options for the row for the customPanes
  1205. *
  1206. * @returns {object} The options for the row extended to include the options from the user.
  1207. */
  1208. SearchPane.prototype._getBonusOptions = function () {
  1209. // We need to reset the thresholds as if they have a value in colOpts then that value will be used
  1210. var defaultMutator = {
  1211. orthogonal: {
  1212. threshold: null
  1213. },
  1214. threshold: null
  1215. };
  1216. return $.extend(true, {}, SearchPane.defaults, defaultMutator, this.c !== undefined ? this.c : {});
  1217. };
  1218. /**
  1219. * Adds the custom options to the pane
  1220. *
  1221. * @returns {Array} Returns the array of rows which have been added to the pane
  1222. */
  1223. SearchPane.prototype._getComparisonRows = function () {
  1224. var colOpts = this.s.colOpts;
  1225. // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
  1226. var options = colOpts.options !== undefined
  1227. ? colOpts.options
  1228. : this.customPaneSettings !== null && this.customPaneSettings.options !== undefined
  1229. ? this.customPaneSettings.options
  1230. : undefined;
  1231. if (options === undefined) {
  1232. return;
  1233. }
  1234. var tableVals = this.s.dt.rows({ search: 'applied' }).data().toArray();
  1235. var appRows = this.s.dt.rows({ search: 'applied' });
  1236. var tableValsTotal = this.s.dt.rows().data().toArray();
  1237. var allRows = this.s.dt.rows();
  1238. var rows = [];
  1239. // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
  1240. this.s.dtPane.clear();
  1241. for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
  1242. var comp = options_1[_i];
  1243. // Initialise the object which is to be placed in the row
  1244. var insert = comp.label !== '' ?
  1245. comp.label :
  1246. this.emptyMessage();
  1247. var comparisonObj = {
  1248. className: comp.className,
  1249. display: insert,
  1250. filter: typeof comp.value === 'function' ? comp.value : [],
  1251. shown: 0,
  1252. sort: insert,
  1253. total: 0,
  1254. type: insert
  1255. };
  1256. // If a custom function is in place
  1257. if (typeof comp.value === 'function') {
  1258. // Count the number of times the function evaluates to true for the data currently being displayed
  1259. for (var tVal = 0; tVal < tableVals.length; tVal++) {
  1260. if (comp.value.call(this.s.dt, tableVals[tVal], appRows[0][tVal])) {
  1261. comparisonObj.shown++;
  1262. }
  1263. }
  1264. // Count the number of times the function evaluates to true for the original data in the Table
  1265. for (var i = 0; i < tableValsTotal.length; i++) {
  1266. if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
  1267. comparisonObj.total++;
  1268. }
  1269. }
  1270. // Update the comparisonObj
  1271. if (typeof comparisonObj.filter !== 'function') {
  1272. comparisonObj.filter.push(comp.filter);
  1273. }
  1274. }
  1275. // If cascadePanes is not active or if it is and the comparisonObj should be shown then add it to the pane
  1276. if (!this.c.cascadePanes || this.c.cascadePanes && comparisonObj.shown !== 0) {
  1277. rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.shown, comparisonObj.total, comparisonObj.sort, comparisonObj.type, comparisonObj.className));
  1278. }
  1279. }
  1280. return rows;
  1281. };
  1282. /**
  1283. * Gets the options for the row for the customPanes
  1284. *
  1285. * @returns {object} The options for the row extended to include the options from the user.
  1286. */
  1287. SearchPane.prototype._getOptions = function () {
  1288. var table = this.s.dt;
  1289. // We need to reset the thresholds as if they have a value in colOpts then that value will be used
  1290. var defaultMutator = {
  1291. collapse: null,
  1292. emptyMessage: false,
  1293. initCollapsed: null,
  1294. orthogonal: {
  1295. threshold: null
  1296. },
  1297. threshold: null
  1298. };
  1299. var columnOptions = table.settings()[0].aoColumns[this.s.index].searchPanes;
  1300. var colOpts = $.extend(true, {}, SearchPane.defaults, defaultMutator, columnOptions);
  1301. if (columnOptions !== undefined &&
  1302. columnOptions.hideCount !== undefined &&
  1303. columnOptions.viewCount === undefined) {
  1304. colOpts.viewCount = !columnOptions.hideCount;
  1305. }
  1306. return colOpts;
  1307. };
  1308. /**
  1309. * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
  1310. *
  1311. * @param select Denotes whether a selection has been made or not
  1312. */
  1313. SearchPane.prototype._makeSelection = function () {
  1314. this.updateTable();
  1315. this.s.updating = true;
  1316. this.s.dt.draw();
  1317. this.s.updating = false;
  1318. };
  1319. /**
  1320. * Fill the array with the values that are currently being displayed in the table
  1321. *
  1322. * @param last boolean to indicate whether this was the last pane a selection was made in
  1323. */
  1324. SearchPane.prototype._populatePane = function (last) {
  1325. if (last === void 0) { last = false; }
  1326. var table = this.s.dt;
  1327. this.s.rowData.arrayFilter = [];
  1328. this.s.rowData.bins = {};
  1329. var settings = this.s.dt.settings()[0];
  1330. // If cascadePanes or viewTotal are active it is necessary to get the data which is currently
  1331. // being displayed for their functionality.
  1332. // Also make sure that this was not the last pane to have a selection made
  1333. if (!this.s.dt.page.info().serverSide) {
  1334. var indexArray = (this.c.cascadePanes || this.c.viewTotal) && (!this.s.clearing && !last) ?
  1335. table.rows({ search: 'applied' }).indexes() :
  1336. table.rows().indexes();
  1337. for (var _i = 0, _a = indexArray.toArray(); _i < _a.length; _i++) {
  1338. var index = _a[_i];
  1339. this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
  1340. }
  1341. }
  1342. };
  1343. /**
  1344. * Populates an array with all of the data for the table
  1345. *
  1346. * @param rowIdx The current row index to be compared
  1347. * @param arrayFilter The array that is to be populated with row Details
  1348. * @param bins The bins object that is to be populated with the row counts
  1349. */
  1350. SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
  1351. if (bins === void 0) { bins = this.s.rowData.bins; }
  1352. var colOpts = this.s.colOpts;
  1353. // Retrieve the rendered data from the cell using the fnGetCellData function
  1354. // rather than the cell().render API method for optimisation
  1355. if (typeof colOpts.orthogonal === 'string') {
  1356. var rendered = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal);
  1357. this.s.rowData.filterMap.set(rowIdx, rendered);
  1358. this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
  1359. }
  1360. else {
  1361. var filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.search);
  1362. // Null and empty string are to be considered the same value
  1363. if (filter === null) {
  1364. filter = '';
  1365. }
  1366. if (typeof filter === 'string') {
  1367. filter = filter.replace(/<[^>]*>/g, '');
  1368. }
  1369. this.s.rowData.filterMap.set(rowIdx, filter);
  1370. if (!bins[filter]) {
  1371. bins[filter] = 1;
  1372. 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);
  1373. this.s.rowData.totalOptions++;
  1374. }
  1375. else {
  1376. bins[filter]++;
  1377. this.s.rowData.totalOptions++;
  1378. return;
  1379. }
  1380. }
  1381. };
  1382. /**
  1383. * Reloads all of the previous selects into the panes
  1384. *
  1385. * @param loadedFilter The loaded filters from a previous state
  1386. */
  1387. SearchPane.prototype._reloadSelect = function (loadedFilter) {
  1388. // If the state was not saved don't selected any
  1389. if (loadedFilter === undefined) {
  1390. return;
  1391. }
  1392. var idx;
  1393. // For each pane, check that the loadedFilter list exists and is not null,
  1394. // find the id of each search item and set it to be selected.
  1395. for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
  1396. if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
  1397. idx = i;
  1398. break;
  1399. }
  1400. }
  1401. if (idx !== undefined) {
  1402. var table = this.s.dtPane;
  1403. var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
  1404. item.filter.toString() :
  1405. null; }).toArray();
  1406. for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
  1407. var filter = _a[_i];
  1408. var id = -1;
  1409. if (filter !== null) {
  1410. id = rows.indexOf(filter.toString());
  1411. }
  1412. if (id > -1) {
  1413. this.s.serverSelecting = true;
  1414. table.row(id).select();
  1415. this.s.serverSelecting = false;
  1416. }
  1417. }
  1418. }
  1419. };
  1420. /**
  1421. * This method decides whether a row should contribute to the pane or not
  1422. *
  1423. * @param filter the value that the row is to be filtered on
  1424. * @param dataIndex the row index
  1425. */
  1426. SearchPane.prototype._search = function (filter, dataIndex) {
  1427. var colOpts = this.s.colOpts;
  1428. var table = this.s.dt;
  1429. // For each item selected in the pane, check if it is available in the cell
  1430. for (var _i = 0, _a = this.selections; _i < _a.length; _i++) {
  1431. var colSelect = _a[_i];
  1432. if (typeof colSelect.filter === 'string' && typeof filter === 'string') {
  1433. // The filter value will not have the &amp; in place but a &,
  1434. // so we need to do a replace to make sure that they will match
  1435. colSelect.filter = colSelect.filter
  1436. .replace(/&amp;/g, '&')
  1437. .replace(/&lt;/g, '<')
  1438. .replace(/&gt;/g, '>')
  1439. .replace(/&quot;/g, '"');
  1440. }
  1441. // if the filter is an array then is the column present in it
  1442. if (Array.isArray(filter)) {
  1443. if (filter.includes(colSelect.filter)) {
  1444. return true;
  1445. }
  1446. }
  1447. // if the filter is a function then does it meet the criteria of that function or not
  1448. else if (typeof colSelect.filter === 'function') {
  1449. if (colSelect.filter.call(table, table.row(dataIndex).data(), dataIndex)) {
  1450. if (colOpts.combiner === 'or') {
  1451. return true;
  1452. }
  1453. }
  1454. // If the combiner is an "and" then we need to check against all possible selections
  1455. // so if it fails here then the and is not met and return false
  1456. else if (colOpts.combiner === 'and') {
  1457. return false;
  1458. }
  1459. }
  1460. // otherwise if the two filter values are equal then return true
  1461. else if (filter === colSelect.filter ||
  1462. // Loose type checking incase number type in column comparing to a string
  1463. // eslint-disable-next-line eqeqeq
  1464. !(typeof filter === 'string' && filter.length === 0) && filter == colSelect.filter ||
  1465. colSelect.filter === null && typeof filter === 'string' && filter === '') {
  1466. return true;
  1467. }
  1468. }
  1469. // If the combiner is an and then we need to check against all possible selections
  1470. // so return true here if so because it would have returned false earlier if it had failed
  1471. if (colOpts.combiner === 'and') {
  1472. return true;
  1473. }
  1474. // Otherwise it hasn't matched with anything by this point so it must be false
  1475. else {
  1476. return false;
  1477. }
  1478. };
  1479. /**
  1480. * Creates the contents of the searchCont div
  1481. *
  1482. * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
  1483. */
  1484. SearchPane.prototype._searchContSetup = function () {
  1485. if (this.c.controls && this.s.colOpts.controls) {
  1486. this.dom.searchButton.appendTo(this.dom.searchLabelCont);
  1487. }
  1488. if (!(this.c.dtOpts.searching === false ||
  1489. this.s.colOpts.dtOpts.searching === false ||
  1490. this.customPaneSettings !== null &&
  1491. this.customPaneSettings.dtOpts !== undefined &&
  1492. this.customPaneSettings.dtOpts.searching !== undefined &&
  1493. !this.customPaneSettings.dtOpts.searching)) {
  1494. this.dom.searchLabelCont.appendTo(this.dom.searchCont);
  1495. }
  1496. };
  1497. /**
  1498. * Adds outline to the pane when a selection has been made
  1499. */
  1500. SearchPane.prototype._searchExtras = function () {
  1501. var updating = this.s.updating;
  1502. this.s.updating = true;
  1503. var filters = this.s.dtPane.rows({ selected: true }).data().pluck('filter').toArray();
  1504. var nullIndex = filters.indexOf(this.emptyMessage());
  1505. var container = $(this.s.dtPane.table().container());
  1506. // If null index is found then search for empty cells as a filter.
  1507. if (nullIndex > -1) {
  1508. filters[nullIndex] = '';
  1509. }
  1510. // If a filter has been applied then outline the respective pane, remove it when it no longer is.
  1511. if (filters.length > 0) {
  1512. container.addClass(this.classes.selected);
  1513. }
  1514. else if (filters.length === 0) {
  1515. container.removeClass(this.classes.selected);
  1516. }
  1517. this.s.updating = updating;
  1518. };
  1519. /**
  1520. * Finds the ratio of the number of different options in the table to the number of rows
  1521. *
  1522. * @param bins the number of different options in the table
  1523. * @param rowCount the total number of rows in the table
  1524. * @returns {number} returns the ratio
  1525. */
  1526. SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
  1527. if (rowCount > 0 &&
  1528. (this.s.rowData.totalOptions > 0 && !this.s.dt.page.info().serverSide ||
  1529. this.s.dt.page.info().serverSide && this.s.tableLength > 0)) {
  1530. return bins / this.s.rowData.totalOptions;
  1531. }
  1532. else {
  1533. return 1;
  1534. }
  1535. };
  1536. /**
  1537. * updates the options within the pane
  1538. *
  1539. * @param draw a flag to define whether this has been called due to a draw event or not
  1540. */
  1541. SearchPane.prototype._updateCommon = function (draw) {
  1542. if (draw === void 0) { draw = false; }
  1543. // Update the panes if doing a deselect. if doing a select then
  1544. // update all of the panes except for the one causing the change
  1545. if (!this.s.dt.page.info().serverSide &&
  1546. this.s.dtPane !== undefined &&
  1547. (!this.s.filteringActive || this.c.cascadePanes || draw === true) &&
  1548. (this.c.cascadePanes !== true || this.s.selectPresent !== true) &&
  1549. (!this.s.lastSelect || !this.s.lastCascade)) {
  1550. var colOpts = this.s.colOpts;
  1551. var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
  1552. var rowData = this.s.rowData;
  1553. // Clear the pane in preparation for adding the updated search options
  1554. this.s.dtPane.clear();
  1555. // If it is not a custom pane
  1556. if (this.colExists) {
  1557. // Only run populatePane if the data has not been collected yet
  1558. if (rowData.arrayFilter.length === 0) {
  1559. this._populatePane(!this.s.filteringActive);
  1560. }
  1561. // If cascadePanes is active and the table has returned to its default state then
  1562. // there is a need to update certain parts ofthe rowData.
  1563. else if (this.c.cascadePanes &&
  1564. this.s.dt.rows().data().toArray().length ===
  1565. this.s.dt.rows({ search: 'applied' }).data().toArray().length) {
  1566. rowData.arrayFilter = rowData.arrayOriginal;
  1567. rowData.bins = rowData.binsOriginal;
  1568. }
  1569. // Otherwise if viewTotal or cascadePanes is active then the data from the table must be read.
  1570. else if (this.c.viewTotal || this.c.cascadePanes) {
  1571. this._populatePane(!this.s.filteringActive);
  1572. }
  1573. // If the viewTotal option is selected then find the totals for the table
  1574. if (this.c.viewTotal) {
  1575. this._detailsPane();
  1576. }
  1577. else {
  1578. rowData.binsTotal = rowData.bins;
  1579. }
  1580. if (this.c.viewTotal && !this.c.cascadePanes) {
  1581. rowData.arrayFilter = rowData.arrayTotals;
  1582. }
  1583. var _loop_1 = function (dataP) {
  1584. // If both view Total and cascadePanes have been selected and the count of the row
  1585. // is not 0 then add it to pane
  1586. // Do this also if the viewTotal option has been selected and cascadePanes has not
  1587. if (dataP &&
  1588. (rowData.bins[dataP.filter] !== undefined &&
  1589. rowData.bins[dataP.filter] !== 0 &&
  1590. this_1.c.cascadePanes ||
  1591. !this_1.c.cascadePanes ||
  1592. this_1.s.clearing)) {
  1593. var row = this_1.addRow(dataP.display, dataP.filter, !this_1.c.viewTotal ?
  1594. rowData.bins[dataP.filter] :
  1595. rowData.bins[dataP.filter] !== undefined ?
  1596. rowData.bins[dataP.filter] :
  1597. 0, this_1.c.viewTotal ?
  1598. String(rowData.binsTotal[dataP.filter]) :
  1599. rowData.bins[dataP.filter], dataP.sort, dataP.type);
  1600. // Find out if the filter was selected in the previous search,
  1601. // if so select it and remove from array.
  1602. var selectIndex = selected.findIndex(function (element) {
  1603. return element.filter === dataP.filter;
  1604. });
  1605. if (selectIndex !== -1) {
  1606. row.select();
  1607. selected.splice(selectIndex, 1);
  1608. }
  1609. }
  1610. };
  1611. var this_1 = this;
  1612. for (var _i = 0, _a = rowData.arrayFilter; _i < _a.length; _i++) {
  1613. var dataP = _a[_i];
  1614. _loop_1(dataP);
  1615. }
  1616. }
  1617. if (colOpts.searchPanes !== undefined && colOpts.searchPanes.options !== undefined ||
  1618. colOpts.options !== undefined ||
  1619. this.customPaneSettings !== null && this.customPaneSettings.options !== undefined) {
  1620. var rows = this._getComparisonRows();
  1621. var _loop_2 = function (row) {
  1622. var selectIndex = selected.findIndex(function (element) {
  1623. if (element.display === row.data().display) {
  1624. return true;
  1625. }
  1626. });
  1627. if (selectIndex !== -1) {
  1628. row.select();
  1629. selected.splice(selectIndex, 1);
  1630. }
  1631. };
  1632. for (var _b = 0, rows_1 = rows; _b < rows_1.length; _b++) {
  1633. var row = rows_1[_b];
  1634. _loop_2(row);
  1635. }
  1636. }
  1637. // Add search options which were previously selected but whos results are no
  1638. // longer present in the resulting data set.
  1639. for (var _c = 0, selected_1 = selected; _c < selected_1.length; _c++) {
  1640. var selectedEl = selected_1[_c];
  1641. var row = this.addRow(selectedEl.display, selectedEl.filter, 0, this.c.viewTotal
  1642. ? selectedEl.total
  1643. : 0, selectedEl.display, selectedEl.display);
  1644. this.s.updating = true;
  1645. row.select();
  1646. this.s.updating = false;
  1647. }
  1648. this.s.dtPane.draw();
  1649. this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
  1650. }
  1651. };
  1652. SearchPane.version = '1.3.0';
  1653. SearchPane.classes = {
  1654. bordered: 'dtsp-bordered',
  1655. buttonGroup: 'dtsp-buttonGroup',
  1656. buttonSub: 'dtsp-buttonSub',
  1657. clear: 'dtsp-clear',
  1658. clearAll: 'dtsp-clearAll',
  1659. clearButton: 'clearButton',
  1660. collapseAll: 'dtsp-collapseAll',
  1661. collapseButton: 'dtsp-collapseButton',
  1662. container: 'dtsp-searchPane',
  1663. countButton: 'dtsp-countButton',
  1664. disabledButton: 'dtsp-disabledButton',
  1665. hidden: 'dtsp-hidden',
  1666. hide: 'dtsp-hide',
  1667. layout: 'dtsp-',
  1668. name: 'dtsp-name',
  1669. nameButton: 'dtsp-nameButton',
  1670. nameCont: 'dtsp-nameCont',
  1671. narrow: 'dtsp-narrow',
  1672. paneButton: 'dtsp-paneButton',
  1673. paneInputButton: 'dtsp-paneInputButton',
  1674. pill: 'dtsp-pill',
  1675. rotated: 'dtsp-rotated',
  1676. search: 'dtsp-search',
  1677. searchCont: 'dtsp-searchCont',
  1678. searchIcon: 'dtsp-searchIcon',
  1679. searchLabelCont: 'dtsp-searchButtonCont',
  1680. selected: 'dtsp-selected',
  1681. smallGap: 'dtsp-smallGap',
  1682. subRow1: 'dtsp-subRow1',
  1683. subRow2: 'dtsp-subRow2',
  1684. subRowsContainer: 'dtsp-subRowsContainer',
  1685. title: 'dtsp-title',
  1686. topRow: 'dtsp-topRow'
  1687. };
  1688. // Define SearchPanes default options
  1689. SearchPane.defaults = {
  1690. cascadePanes: false,
  1691. clear: true,
  1692. collapse: true,
  1693. combiner: 'or',
  1694. container: function (dt) {
  1695. return dt.table().container();
  1696. },
  1697. controls: true,
  1698. dtOpts: {},
  1699. emptyMessage: null,
  1700. hideCount: false,
  1701. i18n: {
  1702. clearPane: '&times;',
  1703. count: '{total}',
  1704. countFiltered: '{shown} ({total})',
  1705. emptyMessage: '<em>No data</em>'
  1706. },
  1707. initCollapsed: false,
  1708. layout: 'auto',
  1709. name: undefined,
  1710. orderable: true,
  1711. orthogonal: {
  1712. display: 'display',
  1713. filter: 'filter',
  1714. hideCount: false,
  1715. search: 'filter',
  1716. show: undefined,
  1717. sort: 'sort',
  1718. threshold: 0.6,
  1719. type: 'type',
  1720. viewCount: true
  1721. },
  1722. preSelect: [],
  1723. threshold: 0.6,
  1724. viewCount: true,
  1725. viewTotal: false
  1726. };
  1727. return SearchPane;
  1728. }());
  1729. var $$1;
  1730. var dataTable$1;
  1731. function setJQuery$1(jq) {
  1732. $$1 = jq;
  1733. dataTable$1 = jq.fn.dataTable;
  1734. }
  1735. var SearchPanes = /** @class */ (function () {
  1736. function SearchPanes(paneSettings, opts, fromInit) {
  1737. var _this = this;
  1738. if (fromInit === void 0) { fromInit = false; }
  1739. this.regenerating = false;
  1740. // Check that the required version of DataTables is included
  1741. if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
  1742. throw new Error('SearchPane requires DataTables 1.10 or newer');
  1743. }
  1744. // Check that Select is included
  1745. // eslint-disable-next-line no-extra-parens
  1746. if (!dataTable$1.select) {
  1747. throw new Error('SearchPane requires Select');
  1748. }
  1749. var table = new dataTable$1.Api(paneSettings);
  1750. this.classes = $$1.extend(true, {}, SearchPanes.classes);
  1751. // Get options from user
  1752. this.c = $$1.extend(true, {}, SearchPanes.defaults, opts);
  1753. // Add extra elements to DOM object including clear
  1754. this.dom = {
  1755. clearAll: $$1('<button type="button">Clear All</button>').addClass(this.classes.clearAll),
  1756. collapseAll: $$1('<button type="button">Collapse All</button>').addClass(this.classes.collapseAll),
  1757. container: $$1('<div/>').addClass(this.classes.panes).text(table.i18n('searchPanes.loadMessage', this.c.i18n.loadMessage)),
  1758. emptyMessage: $$1('<div/>').addClass(this.classes.emptyMessage),
  1759. options: $$1('<div/>').addClass(this.classes.container),
  1760. panes: $$1('<div/>').addClass(this.classes.container),
  1761. showAll: $$1('<button type="button">Show All</button>')
  1762. .addClass(this.classes.showAll)
  1763. .addClass(this.classes.disabledButton)
  1764. .attr('disabled', 'true'),
  1765. title: $$1('<div/>').addClass(this.classes.title),
  1766. titleRow: $$1('<div/>').addClass(this.classes.titleRow),
  1767. wrapper: $$1('<div/>')
  1768. };
  1769. this.s = {
  1770. colOpts: [],
  1771. dt: table,
  1772. filterCount: 0,
  1773. filterPane: -1,
  1774. page: 0,
  1775. paging: false,
  1776. panes: [],
  1777. selectionList: [],
  1778. serverData: {},
  1779. stateRead: false,
  1780. updating: false
  1781. };
  1782. if (table.settings()[0]._searchPanes !== undefined) {
  1783. return;
  1784. }
  1785. this._getState();
  1786. if (this.s.dt.page.info().serverSide) {
  1787. table.on('preXhr.dt', function (e, settings, data) {
  1788. if (data.searchPanes === undefined) {
  1789. data.searchPanes = {};
  1790. }
  1791. if (data.searchPanes_null === undefined) {
  1792. data.searchPanes_null = {};
  1793. }
  1794. for (var _i = 0, _a = _this.s.selectionList; _i < _a.length; _i++) {
  1795. var selection = _a[_i];
  1796. var src = _this.s.dt.column(selection.index).dataSrc();
  1797. if (data.searchPanes[src] === undefined) {
  1798. data.searchPanes[src] = {};
  1799. }
  1800. if (data.searchPanes_null[src] === undefined) {
  1801. data.searchPanes_null[src] = {};
  1802. }
  1803. for (var i = 0; i < selection.rows.length; i++) {
  1804. data.searchPanes[src][i] = selection.rows[i].filter;
  1805. if (data.searchPanes[src][i] === null) {
  1806. data.searchPanes_null[src][i] = true;
  1807. }
  1808. }
  1809. }
  1810. });
  1811. }
  1812. // We are using the xhr event to rebuild the panes if required due to viewTotal being enabled
  1813. // If viewTotal is not enabled then we simply update the data from the server
  1814. table.on('xhr', function (e, settings, json, xhr) {
  1815. if (json && json.searchPanes && json.searchPanes.options) {
  1816. _this.s.serverData = json;
  1817. _this.s.serverData.tableLength = json.recordsTotal;
  1818. _this._serverTotals();
  1819. }
  1820. });
  1821. table.settings()[0]._searchPanes = this;
  1822. this.dom.clearAll.text(table.i18n('searchPanes.clearMessage', this.c.i18n.clearMessage));
  1823. this.dom.collapseAll.text(table.i18n('searchPanes.collapseMessage', this.c.i18n.collapseMessage));
  1824. this.dom.showAll.text(table.i18n('searchPanes.showMessage', this.c.i18n.showMessage));
  1825. if (this.s.dt.settings()[0]._bInitComplete || fromInit) {
  1826. this._paneDeclare(table, paneSettings, opts);
  1827. }
  1828. else {
  1829. table.one('preInit.dt', function (settings) {
  1830. _this._paneDeclare(table, paneSettings, opts);
  1831. });
  1832. }
  1833. return this;
  1834. }
  1835. /**
  1836. * Clear the selections of all of the panes
  1837. */
  1838. SearchPanes.prototype.clearSelections = function () {
  1839. // Load in all of the searchBoxes in the documents
  1840. var searches = this.dom.container.find('.' + this.classes.search.replace(/\s+/g, '.'));
  1841. // For each searchBox set the input text to be empty and then trigger
  1842. // an input on them so that they no longer filter the panes
  1843. searches.each(function () {
  1844. $$1(this).val('');
  1845. $$1(this).trigger('input');
  1846. });
  1847. var returnArray = [];
  1848. // Clear the selectionList to prevent cascadePanes from reselecting rows
  1849. this.s.selectionList = [];
  1850. // For every pane, clear the selections in the pane
  1851. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  1852. var pane = _a[_i];
  1853. if (pane.s.dtPane !== undefined) {
  1854. returnArray.push(pane.clearPane());
  1855. }
  1856. }
  1857. return returnArray;
  1858. };
  1859. /**
  1860. * returns the container node for the searchPanes
  1861. */
  1862. SearchPanes.prototype.getNode = function () {
  1863. return this.dom.container;
  1864. };
  1865. /**
  1866. * rebuilds all of the panes
  1867. */
  1868. SearchPanes.prototype.rebuild = function (targetIdx, maintainSelection) {
  1869. if (targetIdx === void 0) { targetIdx = false; }
  1870. if (maintainSelection === void 0) { maintainSelection = false; }
  1871. this.dom.emptyMessage.remove();
  1872. // As a rebuild from scratch is required, empty the searchpanes container.
  1873. var returnArray = [];
  1874. // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
  1875. if (targetIdx === false) {
  1876. this.dom.panes.empty();
  1877. }
  1878. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  1879. var pane = _a[_i];
  1880. if (targetIdx !== false && pane.s.index !== targetIdx) {
  1881. continue;
  1882. }
  1883. pane.clearData();
  1884. returnArray.push(
  1885. // Pass a boolean to say whether this is the last choice made for maintaining selections when rebuilding
  1886. pane.rebuildPane(this.s.selectionList[this.s.selectionList.length - 1] !== undefined ?
  1887. pane.s.index === this.s.selectionList[this.s.selectionList.length - 1].index :
  1888. false, this.s.dt.page.info().serverSide ?
  1889. this.s.serverData :
  1890. undefined, null, maintainSelection));
  1891. this.dom.panes.append(pane.dom.container);
  1892. }
  1893. if (this.c.cascadePanes || this.c.viewTotal) {
  1894. this.redrawPanes(true);
  1895. }
  1896. else {
  1897. this._updateSelection();
  1898. }
  1899. // Attach panes, clear buttons, and title bar to the document
  1900. this._updateFilterCount();
  1901. this._attachPaneContainer();
  1902. // If the selections are to be maintained, then it is safe to assume that paging is also to be maintained
  1903. // Otherwise, the paging should be reset
  1904. this.s.dt.draw(!maintainSelection);
  1905. // Resize the panes incase there has been a change
  1906. this.resizePanes();
  1907. // If a single pane has been rebuilt then return only that pane
  1908. if (returnArray.length === 1) {
  1909. return returnArray[0];
  1910. }
  1911. // Otherwise return all of the panes that have been rebuilt
  1912. else {
  1913. return returnArray;
  1914. }
  1915. };
  1916. /**
  1917. * Redraws all of the panes
  1918. */
  1919. SearchPanes.prototype.redrawPanes = function (rebuild) {
  1920. if (rebuild === void 0) { rebuild = false; }
  1921. var table = this.s.dt;
  1922. // Only do this if the redraw isn't being triggered by the panes updating themselves
  1923. if (!this.s.updating && !this.s.dt.page.info().serverSide) {
  1924. var filterActive = true;
  1925. var filterPane = this.s.filterPane;
  1926. var selectTotal = null;
  1927. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  1928. var pane = _a[_i];
  1929. if (pane.s.dtPane !== undefined) {
  1930. selectTotal += pane.s.dtPane.rows({ selected: true }).data().toArray().length;
  1931. }
  1932. }
  1933. // If the number of rows currently visible is equal to the number of rows in the table
  1934. // then there can't be any filtering taking place
  1935. if (selectTotal === 0 &&
  1936. table.rows({ search: 'applied' }).data().toArray().length === table.rows().data().toArray().length) {
  1937. filterActive = false;
  1938. }
  1939. // Otherwise if viewTotal is active then it is necessary to determine which panes a select is present in.
  1940. // If there is only one pane with a selection present then it should not show the filtered message as
  1941. // more selections may be made in that pane.
  1942. else if (this.c.viewTotal) {
  1943. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  1944. var pane = _c[_b];
  1945. if (pane.s.dtPane !== undefined) {
  1946. var selectLength = pane.s.dtPane.rows({ selected: true }).data().toArray().length;
  1947. if (selectLength === 0) {
  1948. for (var _d = 0, _e = this.s.selectionList; _d < _e.length; _d++) {
  1949. var selection = _e[_d];
  1950. if (selection.index === pane.s.index && selection.rows.length !== 0) {
  1951. selectLength = selection.rows.length;
  1952. }
  1953. }
  1954. }
  1955. // If filterPane === -1 then a pane with a selection has not been found yet,
  1956. // so set filterPane to that panes index
  1957. if (selectLength > 0 && filterPane === -1) {
  1958. filterPane = pane.s.index;
  1959. }
  1960. // Then if another pane is found with a selection then set filterPane to null to
  1961. // show that multiple panes have selections present
  1962. else if (selectLength > 0) {
  1963. filterPane = null;
  1964. }
  1965. }
  1966. }
  1967. // If the searchbox is in place and filtering is applied then need to cascade down anyway
  1968. if (selectTotal === 0) {
  1969. filterPane = null;
  1970. }
  1971. }
  1972. var deselectIdx = void 0;
  1973. var newSelectionList = [];
  1974. // Don't run this if it is due to the panes regenerating
  1975. if (!this.regenerating) {
  1976. for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
  1977. var pane = _g[_f];
  1978. // Identify the pane where a selection or deselection has been made and add it to the list.
  1979. if (pane.s.selectPresent) {
  1980. this.s.selectionList.push({
  1981. index: pane.s.index,
  1982. protect: false,
  1983. rows: pane.s.dtPane.rows({ selected: true }).data().toArray()
  1984. });
  1985. break;
  1986. }
  1987. else if (pane.s.deselect) {
  1988. deselectIdx = pane.s.index;
  1989. var selectedData = pane.s.dtPane.rows({ selected: true }).data().toArray();
  1990. if (selectedData.length > 0) {
  1991. this.s.selectionList.push({
  1992. index: pane.s.index,
  1993. protect: true,
  1994. rows: selectedData
  1995. });
  1996. }
  1997. }
  1998. }
  1999. if (this.s.selectionList.length > 0) {
  2000. var last = this.s.selectionList[this.s.selectionList.length - 1].index;
  2001. for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
  2002. var pane = _j[_h];
  2003. pane.s.lastSelect = pane.s.index === last;
  2004. }
  2005. }
  2006. // Remove selections from the list from the pane where a deselect has taken place
  2007. for (var i = 0; i < this.s.selectionList.length; i++) {
  2008. if (this.s.selectionList[i].index !== deselectIdx || this.s.selectionList[i].protect === true) {
  2009. var further = false;
  2010. // Find out if this selection is the last one in the list for that pane
  2011. for (var j = i + 1; j < this.s.selectionList.length; j++) {
  2012. if (this.s.selectionList[j].index === this.s.selectionList[i].index) {
  2013. further = true;
  2014. }
  2015. }
  2016. // If there are no selections for this pane in the list then just push this one
  2017. if (!further) {
  2018. newSelectionList.push(this.s.selectionList[i]);
  2019. this.s.selectionList[i].protect = false;
  2020. }
  2021. }
  2022. }
  2023. var solePane = -1;
  2024. if (newSelectionList.length === 1 && selectTotal !== null && selectTotal !== 0) {
  2025. solePane = newSelectionList[0].index;
  2026. }
  2027. // Update all of the panes to reflect the current state of the filters
  2028. for (var _k = 0, _l = this.s.panes; _k < _l.length; _k++) {
  2029. var pane = _l[_k];
  2030. if (pane.s.dtPane !== undefined) {
  2031. var tempFilter = true;
  2032. pane.s.filteringActive = true;
  2033. if (filterPane !== -1 && filterPane !== null && filterPane === pane.s.index ||
  2034. filterActive === false ||
  2035. pane.s.index === solePane) {
  2036. tempFilter = false;
  2037. pane.s.filteringActive = false;
  2038. }
  2039. pane.updatePane(!tempFilter ? false : filterActive);
  2040. }
  2041. }
  2042. // If the length of the selections are different then some of them have been
  2043. // removed and a deselect has occured
  2044. if (newSelectionList.length > 0 && (newSelectionList.length < this.s.selectionList.length || rebuild)) {
  2045. this._cascadeRegen(newSelectionList, selectTotal);
  2046. var last = newSelectionList[newSelectionList.length - 1].index;
  2047. for (var _m = 0, _o = this.s.panes; _m < _o.length; _m++) {
  2048. var pane = _o[_m];
  2049. pane.s.lastSelect = pane.s.index === last;
  2050. }
  2051. }
  2052. else if (newSelectionList.length > 0) {
  2053. // Update all of the other panes as you would just making a normal selection
  2054. for (var _p = 0, _q = this.s.panes; _p < _q.length; _p++) {
  2055. var paneUpdate = _q[_p];
  2056. if (paneUpdate.s.dtPane !== undefined) {
  2057. var tempFilter = true;
  2058. paneUpdate.s.filteringActive = true;
  2059. if (filterPane !== -1 && filterPane !== null && filterPane === paneUpdate.s.index ||
  2060. filterActive === false ||
  2061. paneUpdate.s.index === solePane) {
  2062. tempFilter = false;
  2063. paneUpdate.s.filteringActive = false;
  2064. }
  2065. paneUpdate.updatePane(!tempFilter ? tempFilter : filterActive);
  2066. }
  2067. }
  2068. }
  2069. // Update the label that shows how many filters are in place
  2070. this._updateFilterCount();
  2071. }
  2072. else {
  2073. var solePane = -1;
  2074. if (newSelectionList.length === 1 && selectTotal !== null && selectTotal !== 0) {
  2075. solePane = newSelectionList[0].index;
  2076. }
  2077. for (var _r = 0, _s = this.s.panes; _r < _s.length; _r++) {
  2078. var pane = _s[_r];
  2079. if (pane.s.dtPane !== undefined) {
  2080. var tempFilter = true;
  2081. pane.s.filteringActive = true;
  2082. if (filterPane !== -1 && filterPane !== null && filterPane === pane.s.index ||
  2083. filterActive === false ||
  2084. pane.s.index === solePane) {
  2085. tempFilter = false;
  2086. pane.s.filteringActive = false;
  2087. }
  2088. pane.updatePane(!tempFilter ? tempFilter : filterActive);
  2089. }
  2090. }
  2091. // Update the label that shows how many filters are in place
  2092. this._updateFilterCount();
  2093. }
  2094. if (!filterActive || selectTotal === 0) {
  2095. this.s.selectionList = [];
  2096. }
  2097. }
  2098. };
  2099. /**
  2100. * Resizes all of the panes
  2101. */
  2102. SearchPanes.prototype.resizePanes = function () {
  2103. if (this.c.layout === 'auto') {
  2104. var contWidth = $$1(this.s.dt.searchPanes.container()).width();
  2105. var target = Math.floor(contWidth / 260.0); // The neatest number of panes per row
  2106. var highest = 1;
  2107. var highestmod = 0;
  2108. var dispIndex = [];
  2109. // Get the indexes of all of the displayed panes
  2110. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2111. var pane = _a[_i];
  2112. if (pane.s.displayed) {
  2113. dispIndex.push(pane.s.index);
  2114. }
  2115. }
  2116. var displayCount = dispIndex.length;
  2117. // If the neatest number is the number we have then use this.
  2118. if (target === displayCount) {
  2119. highest = target;
  2120. }
  2121. else {
  2122. // Go from the target down and find the value with the most panes left over, this will be the best fit
  2123. for (var ppr = target; ppr > 1; ppr--) {
  2124. var rem = displayCount % ppr;
  2125. if (rem === 0) {
  2126. highest = ppr;
  2127. highestmod = 0;
  2128. break;
  2129. }
  2130. // If there are more left over at this amount of panes per row (ppr)
  2131. // then it fits better so new values
  2132. else if (rem > highestmod) {
  2133. highest = ppr;
  2134. highestmod = rem;
  2135. }
  2136. }
  2137. }
  2138. // If there is a perfect fit then none are to be wider
  2139. var widerIndexes = highestmod !== 0 ? dispIndex.slice(dispIndex.length - highestmod, dispIndex.length) : [];
  2140. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  2141. var pane = _c[_b];
  2142. // Resize the pane with the new layout
  2143. if (pane.s.displayed) {
  2144. var layout = 'columns-' + (!widerIndexes.includes(pane.s.index) ? highest : highestmod);
  2145. pane.resize(layout);
  2146. }
  2147. }
  2148. }
  2149. else {
  2150. for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
  2151. var pane = _e[_d];
  2152. pane.adjustTopRow();
  2153. }
  2154. }
  2155. return this;
  2156. };
  2157. /**
  2158. * Attach the panes, buttons and title to the document
  2159. */
  2160. SearchPanes.prototype._attach = function () {
  2161. var _this = this;
  2162. this.dom.container.removeClass(this.classes.hide);
  2163. this.dom.titleRow.removeClass(this.classes.hide);
  2164. this.dom.titleRow.remove();
  2165. this.dom.title.appendTo(this.dom.titleRow);
  2166. // If the clear button is permitted attach it
  2167. if (this.c.clear) {
  2168. this.dom.clearAll.appendTo(this.dom.titleRow);
  2169. this.dom.clearAll.on('click.dtsps', function () {
  2170. _this.clearSelections();
  2171. });
  2172. }
  2173. if (this.c.collapse) {
  2174. this._setCollapseListener();
  2175. }
  2176. this.dom.titleRow.appendTo(this.dom.container);
  2177. // Attach the container for each individual pane to the overall container
  2178. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2179. var pane = _a[_i];
  2180. pane.dom.container.appendTo(this.dom.panes);
  2181. }
  2182. // Attach everything to the document
  2183. this.dom.panes.appendTo(this.dom.container);
  2184. if ($$1('div.' + this.classes.container).length === 0) {
  2185. this.dom.container.prependTo(this.s.dt);
  2186. }
  2187. return this.dom.container;
  2188. };
  2189. /**
  2190. * Attach the top row containing the filter count and clear all button
  2191. */
  2192. SearchPanes.prototype._attachExtras = function () {
  2193. this.dom.container.removeClass(this.classes.hide);
  2194. this.dom.titleRow.removeClass(this.classes.hide);
  2195. this.dom.titleRow.remove();
  2196. this.dom.title.appendTo(this.dom.titleRow);
  2197. // If the clear button is permitted attach it
  2198. if (this.c.clear) {
  2199. this.dom.clearAll.appendTo(this.dom.titleRow);
  2200. }
  2201. // If collapsing is permitted attach those buttons
  2202. if (this.c.collapse) {
  2203. this.dom.showAll.appendTo(this.dom.titleRow);
  2204. this.dom.collapseAll.appendTo(this.dom.titleRow);
  2205. }
  2206. this.dom.titleRow.appendTo(this.dom.container);
  2207. return this.dom.container;
  2208. };
  2209. /**
  2210. * If there are no panes to display then this method is called to either
  2211. * display a message in their place or hide them completely.
  2212. */
  2213. SearchPanes.prototype._attachMessage = function () {
  2214. // Create a message to display on the screen
  2215. var message;
  2216. try {
  2217. message = this.s.dt.i18n('searchPanes.emptyPanes', this.c.i18n.emptyPanes);
  2218. }
  2219. catch (error) {
  2220. message = null;
  2221. }
  2222. // If the message is an empty string then searchPanes.emptyPanes is undefined,
  2223. // therefore the pane container should be removed from the display
  2224. if (message === null) {
  2225. this.dom.container.addClass(this.classes.hide);
  2226. this.dom.titleRow.removeClass(this.classes.hide);
  2227. return;
  2228. }
  2229. else {
  2230. this.dom.container.removeClass(this.classes.hide);
  2231. this.dom.titleRow.addClass(this.classes.hide);
  2232. }
  2233. // Otherwise display the message
  2234. this.dom.emptyMessage.text(message);
  2235. this.dom.emptyMessage.appendTo(this.dom.container);
  2236. return this.dom.container;
  2237. };
  2238. /**
  2239. * Attaches the panes to the document and displays a message or hides if there are none
  2240. */
  2241. SearchPanes.prototype._attachPaneContainer = function () {
  2242. // If a pane is to be displayed then attach the normal pane output
  2243. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2244. var pane = _a[_i];
  2245. if (pane.s.displayed === true) {
  2246. return this._attach();
  2247. }
  2248. }
  2249. // Otherwise attach the custom message or remove the container from the display
  2250. return this._attachMessage();
  2251. };
  2252. /**
  2253. * Prepares the panes for selections to be made when cascade is active and a deselect has occured
  2254. *
  2255. * @param newSelectionList the list of selections which are to be made
  2256. */
  2257. SearchPanes.prototype._cascadeRegen = function (newSelectionList, selectTotal) {
  2258. // Set this to true so that the actions taken do not cause this to run until it is finished
  2259. this.regenerating = true;
  2260. // If only one pane has been selected then take note of its index
  2261. var solePane = -1;
  2262. if (newSelectionList.length === 1 && selectTotal !== null && selectTotal !== 0) {
  2263. solePane = newSelectionList[0].index;
  2264. }
  2265. // Let the pane know that a cascadeRegen is taking place to avoid unexpected behaviour
  2266. // and clear all of the previous selections in the pane
  2267. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2268. var pane = _a[_i];
  2269. pane.setCascadeRegen(true);
  2270. pane.setClear(true);
  2271. // If this is the same as the pane with the only selection then pass it as a parameter into clearPane
  2272. if (pane.s.dtPane !== undefined && pane.s.index === solePane || pane.s.dtPane !== undefined) {
  2273. pane.clearPane();
  2274. }
  2275. pane.setClear(false);
  2276. }
  2277. // Rebin panes
  2278. this.s.dt.draw();
  2279. // While all of the selections have been removed, check the table lengths
  2280. // If they are different, another filter is in place and we need to force viewTotal to be used
  2281. var noSelectionsTableLength = this.s.dt.rows({ search: 'applied' }).data().toArray().length;
  2282. var tableLength = this.s.dt.rows().data().toArray().length;
  2283. if (tableLength !== noSelectionsTableLength) {
  2284. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  2285. var pane = _c[_b];
  2286. pane.s.forceViewTotal = true;
  2287. }
  2288. }
  2289. for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
  2290. var pane = _e[_d];
  2291. pane.updatePane(true);
  2292. }
  2293. // Remake Selections
  2294. this._makeCascadeSelections(newSelectionList);
  2295. // Set the selection list property to be the list without the selections from the deselect pane
  2296. this.s.selectionList = newSelectionList;
  2297. // The regeneration of selections is over so set it back to false
  2298. for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
  2299. var pane = _g[_f];
  2300. pane.setCascadeRegen(false);
  2301. }
  2302. this.regenerating = false;
  2303. // ViewTotal has already been forced at this point so can cancel that for future
  2304. if (tableLength !== noSelectionsTableLength) {
  2305. for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
  2306. var pane = _j[_h];
  2307. pane.s.forceViewTotal = false;
  2308. }
  2309. }
  2310. };
  2311. /**
  2312. * Attaches the message to the document but does not add any panes
  2313. */
  2314. SearchPanes.prototype._checkMessage = function () {
  2315. // If a pane is to be displayed then attach the normal pane output
  2316. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2317. var pane = _a[_i];
  2318. if (pane.s.displayed === true) {
  2319. // Ensure that the empty message is removed if a pane is displayed
  2320. this.dom.emptyMessage.remove();
  2321. this.dom.titleRow.removeClass(this.classes.hide);
  2322. return;
  2323. }
  2324. }
  2325. // Otherwise attach the custom message or remove the container from the display
  2326. return this._attachMessage();
  2327. };
  2328. /**
  2329. * Checks which panes are collapsed and then performs relevant actions to the collapse/show all buttons
  2330. *
  2331. * @param pane The pane to be checked
  2332. */
  2333. SearchPanes.prototype._checkCollapse = function () {
  2334. var disableClose = true;
  2335. var disableShow = true;
  2336. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2337. var pane = _a[_i];
  2338. if (pane.s.displayed) {
  2339. // It the pane is not collapsed
  2340. if (!pane.dom.collapseButton.hasClass(pane.classes.rotated)) {
  2341. // Enable the collapse all button
  2342. this.dom.collapseAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  2343. disableClose = false;
  2344. }
  2345. else {
  2346. // Otherwise enable the show all button
  2347. this.dom.showAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  2348. disableShow = false;
  2349. }
  2350. }
  2351. }
  2352. // If this flag is still true, no panes are open so the close button should be disabled
  2353. if (disableClose) {
  2354. this.dom.collapseAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  2355. }
  2356. // If this flag is still true, no panes are closed so the show button should be disabled
  2357. if (disableShow) {
  2358. this.dom.showAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  2359. }
  2360. };
  2361. /**
  2362. * Collapses all of the panes
  2363. */
  2364. SearchPanes.prototype._collapseAll = function () {
  2365. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2366. var pane = _a[_i];
  2367. pane.collapse();
  2368. }
  2369. };
  2370. /**
  2371. * Gets the selection list from the previous state and stores it in the selectionList Property
  2372. */
  2373. SearchPanes.prototype._getState = function () {
  2374. var loadedFilter = this.s.dt.state.loaded();
  2375. if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList !== undefined) {
  2376. this.s.selectionList = loadedFilter.searchPanes.selectionList;
  2377. }
  2378. };
  2379. /**
  2380. * Makes all of the selections when cascade is active
  2381. *
  2382. * @param newSelectionList the list of selections to be made, in the order they were originally selected
  2383. */
  2384. SearchPanes.prototype._makeCascadeSelections = function (newSelectionList) {
  2385. // make selections in the order they were made previously,
  2386. // excluding those from the pane where a deselect was made
  2387. for (var i = 0; i < newSelectionList.length; i++) {
  2388. var _loop_1 = function (pane) {
  2389. if (pane.s.index === newSelectionList[i].index && pane.s.dtPane !== undefined) {
  2390. // When regenerating the cascade selections we need this flag so that
  2391. // the panes are only ignored if it
  2392. // is the last selection and the pane for that selection
  2393. if (i === newSelectionList.length - 1) {
  2394. pane.s.lastCascade = true;
  2395. }
  2396. // if there are any selections currently in the pane then
  2397. // deselect them as we are about to make our new selections
  2398. if (pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0 && pane.s.dtPane !== undefined) {
  2399. pane.setClear(true);
  2400. pane.clearPane();
  2401. pane.setClear(false);
  2402. }
  2403. var _loop_2 = function (row) {
  2404. var found = false;
  2405. pane.s.dtPane.rows().every(function (rowIdx) {
  2406. if (pane.s.dtPane.row(rowIdx).data() !== undefined &&
  2407. row !== undefined &&
  2408. pane.s.dtPane.row(rowIdx).data().filter === row.filter) {
  2409. found = true;
  2410. pane.s.dtPane.row(rowIdx).select();
  2411. }
  2412. });
  2413. if (!found) {
  2414. var newRow = pane.addRow(row.display, row.filter, 0, row.total, row.sort, row.type, row.className);
  2415. newRow.select();
  2416. }
  2417. };
  2418. // select every row in the pane that was selected previously
  2419. for (var _i = 0, _a = newSelectionList[i].rows; _i < _a.length; _i++) {
  2420. var row = _a[_i];
  2421. _loop_2(row);
  2422. }
  2423. pane.s.scrollTop = $$1(pane.s.dtPane.table().node()).parent()[0].scrollTop;
  2424. pane.s.dtPane.draw();
  2425. pane.s.dtPane.table().node().parentNode.scrollTop = pane.s.scrollTop;
  2426. pane.s.lastCascade = false;
  2427. }
  2428. };
  2429. // As the selections may have been made across the panes
  2430. // in a different order to the pane index we must identify
  2431. // which pane has the index of the selection. This is also important for colreorder etc
  2432. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2433. var pane = _a[_i];
  2434. _loop_1(pane);
  2435. }
  2436. }
  2437. };
  2438. /**
  2439. * Declares the instances of individual searchpanes dependant on the number of columns.
  2440. * It is necessary to run this once preInit has completed otherwise no panes will be
  2441. * created as the column count will be 0.
  2442. *
  2443. * @param table the DataTable api for the parent table
  2444. * @param paneSettings the settings passed into the constructor
  2445. * @param opts the options passed into the constructor
  2446. */
  2447. SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
  2448. var _this = this;
  2449. // Create Panes
  2450. table
  2451. .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
  2452. .eq(0)
  2453. .each(function (idx) {
  2454. _this.s.panes.push(new SearchPane(paneSettings, opts, idx, _this.c.layout, _this.dom.panes));
  2455. });
  2456. // If there is any extra custom panes defined then create panes for them too
  2457. var rowLength = table.columns().eq(0).toArray().length;
  2458. var paneLength = this.c.panes.length;
  2459. for (var i = 0; i < paneLength; i++) {
  2460. var id = rowLength + i;
  2461. this.s.panes.push(new SearchPane(paneSettings, opts, id, this.c.layout, this.dom.panes, this.c.panes[i]));
  2462. }
  2463. // If a custom ordering is being used
  2464. if (this.c.order.length > 0) {
  2465. // Make a new Array of panes based upon the order
  2466. var newPanes = this.c.order.map(function (name, index, values) { return _this._findPane(name); });
  2467. // Remove the old panes from the dom
  2468. this.dom.panes.empty();
  2469. this.s.panes = newPanes;
  2470. // Append the panes in the correct order
  2471. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2472. var pane = _a[_i];
  2473. this.dom.panes.append(pane.dom.container);
  2474. }
  2475. }
  2476. // If this internal property is true then the DataTable has been initialised already
  2477. if (this.s.dt.settings()[0]._bInitComplete) {
  2478. this._startup(table);
  2479. }
  2480. else {
  2481. // Otherwise add the paneStartup function to the list of functions
  2482. // that are to be run when the table is initialised. This will garauntee that the
  2483. // panes are initialised before the init event and init Complete callback is fired
  2484. this.s.dt.settings()[0].aoInitComplete.push({ fn: function () {
  2485. _this._startup(table);
  2486. } });
  2487. }
  2488. };
  2489. /**
  2490. * Finds a pane based upon the name of that pane
  2491. *
  2492. * @param name string representing the name of the pane
  2493. * @returns SearchPane The pane which has that name
  2494. */
  2495. SearchPanes.prototype._findPane = function (name) {
  2496. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2497. var pane = _a[_i];
  2498. if (name === pane.s.name) {
  2499. return pane;
  2500. }
  2501. }
  2502. };
  2503. /**
  2504. * Works out which panes to update when data is recieved from the server and viewTotal is active
  2505. */
  2506. SearchPanes.prototype._serverTotals = function () {
  2507. var selectPresent = false;
  2508. var deselectPresent = false;
  2509. var table = this.s.dt;
  2510. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2511. var pane = _a[_i];
  2512. // Identify the pane where a selection or deselection has been made and add it to the list.
  2513. if (pane.s.selectPresent) {
  2514. this.s.selectionList.push({
  2515. index: pane.s.index,
  2516. protect: false,
  2517. rows: pane.s.dtPane.rows({ selected: true }).data().toArray()
  2518. });
  2519. pane.s.selectPresent = false;
  2520. selectPresent = true;
  2521. break;
  2522. }
  2523. else if (pane.s.deselect) {
  2524. var selectedData = pane.s.dtPane.rows({ selected: true }).data().toArray();
  2525. if (selectedData.length > 0) {
  2526. this.s.selectionList.push({
  2527. index: pane.s.index,
  2528. protect: true,
  2529. rows: selectedData
  2530. });
  2531. }
  2532. selectPresent = true;
  2533. deselectPresent = true;
  2534. }
  2535. }
  2536. // Build an updated list based on any selections or deselections added
  2537. if (!selectPresent) {
  2538. this.s.selectionList = [];
  2539. }
  2540. else {
  2541. var newSelectionList = [];
  2542. for (var i = 0; i < this.s.selectionList.length; i++) {
  2543. var further = false;
  2544. // Find out if this selection is the last one in the list for that pane
  2545. for (var j = i + 1; j < this.s.selectionList.length; j++) {
  2546. if (this.s.selectionList[j].index === this.s.selectionList[i].index) {
  2547. further = true;
  2548. }
  2549. }
  2550. // If there are no selections for this pane in the list then just push this one
  2551. if (!further) {
  2552. var push = false;
  2553. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  2554. var pane = _c[_b];
  2555. if (pane.s.index === this.s.selectionList[i].index &&
  2556. pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0) {
  2557. push = true;
  2558. }
  2559. }
  2560. if (push) {
  2561. newSelectionList.push(this.s.selectionList[i]);
  2562. }
  2563. }
  2564. }
  2565. this.s.selectionList = newSelectionList;
  2566. }
  2567. var initIdx = -1;
  2568. // If there has been a deselect and only one pane has a selection then update everything
  2569. if (deselectPresent && this.s.selectionList.length === 1) {
  2570. for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
  2571. var pane = _e[_d];
  2572. pane.s.lastSelect = false;
  2573. pane.s.deselect = false;
  2574. if (pane.s.dtPane !== undefined && pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0) {
  2575. initIdx = pane.s.index;
  2576. }
  2577. }
  2578. }
  2579. // Otherwise if there are more 1 selections then find the last one and set it to not update that pane
  2580. else if (this.s.selectionList.length > 0) {
  2581. var last = this.s.selectionList[this.s.selectionList.length - 1].index;
  2582. for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
  2583. var pane = _g[_f];
  2584. pane.s.lastSelect = pane.s.index === last;
  2585. pane.s.deselect = false;
  2586. }
  2587. }
  2588. // Otherwise if there are no selections then find where that took place and do not update to maintain scrolling
  2589. else if (this.s.selectionList.length === 0) {
  2590. for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
  2591. var pane = _j[_h];
  2592. // pane.s.lastSelect = (pane.s.deselect === true);
  2593. pane.s.lastSelect = false;
  2594. pane.s.deselect = false;
  2595. }
  2596. }
  2597. this.dom.panes.empty();
  2598. // Rebuild the desired panes
  2599. for (var _k = 0, _l = this.s.panes; _k < _l.length; _k++) {
  2600. var pane = _l[_k];
  2601. if (!pane.s.lastSelect) {
  2602. pane.rebuildPane(undefined, this.s.dt.page.info().serverSide ? this.s.serverData : undefined, pane.s.index === initIdx ? true : null, true);
  2603. }
  2604. else {
  2605. pane._setListeners();
  2606. }
  2607. // append all of the panes and enable select
  2608. this.dom.panes.append(pane.dom.container);
  2609. if (pane.s.dtPane !== undefined) {
  2610. $$1(pane.s.dtPane.table().node()).parent()[0].scrollTop = pane.s.scrollTop;
  2611. // eslint-disable-next-line no-extra-parens
  2612. $$1.fn.dataTable.select.init(pane.s.dtPane);
  2613. }
  2614. }
  2615. this._updateSelection();
  2616. };
  2617. /**
  2618. * Sets the listeners for the collapse and show all buttons
  2619. * Also sets and performs checks on current panes to see if they are collapsed
  2620. */
  2621. SearchPanes.prototype._setCollapseListener = function () {
  2622. var _this = this;
  2623. this.dom.collapseAll.on('click.dtsps', function () {
  2624. _this._collapseAll();
  2625. _this.dom.collapseAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  2626. _this.dom.showAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  2627. _this.s.dt.state.save();
  2628. });
  2629. this.dom.showAll.on('click.dtsps', function () {
  2630. _this._showAll();
  2631. _this.dom.showAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  2632. _this.dom.collapseAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  2633. _this.s.dt.state.save();
  2634. });
  2635. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2636. var pane = _a[_i];
  2637. // We want to make the same check whenever there is a collapse/expand
  2638. pane.dom.collapseButton.on('click', function () { return _this._checkCollapse(); });
  2639. }
  2640. this._checkCollapse();
  2641. };
  2642. /**
  2643. * Shows all of the panes
  2644. */
  2645. SearchPanes.prototype._showAll = function () {
  2646. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2647. var pane = _a[_i];
  2648. pane.show();
  2649. }
  2650. };
  2651. /**
  2652. * Initialises the tables previous/preset selections and initialises callbacks for events
  2653. *
  2654. * @param table the parent table for which the searchPanes are being created
  2655. */
  2656. SearchPanes.prototype._startup = function (table) {
  2657. var _this = this;
  2658. this.dom.container.text('');
  2659. // Attach clear button and title bar to the document
  2660. this._attachExtras();
  2661. this.dom.container.append(this.dom.panes);
  2662. this.dom.panes.empty();
  2663. var loadedFilter = this.s.dt.state.loaded();
  2664. if (this.c.viewTotal && !this.c.cascadePanes) {
  2665. if (loadedFilter !== null &&
  2666. loadedFilter !== undefined &&
  2667. loadedFilter.searchPanes !== undefined &&
  2668. loadedFilter.searchPanes.panes !== undefined) {
  2669. var filterActive = false;
  2670. for (var _i = 0, _a = loadedFilter.searchPanes.panes; _i < _a.length; _i++) {
  2671. var pane = _a[_i];
  2672. if (pane.selected.length > 0) {
  2673. filterActive = true;
  2674. break;
  2675. }
  2676. }
  2677. if (filterActive) {
  2678. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  2679. var pane = _c[_b];
  2680. pane.s.showFiltered = true;
  2681. }
  2682. }
  2683. }
  2684. }
  2685. for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
  2686. var pane = _e[_d];
  2687. pane.rebuildPane(undefined, Object.keys(this.s.serverData).length > 0 ? this.s.serverData : undefined);
  2688. this.dom.panes.append(pane.dom.container);
  2689. }
  2690. // If the layout is set to auto then the panes need to be resized to their best fit
  2691. if (this.c.layout === 'auto') {
  2692. this.resizePanes();
  2693. }
  2694. // Reset the paging if that has been saved in the state
  2695. if (!this.s.stateRead && loadedFilter !== null && loadedFilter !== undefined) {
  2696. this.s.dt.page(loadedFilter.start / this.s.dt.page.len());
  2697. this.s.dt.draw('page');
  2698. }
  2699. this.s.stateRead = true;
  2700. if (this.c.viewTotal && !this.c.cascadePanes) {
  2701. for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
  2702. var pane = _g[_f];
  2703. pane.updatePane();
  2704. }
  2705. }
  2706. this._checkMessage();
  2707. // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
  2708. table.on('preDraw.dtsps', function () {
  2709. // Check that the panes are not updating to avoid infinite loops
  2710. // Also check that this draw is not due to paging
  2711. if (!_this.s.updating && !_this.s.paging) {
  2712. if ((_this.c.cascadePanes || _this.c.viewTotal) && !_this.s.dt.page.info().serverSide) {
  2713. _this.redrawPanes(_this.c.viewTotal);
  2714. }
  2715. else {
  2716. _this._updateFilterCount();
  2717. _this._updateSelection();
  2718. }
  2719. _this.s.filterPane = -1;
  2720. }
  2721. // Paging flag reset - we only need to dodge the draw once
  2722. _this.s.paging = false;
  2723. });
  2724. $$1(window).on('resize.dtsp', dataTable$1.util.throttle(function () {
  2725. _this.resizePanes();
  2726. }));
  2727. // Whenever a state save occurs store the selection list in the state object
  2728. this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
  2729. if (data.searchPanes === undefined) {
  2730. data.searchPanes = {};
  2731. }
  2732. data.searchPanes.selectionList = _this.s.selectionList;
  2733. });
  2734. // Listener for paging on main table
  2735. table.off('page');
  2736. table.on('page', function () {
  2737. _this.s.paging = true;
  2738. _this.s.page = _this.s.dt.page();
  2739. });
  2740. if (this.s.dt.page.info().serverSide) {
  2741. table.off('preXhr.dt');
  2742. table.on('preXhr.dt', function (e, settings, data) {
  2743. if (data.searchPanes === undefined) {
  2744. data.searchPanes = {};
  2745. }
  2746. if (data.searchPanes_null === undefined) {
  2747. data.searchPanes_null = {};
  2748. }
  2749. // Count how many filters are being applied
  2750. var filterCount = 0;
  2751. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  2752. var pane = _a[_i];
  2753. var src = _this.s.dt.column(pane.s.index).dataSrc();
  2754. if (data.searchPanes[src] === undefined) {
  2755. data.searchPanes[src] = {};
  2756. }
  2757. if (data.searchPanes_null[src] === undefined) {
  2758. data.searchPanes_null[src] = {};
  2759. }
  2760. if (pane.s.dtPane !== undefined) {
  2761. var rowData = pane.s.dtPane.rows({ selected: true }).data().toArray();
  2762. for (var i = 0; i < rowData.length; i++) {
  2763. data.searchPanes[src][i] = rowData[i].filter;
  2764. if (data.searchPanes[src][i] === null) {
  2765. data.searchPanes_null[src][i] = true;
  2766. }
  2767. filterCount++;
  2768. }
  2769. }
  2770. }
  2771. if (_this.c.viewTotal) {
  2772. _this._prepViewTotal(filterCount);
  2773. }
  2774. // If there is a filter to be applied, then we need to read from the start of the result set
  2775. // and set the paging to 0. This matches the behaviour of client side processing
  2776. if (filterCount > 0) {
  2777. // If the number of filters has changed we need to read from the start of the
  2778. // result set and reset the paging
  2779. if (filterCount !== _this.s.filterCount) {
  2780. data.start = 0;
  2781. _this.s.page = 0;
  2782. }
  2783. // Otherwise it is a paging request and we need to read from whatever the paging has been set to
  2784. else {
  2785. data.start = _this.s.page * _this.s.dt.page.len();
  2786. }
  2787. _this.s.dt.page(_this.s.page);
  2788. _this.s.filterCount = filterCount;
  2789. }
  2790. });
  2791. }
  2792. else {
  2793. table.on('preXhr.dt', function (e, settings, data) {
  2794. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  2795. var pane = _a[_i];
  2796. pane.clearData();
  2797. }
  2798. });
  2799. }
  2800. // If the data is reloaded from the server then it is possible that it has changed completely,
  2801. // so we need to rebuild the panes
  2802. this.s.dt.on('xhr', function (e, settings, json, xhr) {
  2803. if (settings.nTable !== _this.s.dt.table().node()) {
  2804. return;
  2805. }
  2806. var processing = false;
  2807. if (!_this.s.dt.page.info().serverSide) {
  2808. _this.s.dt.one('preDraw', function () {
  2809. if (processing) {
  2810. return;
  2811. }
  2812. var page = _this.s.dt.page();
  2813. processing = true;
  2814. _this.s.updating = true;
  2815. _this.dom.panes.empty();
  2816. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  2817. var pane = _a[_i];
  2818. pane.clearData(); // Clears all of the bins and will mean that the data has to be re-read
  2819. // Pass a boolean to say whether this is the last choice made for maintaining selections
  2820. // when rebuilding
  2821. pane.rebuildPane(_this.s.selectionList[_this.s.selectionList.length - 1] !== undefined ?
  2822. pane.s.index === _this.s.selectionList[_this.s.selectionList.length - 1].index :
  2823. false, undefined, undefined, true);
  2824. _this.dom.panes.append(pane.dom.container);
  2825. }
  2826. if (!_this.s.dt.page.info().serverSide) {
  2827. _this.s.dt.draw();
  2828. }
  2829. _this.s.updating = false;
  2830. if (_this.c.cascadePanes || _this.c.viewTotal) {
  2831. _this.redrawPanes(_this.c.cascadePanes);
  2832. }
  2833. else {
  2834. _this._updateSelection();
  2835. }
  2836. _this._checkMessage();
  2837. _this.s.dt.one('draw', function () {
  2838. _this.s.updating = true;
  2839. _this.s.dt.page(page).draw(false);
  2840. _this.s.updating = false;
  2841. });
  2842. });
  2843. }
  2844. });
  2845. // PreSelect any selections which have been defined using the preSelect option
  2846. for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
  2847. var pane = _j[_h];
  2848. if (pane !== undefined &&
  2849. pane.s.dtPane !== undefined &&
  2850. (pane.s.colOpts.preSelect !== undefined && pane.s.colOpts.preSelect.length > 0 ||
  2851. pane.customPaneSettings !== null &&
  2852. pane.customPaneSettings.preSelect !== undefined &&
  2853. pane.customPaneSettings.preSelect.length > 0)) {
  2854. var tableLength = pane.s.dtPane.rows().data().toArray().length;
  2855. for (var i = 0; i < tableLength; i++) {
  2856. if (pane.s.colOpts.preSelect.includes(pane.s.dtPane.cell(i, 0).data()) ||
  2857. pane.customPaneSettings !== null &&
  2858. pane.customPaneSettings.preSelect !== undefined &&
  2859. pane.customPaneSettings.preSelect.includes(pane.s.dtPane.cell(i, 0).data())) {
  2860. pane.s.dtPane.row(i).select();
  2861. }
  2862. }
  2863. pane.updateTable();
  2864. }
  2865. }
  2866. if (this.s.selectionList !== undefined && this.s.selectionList.length > 0) {
  2867. var last = this.s.selectionList[this.s.selectionList.length - 1].index;
  2868. for (var _k = 0, _l = this.s.panes; _k < _l.length; _k++) {
  2869. var pane = _l[_k];
  2870. pane.s.lastSelect = pane.s.index === last;
  2871. }
  2872. }
  2873. // If cascadePanes is active then make the previous selections in the order they were previously
  2874. if (this.s.selectionList.length > 0 && this.c.cascadePanes) {
  2875. this._cascadeRegen(this.s.selectionList, this.s.selectionList.length);
  2876. }
  2877. // Update the title bar to show how many filters have been selected
  2878. this._updateFilterCount();
  2879. // If the table is destroyed and restarted then clear the selections so that they do not persist.
  2880. table.on('destroy.dtsps', function () {
  2881. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  2882. var pane = _a[_i];
  2883. pane.destroy();
  2884. }
  2885. table.off('.dtsps');
  2886. _this.dom.collapseAll.off('.dtsps');
  2887. _this.dom.showAll.off('.dtsps');
  2888. _this.dom.clearAll.off('.dtsps');
  2889. _this.dom.container.remove();
  2890. _this.clearSelections();
  2891. });
  2892. if (this.c.collapse) {
  2893. this._setCollapseListener();
  2894. }
  2895. // When the clear All button has been pressed clear all of the selections in the panes
  2896. if (this.c.clear) {
  2897. this.dom.clearAll.on('click.dtsps', function () {
  2898. _this.clearSelections();
  2899. });
  2900. }
  2901. table.settings()[0]._searchPanes = this;
  2902. // This state save is required so that state is maintained over multiple refreshes if no actions are made
  2903. this.s.dt.state.save();
  2904. };
  2905. SearchPanes.prototype._prepViewTotal = function (selectTotal) {
  2906. var filterPane = this.s.filterPane;
  2907. var filterActive = false;
  2908. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2909. var pane = _a[_i];
  2910. if (pane.s.dtPane !== undefined) {
  2911. var selectLength = pane.s.dtPane.rows({ selected: true }).data().toArray().length;
  2912. // If filterPane === -1 then a pane with a selection has not been found yet,
  2913. // so set filterPane to that panes index
  2914. if (selectLength > 0 && filterPane === -1) {
  2915. filterPane = pane.s.index;
  2916. filterActive = true;
  2917. }
  2918. // Then if another pane is found with a selection then set filterPane to null to
  2919. // show that multiple panes have selections present
  2920. else if (selectLength > 0) {
  2921. filterPane = null;
  2922. }
  2923. }
  2924. }
  2925. if (selectTotal !== null && selectTotal !== 0) {
  2926. filterPane = null;
  2927. }
  2928. // Update all of the panes to reflect the current state of the filters
  2929. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  2930. var pane = _c[_b];
  2931. if (pane.s.dtPane !== undefined) {
  2932. pane.s.filteringActive = true;
  2933. if (filterPane !== -1 && filterPane !== null && filterPane === pane.s.index ||
  2934. filterActive === false) {
  2935. pane.s.filteringActive = false;
  2936. }
  2937. }
  2938. }
  2939. };
  2940. /**
  2941. * Updates the number of filters that have been applied in the title
  2942. */
  2943. SearchPanes.prototype._updateFilterCount = function () {
  2944. var filterCount = 0;
  2945. // Add the number of all of the filters throughout the panes
  2946. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2947. var pane = _a[_i];
  2948. if (pane.s.dtPane !== undefined) {
  2949. filterCount += pane.getPaneCount();
  2950. }
  2951. }
  2952. // Run the message through the internationalisation method to improve readability
  2953. var message = this.s.dt.i18n('searchPanes.title', this.c.i18n.title, filterCount);
  2954. this.dom.title.text(message);
  2955. if (this.c.filterChanged !== undefined && typeof this.c.filterChanged === 'function') {
  2956. this.c.filterChanged.call(this.s.dt, filterCount);
  2957. }
  2958. if (filterCount === 0) {
  2959. this.dom.clearAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  2960. }
  2961. else {
  2962. this.dom.clearAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  2963. }
  2964. };
  2965. /**
  2966. * Updates the selectionList when cascade is not in place
  2967. */
  2968. SearchPanes.prototype._updateSelection = function () {
  2969. this.s.selectionList = [];
  2970. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  2971. var pane = _a[_i];
  2972. if (pane.s.dtPane !== undefined) {
  2973. this.s.selectionList.push({
  2974. index: pane.s.index,
  2975. protect: false,
  2976. rows: pane.s.dtPane.rows({ selected: true }).data().toArray()
  2977. });
  2978. }
  2979. }
  2980. };
  2981. SearchPanes.version = '1.4.0';
  2982. SearchPanes.classes = {
  2983. clear: 'dtsp-clear',
  2984. clearAll: 'dtsp-clearAll',
  2985. collapseAll: 'dtsp-collapseAll',
  2986. container: 'dtsp-searchPanes',
  2987. disabledButton: 'dtsp-disabledButton',
  2988. emptyMessage: 'dtsp-emptyMessage',
  2989. hide: 'dtsp-hidden',
  2990. panes: 'dtsp-panesContainer',
  2991. search: 'dtsp-search',
  2992. showAll: 'dtsp-showAll',
  2993. title: 'dtsp-title',
  2994. titleRow: 'dtsp-titleRow'
  2995. };
  2996. // Define SearchPanes default options
  2997. SearchPanes.defaults = {
  2998. cascadePanes: false,
  2999. clear: true,
  3000. collapse: true,
  3001. columns: [],
  3002. container: function (dt) {
  3003. return dt.table().container();
  3004. },
  3005. filterChanged: undefined,
  3006. i18n: {
  3007. clearMessage: 'Clear All',
  3008. clearPane: '&times;',
  3009. collapse: {
  3010. 0: 'SearchPanes',
  3011. _: 'SearchPanes (%d)'
  3012. },
  3013. collapseMessage: 'Collapse All',
  3014. count: '{total}',
  3015. countFiltered: '{shown} ({total})',
  3016. emptyMessage: '<em>No data</em>',
  3017. emptyPanes: 'No SearchPanes',
  3018. loadMessage: 'Loading Search Panes...',
  3019. showMessage: 'Show All',
  3020. title: 'Filters Active - %d'
  3021. },
  3022. layout: 'auto',
  3023. order: [],
  3024. panes: [],
  3025. viewTotal: false
  3026. };
  3027. return SearchPanes;
  3028. }());
  3029. /*! SearchPanes 1.4.0
  3030. * 2019-2020 SpryMedia Ltd - datatables.net/license
  3031. */
  3032. // DataTables extensions common UMD. Note that this allows for AMD, CommonJS
  3033. // (with window and jQuery being allowed as parameters to the returned
  3034. // function) or just default browser loading.
  3035. (function (factory) {
  3036. if (typeof define === 'function' && define.amd) {
  3037. // AMD
  3038. define(['jquery', 'datatables.net'], function ($) {
  3039. return factory($, window, document);
  3040. });
  3041. }
  3042. else if (typeof exports === 'object') {
  3043. // CommonJS
  3044. module.exports = function (root, $) {
  3045. if (!root) {
  3046. root = window;
  3047. }
  3048. if (!$ || !$.fn.dataTable) {
  3049. // eslint-disable-next-line @typescript-eslint/no-var-requires
  3050. $ = require('datatables.net')(root, $).$;
  3051. }
  3052. return factory($, root, root.document);
  3053. };
  3054. }
  3055. else {
  3056. // Browser - assume jQuery has already been loaded
  3057. // eslint-disable-next-line no-extra-parens
  3058. factory(window.jQuery, window, document);
  3059. }
  3060. }(function ($, window, document) {
  3061. setJQuery($);
  3062. setJQuery$1($);
  3063. var dataTable = $.fn.dataTable;
  3064. // eslint-disable-next-line no-extra-parens
  3065. $.fn.dataTable.SearchPanes = SearchPanes;
  3066. // eslint-disable-next-line no-extra-parens
  3067. $.fn.DataTable.SearchPanes = SearchPanes;
  3068. // eslint-disable-next-line no-extra-parens
  3069. $.fn.dataTable.SearchPane = SearchPane;
  3070. // eslint-disable-next-line no-extra-parens
  3071. $.fn.DataTable.SearchPane = SearchPane;
  3072. // eslint-disable-next-line no-extra-parens
  3073. var apiRegister = $.fn.dataTable.Api.register;
  3074. apiRegister('searchPanes()', function () {
  3075. return this;
  3076. });
  3077. apiRegister('searchPanes.clearSelections()', function () {
  3078. return this.iterator('table', function (ctx) {
  3079. if (ctx._searchPanes) {
  3080. ctx._searchPanes.clearSelections();
  3081. }
  3082. });
  3083. });
  3084. apiRegister('searchPanes.rebuildPane()', function (targetIdx, maintainSelections) {
  3085. return this.iterator('table', function (ctx) {
  3086. if (ctx._searchPanes) {
  3087. ctx._searchPanes.rebuild(targetIdx, maintainSelections);
  3088. }
  3089. });
  3090. });
  3091. apiRegister('searchPanes.resizePanes()', function () {
  3092. var ctx = this.context[0];
  3093. return ctx._searchPanes ?
  3094. ctx._searchPanes.resizePanes() :
  3095. null;
  3096. });
  3097. apiRegister('searchPanes.container()', function () {
  3098. var ctx = this.context[0];
  3099. return ctx._searchPanes
  3100. ? ctx._searchPanes.getNode()
  3101. : null;
  3102. });
  3103. $.fn.dataTable.ext.buttons.searchPanesClear = {
  3104. action: function (e, dt, node, config) {
  3105. dt.searchPanes.clearSelections();
  3106. },
  3107. text: 'Clear Panes'
  3108. };
  3109. $.fn.dataTable.ext.buttons.searchPanes = {
  3110. action: function (e, dt, node, config) {
  3111. e.stopPropagation();
  3112. this.popover(config._panes.getNode(), {
  3113. align: 'dt-container'
  3114. });
  3115. config._panes.rebuild(undefined, true);
  3116. },
  3117. config: {},
  3118. init: function (dt, node, config) {
  3119. var panes = new $.fn.dataTable.SearchPanes(dt, $.extend({
  3120. filterChanged: function (count) {
  3121. // console.log(dt.context[0])
  3122. dt.button(node).text(dt.i18n('searchPanes.collapse', dt.context[0].oLanguage.searchPanes !== undefined ?
  3123. dt.context[0].oLanguage.searchPanes.collapse :
  3124. dt.context[0]._searchPanes.c.i18n.collapse, count));
  3125. }
  3126. }, config.config));
  3127. var message = dt.i18n('searchPanes.collapse', panes.c.i18n.collapse, 0);
  3128. dt.button(node).text(message);
  3129. config._panes = panes;
  3130. },
  3131. text: 'Search Panes'
  3132. };
  3133. function _init(settings, options, fromPre) {
  3134. if (options === void 0) { options = null; }
  3135. if (fromPre === void 0) { fromPre = false; }
  3136. var api = new dataTable.Api(settings);
  3137. var opts = options
  3138. ? options
  3139. : api.init().searchPanes || dataTable.defaults.searchPanes;
  3140. var searchPanes = new SearchPanes(api, opts, fromPre);
  3141. var node = searchPanes.getNode();
  3142. return node;
  3143. }
  3144. // Attach a listener to the document which listens for DataTables initialisation
  3145. // events so we can automatically initialise
  3146. $(document).on('preInit.dt.dtsp', function (e, settings, json) {
  3147. if (e.namespace !== 'dt') {
  3148. return;
  3149. }
  3150. if (settings.oInit.searchPanes ||
  3151. dataTable.defaults.searchPanes) {
  3152. if (!settings._searchPanes) {
  3153. _init(settings, null, true);
  3154. }
  3155. }
  3156. });
  3157. // DataTables `dom` feature option
  3158. dataTable.ext.feature.push({
  3159. cFeature: 'P',
  3160. fnInit: _init
  3161. });
  3162. // DataTables 2 layout feature
  3163. if (dataTable.ext.features) {
  3164. dataTable.ext.features.register('searchPanes', _init);
  3165. }
  3166. }));
  3167. }());