datatables.net-searchbuilder 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*! SearchBuilder 1.1.0
1
+ /*! SearchBuilder 1.3.1
2
2
  * ©SpryMedia Ltd - datatables.net/license/mit
3
3
  */
4
4
  (function () {
@@ -60,7 +60,7 @@
60
60
  .addClass(this.classes.italic)
61
61
  .attr('autocomplete', 'hacking'),
62
62
  conditionTitle: $$2('<option value="" disabled selected hidden/>')
63
- .text(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
63
+ .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
64
64
  container: $$2('<div/>')
65
65
  .addClass(this.classes.container),
66
66
  data: $$2('<select/>')
@@ -68,24 +68,28 @@
68
68
  .addClass(this.classes.dropDown)
69
69
  .addClass(this.classes.italic),
70
70
  dataTitle: $$2('<option value="" disabled selected hidden/>')
71
- .text(this.s.dt.i18n('searchBuilder.data', i18n.data)),
71
+ .html(this.s.dt.i18n('searchBuilder.data', i18n.data)),
72
72
  defaultValue: $$2('<select disabled/>')
73
73
  .addClass(this.classes.value)
74
74
  .addClass(this.classes.dropDown)
75
- .addClass(this.classes.select),
76
- "delete": $$2('<button>&times</button>')
75
+ .addClass(this.classes.select)
76
+ .addClass(this.classes.italic),
77
+ "delete": $$2('<button/>')
78
+ .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"]))
77
79
  .addClass(this.classes["delete"])
78
80
  .addClass(this.classes.button)
79
81
  .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle))
80
82
  .attr('type', 'button'),
81
83
  // eslint-disable-next-line no-useless-escape
82
- left: $$2('<button>\<</button>')
84
+ left: $$2('<button/>')
85
+ .html(this.s.dt.i18n('searchBuilder.left', i18n.left))
83
86
  .addClass(this.classes.left)
84
87
  .addClass(this.classes.button)
85
88
  .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle))
86
89
  .attr('type', 'button'),
87
90
  // eslint-disable-next-line no-useless-escape
88
- right: $$2('<button>\></button>')
91
+ right: $$2('<button/>')
92
+ .html(this.s.dt.i18n('searchBuilder.right', i18n.right))
89
93
  .addClass(this.classes.right)
90
94
  .addClass(this.classes.button)
91
95
  .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle))
@@ -97,8 +101,8 @@
97
101
  .addClass(this.classes.italic)
98
102
  .addClass(this.classes.select)
99
103
  ],
100
- valueTitle: $$2('<option value="--valueTitle--" selected/>')
101
- .text(this.s.dt.i18n('searchBuilder.value', i18n.value))
104
+ valueTitle: $$2('<option value="--valueTitle--" disabled selected hidden/>')
105
+ .html(this.s.dt.i18n('searchBuilder.value', i18n.value))
102
106
  };
103
107
  // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder
104
108
  if (this.c.greyscale) {
@@ -111,18 +115,32 @@
111
115
  }
112
116
  }
113
117
  // For responsive design, adjust the criterias properties on the following events
114
- this.s.dt.on('draw.dtsp', function () {
118
+ this.s.dt.on('draw.dtsb', function () {
115
119
  _this._adjustCriteria();
116
120
  });
117
- this.s.dt.on('buttons-action', function () {
121
+ this.s.dt.on('buttons-action.dtsb', function () {
118
122
  _this._adjustCriteria();
119
123
  });
120
- $$2(window).on('resize.dtsp', dataTable$2.util.throttle(function () {
124
+ $$2(window).on('resize.dtsb', dataTable$2.util.throttle(function () {
121
125
  _this._adjustCriteria();
122
126
  }));
123
127
  this._buildCriteria();
124
128
  return this;
125
129
  }
130
+ /**
131
+ * Escape html characters within a string
132
+ *
133
+ * @param txt the string to be escaped
134
+ * @returns the escaped string
135
+ */
136
+ Criteria._escapeHTML = function (txt) {
137
+ return txt
138
+ .toString()
139
+ .replace(/&amp;/g, '&')
140
+ .replace(/&lt;/g, '<')
141
+ .replace(/&gt;/g, '>')
142
+ .replace(/&quot;/g, '"');
143
+ };
126
144
  /**
127
145
  * Adds the left button to the criteria
128
146
  */
@@ -217,15 +235,15 @@
217
235
  filter.sort();
218
236
  for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
219
237
  var filt = filter_1[_i];
220
- if (filt) {
238
+ if (filt && typeof filt === 'string') {
221
239
  filt = filt.replace(/[\r\n\u2028]/g, ' ');
222
240
  }
223
241
  }
224
242
  }
225
- else if (filter !== null) {
243
+ else if (filter !== null && typeof filter === 'string') {
226
244
  filter = filter.replace(/[\r\n\u2028]/g, ' ');
227
245
  }
228
- if (this.s.type.includes('html')) {
246
+ if (this.s.type.includes('html') && typeof filter === 'string') {
229
247
  filter = filter.replace(/(<([^>]+)>)/ig, '');
230
248
  }
231
249
  // Not ideal, but jqueries .val() returns an empty string even
@@ -239,8 +257,8 @@
239
257
  /**
240
258
  * Gets the details required to rebuild the criteria
241
259
  */
242
- Criteria.prototype.getDetails = function () {
243
- var value = this.s.value;
260
+ Criteria.prototype.getDetails = function (deFormatDates) {
261
+ if (deFormatDates === void 0) { deFormatDates = false; }
244
262
  // This check is in place for if a custom decimal character is in place
245
263
  if (this.s.type !== null &&
246
264
  this.s.type.includes('num') &&
@@ -258,11 +276,37 @@
258
276
  this.s.value[i] = splitRD.join('.');
259
277
  }
260
278
  }
279
+ else if (this.s.type !== null && deFormatDates) {
280
+ if (this.s.type.includes('date') ||
281
+ this.s.type.includes('time')) {
282
+ for (var i = 0; i < this.s.value.length; i++) {
283
+ if (this.s.value[i].match(/^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$/g) === null) {
284
+ this.s.value[i] = '';
285
+ }
286
+ }
287
+ }
288
+ else if (this.s.type.includes('moment')) {
289
+ for (var i = 0; i < this.s.value.length; i++) {
290
+ this.s.value[i] = moment(this.s.value[i], this.s.dateFormat).toISOString();
291
+ }
292
+ }
293
+ else if (this.s.type.includes('luxon')) {
294
+ for (var i = 0; i < this.s.value.length; i++) {
295
+ this.s.value[i] = luxon.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toISO();
296
+ }
297
+ }
298
+ }
299
+ if (this.s.type.includes('num') && this.s.dt.page.info().serverSide) {
300
+ for (var i = 0; i < this.s.value.length; i++) {
301
+ this.s.value[i] = this.s.value[i].replace(/[^0-9.]/g, '');
302
+ }
303
+ }
261
304
  return {
262
305
  condition: this.s.condition,
263
306
  data: this.s.data,
264
307
  origData: this.s.origData,
265
- value: value
308
+ type: this.s.type,
309
+ value: this.s.value.map(function (a) { return a.toString(); })
266
310
  };
267
311
  };
268
312
  /**
@@ -302,12 +346,17 @@
302
346
  var italic_1 = this.classes.italic;
303
347
  var data_1 = this.dom.data;
304
348
  this.dom.data.children('option').each(function () {
305
- if ($$2(this).text() === loadedCriteria.data) {
306
- $$2(this).attr('selected', true);
349
+ if (!foundData &&
350
+ ($$2(this).text() === loadedCriteria.data ||
351
+ loadedCriteria.origData && $$2(this).prop('origData') === loadedCriteria.origData)) {
352
+ $$2(this).prop('selected', true);
307
353
  data_1.removeClass(italic_1);
308
354
  foundData = true;
309
355
  dataIdx = $$2(this).val();
310
356
  }
357
+ else {
358
+ $$2(this).removeProp('selected');
359
+ }
311
360
  });
312
361
  }
313
362
  // If the data has been found and selected then the condition can be populated and searched
@@ -319,25 +368,39 @@
319
368
  this.dom.dataTitle.remove();
320
369
  this._populateCondition();
321
370
  this.dom.conditionTitle.remove();
322
- var condition_1;
371
+ var condition = void 0;
323
372
  // Check to see if the previously selected condition exists, if so select it
324
- this.dom.condition.children('option').each(function () {
373
+ var options = this.dom.condition.children('option');
374
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
375
+ for (var i = 0; i < options.length; i++) {
376
+ var option = $$2(options[i]);
325
377
  if (loadedCriteria.condition !== undefined &&
326
- $$2(this).val() === loadedCriteria.condition &&
378
+ option.val() === loadedCriteria.condition &&
327
379
  typeof loadedCriteria.condition === 'string') {
328
- $$2(this).attr('selected', true);
329
- condition_1 = $$2(this).val();
380
+ option.prop('selected', true);
381
+ condition = option.val();
330
382
  }
331
- });
332
- this.s.condition = condition_1;
383
+ else {
384
+ option.removeProp('selected');
385
+ }
386
+ }
387
+ this.s.condition = condition;
333
388
  // If the condition has been found and selected then the value can be populated and searched
334
389
  if (this.s.condition !== undefined) {
390
+ this.dom.conditionTitle.removeProp('selected');
335
391
  this.dom.conditionTitle.remove();
336
392
  this.dom.condition.removeClass(this.classes.italic);
393
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
394
+ for (var i = 0; i < options.length; i++) {
395
+ var option = $$2(options[i]);
396
+ if (option.val() !== this.s.condition) {
397
+ option.removeProp('selected');
398
+ }
399
+ }
337
400
  this._populateValue(loadedCriteria);
338
401
  }
339
402
  else {
340
- this.dom.conditionTitle.prependTo(this.dom.condition).attr('selected', 'true');
403
+ this.dom.conditionTitle.prependTo(this.dom.condition).prop('selected', true);
341
404
  }
342
405
  }
343
406
  };
@@ -348,56 +411,82 @@
348
411
  var _this = this;
349
412
  this.dom.data
350
413
  .unbind('change')
351
- .on('change', function () {
352
- _this.dom.dataTitle.attr('selected', 'false');
353
- _this.dom.data.removeClass(_this.classes.italic);
354
- _this.s.dataIdx = +_this.dom.data.children('option:selected').val();
355
- _this.s.data = _this.dom.data.children('option:selected').text();
356
- _this.s.origData = _this.dom.data.children('option:selected').attr('origData');
357
- _this.c.orthogonal = _this._getOptions().orthogonal;
358
- // When the data is changed, the values in condition and value may also change so need to renew them
359
- _this._clearCondition();
360
- _this._clearValue();
361
- _this._populateCondition();
362
- // If this criteria was previously active in the search then
363
- // remove it from the search and trigger a new search
364
- if (_this.s.filled) {
365
- _this.s.filled = false;
366
- _this.s.dt.draw();
367
- _this.setListeners();
414
+ .on('change.dtsb', function () {
415
+ _this.dom.dataTitle.removeProp('selected');
416
+ // Need to go over every option to identify the correct selection
417
+ var options = _this.dom.data.children('option.' + _this.classes.option);
418
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
419
+ for (var i = 0; i < options.length; i++) {
420
+ var option = $$2(options[i]);
421
+ if (option.val() === _this.dom.data.val()) {
422
+ _this.dom.data.removeClass(_this.classes.italic);
423
+ option.prop('selected', true);
424
+ _this.s.dataIdx = +option.val();
425
+ _this.s.data = option.text();
426
+ _this.s.origData = option.prop('origData');
427
+ _this.c.orthogonal = _this._getOptions().orthogonal;
428
+ // When the data is changed, the values in condition and
429
+ // value may also change so need to renew them
430
+ _this._clearCondition();
431
+ _this._clearValue();
432
+ _this._populateCondition();
433
+ // If this criteria was previously active in the search then
434
+ // remove it from the search and trigger a new search
435
+ if (_this.s.filled) {
436
+ _this.s.filled = false;
437
+ _this.s.dt.draw();
438
+ _this.setListeners();
439
+ }
440
+ _this.s.dt.state.save();
441
+ }
442
+ else {
443
+ option.removeProp('selected');
444
+ }
368
445
  }
369
- _this.s.dt.state.save();
370
446
  });
371
447
  this.dom.condition
372
448
  .unbind('change')
373
- .on('change', function () {
374
- _this.dom.conditionTitle.attr('selected', 'false');
375
- _this.dom.condition.removeClass(_this.classes.italic);
376
- var condDisp = _this.dom.condition.children('option:selected').val();
377
- // Find the condition that has been selected and store it internally
378
- for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) {
379
- var cond = _a[_i];
380
- if (cond === condDisp) {
381
- _this.s.condition = condDisp;
382
- break;
383
- }
384
- }
385
- // When the condition is changed, the value selector may switch between
386
- // a select element and an input element
387
- _this._clearValue();
388
- _this._populateValue();
389
- for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) {
390
- var val = _c[_b];
391
- // If this criteria was previously active in the search then remove
392
- // it from the search and trigger a new search
393
- if (_this.s.filled && val !== undefined && _this.dom.container.has(val[0]).length !== 0) {
394
- _this.s.filled = false;
395
- _this.s.dt.draw();
396
- _this.setListeners();
449
+ .on('change.dtsb', function () {
450
+ _this.dom.conditionTitle.removeProp('selected');
451
+ // Need to go over every option to identify the correct selection
452
+ var options = _this.dom.condition.children('option.' + _this.classes.option);
453
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
454
+ for (var i = 0; i < options.length; i++) {
455
+ var option = $$2(options[i]);
456
+ if (option.val() === _this.dom.condition.val()) {
457
+ _this.dom.condition.removeClass(_this.classes.italic);
458
+ option.prop('selected', true);
459
+ var condDisp = option.val();
460
+ // Find the condition that has been selected and store it internally
461
+ for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) {
462
+ var cond = _a[_i];
463
+ if (cond === condDisp) {
464
+ _this.s.condition = condDisp;
465
+ break;
466
+ }
467
+ }
468
+ // When the condition is changed, the value selector may switch between
469
+ // a select element and an input element
470
+ _this._clearValue();
471
+ _this._populateValue();
472
+ for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) {
473
+ var val = _c[_b];
474
+ // If this criteria was previously active in the search then remove
475
+ // it from the search and trigger a new search
476
+ if (_this.s.filled && val !== undefined && _this.dom.container.has(val[0]).length !== 0) {
477
+ _this.s.filled = false;
478
+ _this.s.dt.draw();
479
+ _this.setListeners();
480
+ }
481
+ }
482
+ if (_this.dom.value.length === 0 ||
483
+ _this.dom.value.length === 1 && _this.dom.value[0] === undefined) {
484
+ _this.s.dt.draw();
485
+ }
486
+ }
487
+ else {
488
+ option.removeProp('selected');
397
489
  }
398
- }
399
- if (_this.dom.value.length === 0 || _this.dom.value.length === 1 && _this.dom.value[0] === undefined) {
400
- _this.s.dt.draw();
401
490
  }
402
491
  });
403
492
  };
@@ -475,7 +564,7 @@
475
564
  */
476
565
  Criteria.prototype._clearCondition = function () {
477
566
  this.dom.condition.empty();
478
- this.dom.conditionTitle.attr('selected', 'true').attr('disabled', 'true');
567
+ this.dom.conditionTitle.prop('selected', true).attr('disabled', 'true');
479
568
  this.dom.condition.prepend(this.dom.conditionTitle).prop('selectedIndex', 0);
480
569
  this.s.conditions = {};
481
570
  this.s.condition = undefined;
@@ -526,7 +615,7 @@
526
615
  }
527
616
  // Append the default valueTitle to the default select element
528
617
  this.dom.valueTitle
529
- .attr('selected', 'true');
618
+ .prop('selected', true);
530
619
  this.dom.defaultValue
531
620
  .append(this.dom.valueTitle)
532
621
  .insertAfter(this.dom.condition);
@@ -560,10 +649,19 @@
560
649
  if (conditionsLength === 0) {
561
650
  var column = +this.dom.data.children('option:selected').val();
562
651
  this.s.type = this.s.dt.columns().type().toArray()[column];
563
- // If the column type is unknown, call a draw to try reading it again
564
- if (this.s.type === null) {
565
- this.s.dt.draw(false);
566
- this.setListeners();
652
+ var colInits = this.s.dt.settings()[0].aoColumns;
653
+ if (colInits !== undefined) {
654
+ var colInit = colInits[column];
655
+ if (colInit.searchBuilderType !== undefined && colInit.searchBuilderType !== null) {
656
+ this.s.type = colInit.searchBuilderType;
657
+ }
658
+ else if (this.s.type === undefined || this.s.type === null) {
659
+ this.s.type = colInit.sType;
660
+ }
661
+ }
662
+ // If the column type is still unknown, call a draw to try reading it again
663
+ if (this.s.type === null || this.s.type === undefined) {
664
+ $$2.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
567
665
  this.s.type = this.s.dt.columns().type().toArray()[column];
568
666
  }
569
667
  // Enable the condition element
@@ -573,7 +671,7 @@
573
671
  .append(this.dom.conditionTitle)
574
672
  .addClass(this.classes.italic);
575
673
  this.dom.conditionTitle
576
- .attr('selected', 'true');
674
+ .prop('selected', true);
577
675
  var decimal = this.s.dt.settings()[0].oLanguage.sDecimal;
578
676
  // This check is in place for if a custom decimal character is in place
579
677
  if (decimal !== '' && this.s.type.indexOf(decimal) === this.s.type.length - decimal.length) {
@@ -640,7 +738,7 @@
640
738
  .addClass(this.classes.option)
641
739
  .addClass(this.classes.notItalic);
642
740
  if (this.s.condition !== undefined && this.s.condition === condName) {
643
- newOpt.attr('selected', true);
741
+ newOpt.prop('selected', true);
644
742
  this.dom.condition.removeClass(this.classes.italic);
645
743
  }
646
744
  conditionOpts.push(newOpt);
@@ -694,7 +792,11 @@
694
792
  })
695
793
  .addClass(_this.classes.option)
696
794
  .addClass(_this.classes.notItalic)
697
- .attr('origData', col.data));
795
+ .prop('origData', col.data)
796
+ .prop('selected', _this.s.dataIdx === opt.index ? true : false));
797
+ if (_this.s.dataIdx === opt.index) {
798
+ _this.dom.dataTitle.removeProp('selected');
799
+ }
698
800
  }
699
801
  }
700
802
  });
@@ -717,10 +819,11 @@
717
819
  })
718
820
  .addClass(this_1.classes.option)
719
821
  .addClass(this_1.classes.notItalic)
720
- .attr('origData', data.origData);
822
+ .prop('origData', data.origData);
721
823
  if (this_1.s.data === data.text) {
722
824
  this_1.s.dataIdx = data.index;
723
- newOpt.attr('selected', true);
825
+ this_1.dom.dataTitle.removeProp('selected');
826
+ newOpt.prop('selected', true);
724
827
  this_1.dom.data.removeClass(this_1.classes.italic);
725
828
  }
726
829
  this_1.dom.data.append(newOpt);
@@ -793,7 +896,11 @@
793
896
  this.setListeners();
794
897
  // If it can and this is different to before then trigger a draw
795
898
  if (prevFilled !== this.s.filled) {
796
- this.s.dt.draw();
899
+ // If using SSP we want to restrict the amount of server calls that take place
900
+ // and this will already have taken place
901
+ if (!this.s.dt.page.info().serverSide) {
902
+ this.s.dt.draw();
903
+ }
797
904
  this.setListeners();
798
905
  }
799
906
  };
@@ -860,6 +967,7 @@
860
967
  var column = that.dom.data.children('option:selected').val();
861
968
  var indexArray = that.s.dt.rows().indexes().toArray();
862
969
  var settings = that.s.dt.settings()[0];
970
+ that.dom.valueTitle.prop('selected', true);
863
971
  // Declare select element to be used with all of the default classes and listeners.
864
972
  var el = $$2('<select/>')
865
973
  .addClass(Criteria.classes.value)
@@ -867,7 +975,7 @@
867
975
  .addClass(Criteria.classes.italic)
868
976
  .addClass(Criteria.classes.select)
869
977
  .append(that.dom.valueTitle)
870
- .on('change', function () {
978
+ .on('change.dtsb', function () {
871
979
  $$2(this).removeClass(Criteria.classes.italic);
872
980
  fn(that, this);
873
981
  });
@@ -894,22 +1002,20 @@
894
1002
  };
895
1003
  // If we are dealing with an array type, either make sure we are working with arrays, or sort them
896
1004
  if (that.s.type === 'array') {
897
- value.filter = !Array.isArray(value.filter) ?
898
- [value.filter] :
899
- value.filter = value.filter.sort();
900
- value.text = !Array.isArray(value.text) ?
901
- [value.text] :
902
- value.text = value.text.sort();
1005
+ value.filter = !Array.isArray(value.filter) ? [value.filter] : value.filter;
1006
+ value.text = !Array.isArray(value.text) ? [value.text] : value.text;
903
1007
  }
904
1008
  // Function to add an option to the select element
905
1009
  var addOption = function (filt, text) {
1010
+ if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
1011
+ filt.replace(/(<([^>]+)>)/ig, '');
1012
+ }
906
1013
  // Add text and value, stripping out any html if that is the column type
907
1014
  var opt = $$2('<option>', {
908
1015
  type: Array.isArray(filt) ? 'Array' : 'String',
909
- value: that.s.type.includes('html') && filt !== null && typeof filt === 'string' ?
910
- filt.replace(/(<([^>]+)>)/ig, '') :
911
- filt
1016
+ value: filt
912
1017
  })
1018
+ .data('sbv', filt)
913
1019
  .addClass(that.classes.option)
914
1020
  .addClass(that.classes.notItalic)
915
1021
  // Have to add the text this way so that special html characters are not escaped - &amp; etc.
@@ -926,8 +1032,9 @@
926
1032
  }
927
1033
  // If this value was previously selected as indicated by preDefined, then select it again
928
1034
  if (preDefined !== null && opt.val() === preDefined[0]) {
929
- opt.attr('selected', true);
1035
+ opt.prop('selected', true);
930
1036
  el.removeClass(Criteria.classes.italic);
1037
+ that.dom.valueTitle.removeProp('selected');
931
1038
  }
932
1039
  }
933
1040
  };
@@ -939,7 +1046,7 @@
939
1046
  }
940
1047
  // Otherwise the value that is in the cell is to be added
941
1048
  else {
942
- addOption(value.filter, value.text);
1049
+ addOption(value.filter, Array.isArray(value.text) ? value.text.join(', ') : value.text);
943
1050
  }
944
1051
  }
945
1052
  options.sort(function (a, b) {
@@ -1005,9 +1112,12 @@
1005
1112
  var el = $$2('<input/>')
1006
1113
  .addClass(Criteria.classes.value)
1007
1114
  .addClass(Criteria.classes.input)
1008
- .on('input keypress', that._throttle(function (e) {
1115
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1009
1116
  var code = e.keyCode || e.which;
1010
- if (!that.c.enterSearch || code === 13) {
1117
+ if (!that.c.enterSearch &&
1118
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1119
+ that.s.dt.settings()[0].oInit.search["return"]) ||
1120
+ code === 13) {
1011
1121
  return fn(that, this);
1012
1122
  }
1013
1123
  }, searchDelay === null ? 100 : searchDelay));
@@ -1019,7 +1129,7 @@
1019
1129
  el.val(preDefined[0]);
1020
1130
  }
1021
1131
  // This is add responsive functionality to the logic button without redrawing everything else
1022
- that.s.dt.one('draw', function () {
1132
+ that.s.dt.one('draw.dtsb', function () {
1023
1133
  that.s.topGroup.trigger('dtsb-redrawLogic');
1024
1134
  });
1025
1135
  return el;
@@ -1035,21 +1145,27 @@
1035
1145
  $$2('<input/>')
1036
1146
  .addClass(Criteria.classes.value)
1037
1147
  .addClass(Criteria.classes.input)
1038
- .on('input keypress', that._throttle(function (e) {
1148
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1039
1149
  var code = e.keyCode || e.which;
1040
- if (!that.c.enterSearch || code === 13) {
1150
+ if (!that.c.enterSearch &&
1151
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1152
+ that.s.dt.settings()[0].oInit.search["return"]) ||
1153
+ code === 13) {
1041
1154
  return fn(that, this);
1042
1155
  }
1043
1156
  }, searchDelay === null ? 100 : searchDelay)),
1044
1157
  $$2('<span>')
1045
1158
  .addClass(that.classes.joiner)
1046
- .text(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1159
+ .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1047
1160
  $$2('<input/>')
1048
1161
  .addClass(Criteria.classes.value)
1049
1162
  .addClass(Criteria.classes.input)
1050
- .on('input keypress', that._throttle(function (e) {
1163
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1051
1164
  var code = e.keyCode || e.which;
1052
- if (!that.c.enterSearch || code === 13) {
1165
+ if (!that.c.enterSearch &&
1166
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1167
+ that.s.dt.settings()[0].oInit.search["return"]) ||
1168
+ code === 13) {
1053
1169
  return fn(that, this);
1054
1170
  }
1055
1171
  }, searchDelay === null ? 100 : searchDelay))
@@ -1064,7 +1180,7 @@
1064
1180
  els[2].val(preDefined[1]);
1065
1181
  }
1066
1182
  // This is add responsive functionality to the logic button without redrawing everything else
1067
- that.s.dt.one('draw', function () {
1183
+ that.s.dt.one('draw.dtsb', function () {
1068
1184
  that.s.topGroup.trigger('dtsb-redrawLogic');
1069
1185
  });
1070
1186
  return els;
@@ -1083,10 +1199,12 @@
1083
1199
  attachTo: 'input',
1084
1200
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1085
1201
  })
1086
- .on('change', that._throttle(function () {
1202
+ .on('change.dtsb', that._throttle(function () {
1087
1203
  return fn(that, this);
1088
1204
  }, searchDelay === null ? 100 : searchDelay))
1089
- .on('input keypress', that.c.enterSearch ?
1205
+ .on('input.dtsb keypress.dtsb', that.c.enterSearch ||
1206
+ that.s.dt.settings()[0].oInit.search !== undefined &&
1207
+ that.s.dt.settings()[0].oInit.search["return"] ?
1090
1208
  function (e) {
1091
1209
  that._throttle(function () {
1092
1210
  var code = e.keyCode || e.which;
@@ -1106,14 +1224,14 @@
1106
1224
  el.val(preDefined[0]);
1107
1225
  }
1108
1226
  // This is add responsive functionality to the logic button without redrawing everything else
1109
- that.s.dt.one('draw', function () {
1227
+ that.s.dt.one('draw.dtsb', function () {
1110
1228
  that.s.topGroup.trigger('dtsb-redrawLogic');
1111
1229
  });
1112
1230
  return el;
1113
1231
  };
1114
1232
  Criteria.initNoValue = function (that) {
1115
1233
  // This is add responsive functionality to the logic button without redrawing everything else
1116
- that.s.dt.one('draw', function () {
1234
+ that.s.dt.one('draw.dtsb', function () {
1117
1235
  that.s.topGroup.trigger('dtsb-redrawLogic');
1118
1236
  });
1119
1237
  };
@@ -1130,18 +1248,23 @@
1130
1248
  attachTo: 'input',
1131
1249
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1132
1250
  })
1133
- .on('change', searchDelay !== null ?
1251
+ .on('change.dtsb', searchDelay !== null ?
1134
1252
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1135
1253
  return fn(that, this);
1136
1254
  }, searchDelay) :
1137
1255
  function () {
1138
1256
  fn(that, _this);
1139
1257
  })
1140
- .on('input keypress', !that.c.enterSearch && searchDelay !== null ?
1258
+ .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
1259
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1260
+ that.s.dt.settings()[0].oInit.search["return"]) &&
1261
+ searchDelay !== null ?
1141
1262
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1142
1263
  return fn(that, this);
1143
1264
  }, searchDelay) :
1144
- that.c.enterSearch ?
1265
+ that.c.enterSearch ||
1266
+ that.s.dt.settings()[0].oInit.search !== undefined &&
1267
+ that.s.dt.settings()[0].oInit.search["return"] ?
1145
1268
  function (e) {
1146
1269
  var code = e.keyCode || e.which;
1147
1270
  if (code === 13) {
@@ -1153,7 +1276,7 @@
1153
1276
  }),
1154
1277
  $$2('<span>')
1155
1278
  .addClass(that.classes.joiner)
1156
- .text(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1279
+ .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1157
1280
  $$2('<input/>')
1158
1281
  .addClass(Criteria.classes.value)
1159
1282
  .addClass(Criteria.classes.input)
@@ -1161,18 +1284,23 @@
1161
1284
  attachTo: 'input',
1162
1285
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1163
1286
  })
1164
- .on('change', searchDelay !== null ?
1287
+ .on('change.dtsb', searchDelay !== null ?
1165
1288
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1166
1289
  return fn(that, this);
1167
1290
  }, searchDelay) :
1168
1291
  function () {
1169
1292
  fn(that, _this);
1170
1293
  })
1171
- .on('input keypress', !that.c.enterSearch && searchDelay !== null ?
1294
+ .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
1295
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1296
+ that.s.dt.settings()[0].oInit.search["return"]) &&
1297
+ searchDelay !== null ?
1172
1298
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1173
1299
  return fn(that, this);
1174
1300
  }, searchDelay) :
1175
- that.c.enterSearch ?
1301
+ that.c.enterSearch ||
1302
+ that.s.dt.settings()[0].oInit.search !== undefined &&
1303
+ that.s.dt.settings()[0].oInit.search["return"] ?
1176
1304
  function (e) {
1177
1305
  var code = e.keyCode || e.which;
1178
1306
  if (code === 13) {
@@ -1193,7 +1321,7 @@
1193
1321
  els[2].val(preDefined[1]);
1194
1322
  }
1195
1323
  // This is add responsive functionality to the logic button without redrawing everything else
1196
- that.s.dt.one('draw', function () {
1324
+ that.s.dt.one('draw.dtsb', function () {
1197
1325
  that.s.topGroup.trigger('dtsb-redrawLogic');
1198
1326
  });
1199
1327
  return els;
@@ -1210,7 +1338,7 @@
1210
1338
  element.children('option').length -
1211
1339
  element.children('option.' + Criteria.classes.notItalic).length &&
1212
1340
  element.children('option:selected').length === 1 &&
1213
- element.children('option:selected')[0] === element.children('option:hidden')[0]) {
1341
+ element.children('option:selected')[0] === element.children('option')[0]) {
1214
1342
  allFilled = false;
1215
1343
  }
1216
1344
  }
@@ -1239,11 +1367,7 @@
1239
1367
  for (var _i = 0, el_3 = el; _i < el_3.length; _i++) {
1240
1368
  var element = el_3[_i];
1241
1369
  if (element.is('select')) {
1242
- var val = element.children('option:selected').val();
1243
- // If the type of the option is an array we need to split it up and sort it
1244
- values.push(element.children('option:selected').attr('type') === 'Array' ?
1245
- val.split(',').sort() :
1246
- val);
1370
+ values.push(Criteria._escapeHTML(element.children('option:selected').data('sbv')));
1247
1371
  }
1248
1372
  }
1249
1373
  return values;
@@ -1257,7 +1381,7 @@
1257
1381
  for (var _i = 0, el_4 = el; _i < el_4.length; _i++) {
1258
1382
  var element = el_4[_i];
1259
1383
  if (element.is('input')) {
1260
- values.push(element.val());
1384
+ values.push(Criteria._escapeHTML(element.val()));
1261
1385
  }
1262
1386
  }
1263
1387
  return values;
@@ -2060,6 +2184,18 @@
2060
2184
  }
2061
2185
  },
2062
2186
  // eslint-disable-next-line sort-keys
2187
+ '!starts': {
2188
+ conditionName: function (dt, i18n) {
2189
+ return dt.i18n('searchBuilder.conditions.string.notStartsWith', i18n.conditions.string.notStartsWith);
2190
+ },
2191
+ init: Criteria.initInput,
2192
+ inputValue: Criteria.inputValueInput,
2193
+ isInputValid: Criteria.isInputValidInput,
2194
+ search: function (value, comparison) {
2195
+ return value.toLowerCase().indexOf(comparison[0].toLowerCase()) !== 0;
2196
+ }
2197
+ },
2198
+ // eslint-disable-next-line sort-keys
2063
2199
  'contains': {
2064
2200
  conditionName: function (dt, i18n) {
2065
2201
  return dt.i18n('searchBuilder.conditions.string.contains', i18n.conditions.string.contains);
@@ -2071,6 +2207,18 @@
2071
2207
  return value.toLowerCase().includes(comparison[0].toLowerCase());
2072
2208
  }
2073
2209
  },
2210
+ // eslint-disable-next-line sort-keys
2211
+ '!contains': {
2212
+ conditionName: function (dt, i18n) {
2213
+ return dt.i18n('searchBuilder.conditions.string.notContains', i18n.conditions.string.notContains);
2214
+ },
2215
+ init: Criteria.initInput,
2216
+ inputValue: Criteria.inputValueInput,
2217
+ isInputValid: Criteria.isInputValidInput,
2218
+ search: function (value, comparison) {
2219
+ return !value.toLowerCase().includes(comparison[0].toLowerCase());
2220
+ }
2221
+ },
2074
2222
  'ends': {
2075
2223
  conditionName: function (dt, i18n) {
2076
2224
  return dt.i18n('searchBuilder.conditions.string.endsWith', i18n.conditions.string.endsWith);
@@ -2082,6 +2230,18 @@
2082
2230
  return value.toLowerCase().endsWith(comparison[0].toLowerCase());
2083
2231
  }
2084
2232
  },
2233
+ // eslint-disable-next-line sort-keys
2234
+ '!ends': {
2235
+ conditionName: function (dt, i18n) {
2236
+ return dt.i18n('searchBuilder.conditions.string.notEndsWith', i18n.conditions.string.notEndsWith);
2237
+ },
2238
+ init: Criteria.initInput,
2239
+ inputValue: Criteria.inputValueInput,
2240
+ isInputValid: Criteria.isInputValidInput,
2241
+ search: function (value, comparison) {
2242
+ return !value.toLowerCase().endsWith(comparison[0].toLowerCase());
2243
+ }
2244
+ },
2085
2245
  'null': {
2086
2246
  conditionName: function (dt, i18n) {
2087
2247
  return dt.i18n('searchBuilder.conditions.string.empty', i18n.conditions.string.empty);
@@ -2242,10 +2402,13 @@
2242
2402
  clearAll: 'Clear All',
2243
2403
  condition: 'Condition',
2244
2404
  data: 'Data',
2405
+ "delete": '&times',
2245
2406
  deleteTitle: 'Delete filtering rule',
2407
+ left: '<',
2246
2408
  leftTitle: 'Outdent criteria',
2247
2409
  logicAnd: 'And',
2248
2410
  logicOr: 'Or',
2411
+ right: '>',
2249
2412
  rightTitle: 'Indent criteria',
2250
2413
  title: {
2251
2414
  0: 'Custom Search Builder',
@@ -2298,6 +2461,7 @@
2298
2461
  isChild: isChild,
2299
2462
  logic: undefined,
2300
2463
  opts: opts,
2464
+ preventRedraw: false,
2301
2465
  toDrop: undefined,
2302
2466
  topGroup: topGroup
2303
2467
  };
@@ -2344,7 +2508,8 @@
2344
2508
  */
2345
2509
  // Eslint upset at empty object but needs to be done
2346
2510
  // eslint-disable-next-line @typescript-eslint/ban-types
2347
- Group.prototype.getDetails = function () {
2511
+ Group.prototype.getDetails = function (deFormatDates) {
2512
+ if (deFormatDates === void 0) { deFormatDates = false; }
2348
2513
  if (this.s.criteria.length === 0) {
2349
2514
  return {};
2350
2515
  }
@@ -2355,7 +2520,7 @@
2355
2520
  // NOTE here crit could be either a subgroup or a criteria
2356
2521
  for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
2357
2522
  var crit = _a[_i];
2358
- details.criteria.push(crit.criteria.getDetails());
2523
+ details.criteria.push(crit.criteria.getDetails(deFormatDates));
2359
2524
  }
2360
2525
  return details;
2361
2526
  };
@@ -2380,7 +2545,7 @@
2380
2545
  return;
2381
2546
  }
2382
2547
  this.s.logic = loadedDetails.logic;
2383
- this.dom.logic.children().first().text(this.s.logic === 'OR'
2548
+ this.dom.logic.children().first().html(this.s.logic === 'OR'
2384
2549
  ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
2385
2550
  : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
2386
2551
  // Add all of the criteria, be it a sub group or a criteria
@@ -2408,6 +2573,9 @@
2408
2573
  * Redraws the Contents of the searchBuilder Groups and Criteria
2409
2574
  */
2410
2575
  Group.prototype.redrawContents = function () {
2576
+ if (this.s.preventRedraw) {
2577
+ return;
2578
+ }
2411
2579
  // Clear the container out and add the basic elements
2412
2580
  this.dom.container.children().detach();
2413
2581
  this.dom.container
@@ -2529,7 +2697,7 @@
2529
2697
  Group.prototype.setListeners = function () {
2530
2698
  var _this = this;
2531
2699
  this.dom.add.unbind('click');
2532
- this.dom.add.on('click', function () {
2700
+ this.dom.add.on('click.dtsb', function () {
2533
2701
  // If this is the parent group then the logic button has not been added yet
2534
2702
  if (!_this.s.isChild) {
2535
2703
  _this.dom.container.prepend(_this.dom.logicContainer);
@@ -2764,7 +2932,7 @@
2764
2932
  var _this = this;
2765
2933
  criteria.dom["delete"]
2766
2934
  .unbind('click')
2767
- .on('click', function () {
2935
+ .on('click.dtsb', function () {
2768
2936
  _this._removeCriteria(criteria);
2769
2937
  criteria.dom.container.remove();
2770
2938
  for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
@@ -2776,12 +2944,11 @@
2776
2944
  criteria.destroy();
2777
2945
  _this.s.dt.draw();
2778
2946
  _this.s.topGroup.trigger('dtsb-redrawContents');
2779
- _this.s.topGroup.trigger('dtsb-updateTitle');
2780
2947
  return false;
2781
2948
  });
2782
2949
  criteria.dom.right
2783
2950
  .unbind('click')
2784
- .on('click', function () {
2951
+ .on('click.dtsb', function () {
2785
2952
  var idx = criteria.s.index;
2786
2953
  var group = new Group(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, true, _this.s.depth + 1);
2787
2954
  // Add the criteria that is to be moved to the new group
@@ -2795,7 +2962,7 @@
2795
2962
  });
2796
2963
  criteria.dom.left
2797
2964
  .unbind('click')
2798
- .on('click', function () {
2965
+ .on('click.dtsb', function () {
2799
2966
  _this.s.toDrop = new Criteria(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index);
2800
2967
  _this.s.toDrop.s = criteria.s;
2801
2968
  _this.s.toDrop.c = criteria.c;
@@ -2820,13 +2987,12 @@
2820
2987
  var _this = this;
2821
2988
  this.dom.clear
2822
2989
  .unbind('click')
2823
- .on('click', function () {
2990
+ .on('click.dtsb', function () {
2824
2991
  if (!_this.s.isChild) {
2825
2992
  _this.dom.container.trigger('dtsb-clearContents');
2826
2993
  return false;
2827
2994
  }
2828
2995
  _this.destroy();
2829
- _this.s.topGroup.trigger('dtsb-updateTitle');
2830
2996
  _this.s.topGroup.trigger('dtsb-redrawContents');
2831
2997
  return false;
2832
2998
  });
@@ -2841,21 +3007,21 @@
2841
3007
  // Set listeners for the new group
2842
3008
  group.dom.add
2843
3009
  .unbind('click')
2844
- .on('click', function () {
3010
+ .on('click.dtsb', function () {
2845
3011
  _this.setupLogic();
2846
3012
  _this.dom.container.trigger('dtsb-add');
2847
3013
  return false;
2848
3014
  });
2849
3015
  group.dom.container
2850
3016
  .unbind('dtsb-add')
2851
- .on('dtsb-add', function () {
3017
+ .on('dtsb-add.dtsb', function () {
2852
3018
  _this.setupLogic();
2853
3019
  _this.dom.container.trigger('dtsb-add');
2854
3020
  return false;
2855
3021
  });
2856
3022
  group.dom.container
2857
3023
  .unbind('dtsb-destroy')
2858
- .on('dtsb-destroy', function () {
3024
+ .on('dtsb-destroy.dtsb', function () {
2859
3025
  _this._removeCriteria(group, true);
2860
3026
  group.dom.container.remove();
2861
3027
  _this.setupLogic();
@@ -2863,7 +3029,7 @@
2863
3029
  });
2864
3030
  group.dom.container
2865
3031
  .unbind('dtsb-dropCriteria')
2866
- .on('dtsb-dropCriteria', function () {
3032
+ .on('dtsb-dropCriteria.dtsb', function () {
2867
3033
  var toDrop = group.s.toDrop;
2868
3034
  toDrop.s.index = group.s.index;
2869
3035
  toDrop.updateArrows(_this.s.criteria.length > 1, false);
@@ -2877,8 +3043,8 @@
2877
3043
  */
2878
3044
  Group.prototype._setup = function () {
2879
3045
  this.setListeners();
2880
- this.dom.add.text(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
2881
- this.dom.logic.children().first().text(this.c.logic === 'OR'
3046
+ this.dom.add.html(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
3047
+ this.dom.logic.children().first().html(this.c.logic === 'OR'
2882
3048
  ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
2883
3049
  : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
2884
3050
  this.s.logic = this.c.logic === 'OR' ? 'OR' : 'AND';
@@ -2900,7 +3066,7 @@
2900
3066
  var _this = this;
2901
3067
  this.dom.logic
2902
3068
  .unbind('click')
2903
- .on('click', function () {
3069
+ .on('click.dtsb', function () {
2904
3070
  _this._toggleLogic();
2905
3071
  _this.s.dt.draw();
2906
3072
  for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
@@ -2915,11 +3081,11 @@
2915
3081
  Group.prototype._toggleLogic = function () {
2916
3082
  if (this.s.logic === 'OR') {
2917
3083
  this.s.logic = 'AND';
2918
- this.dom.logic.children().first().text(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
3084
+ this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
2919
3085
  }
2920
3086
  else if (this.s.logic === 'AND') {
2921
3087
  this.s.logic = 'OR';
2922
- this.dom.logic.children().first().text(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
3088
+ this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
2923
3089
  }
2924
3090
  };
2925
3091
  Group.version = '1.1.0';
@@ -2959,10 +3125,13 @@
2959
3125
  clearAll: 'Clear All',
2960
3126
  condition: 'Condition',
2961
3127
  data: 'Data',
3128
+ "delete": '&times',
2962
3129
  deleteTitle: 'Delete filtering rule',
3130
+ left: '<',
2963
3131
  leftTitle: 'Outdent criteria',
2964
3132
  logicAnd: 'And',
2965
3133
  logicOr: 'Or',
3134
+ right: '>',
2966
3135
  rightTitle: 'Indent criteria',
2967
3136
  title: {
2968
3137
  0: 'Custom Search Builder',
@@ -3031,9 +3200,15 @@
3031
3200
  return;
3032
3201
  }
3033
3202
  table.settings()[0]._searchBuilder = this;
3034
- this.s.dt.one('preXhr', function (e, settings, data) {
3035
- data.searchBuilder = _this.c.preDefined !== false ? _this.c.preDefined : null;
3036
- });
3203
+ // If using SSP we want to include the previous state in the very first server call
3204
+ if (this.s.dt.page.info().serverSide) {
3205
+ this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
3206
+ var loadedState = _this.s.dt.state.loaded();
3207
+ if (loadedState && loadedState.searchBuilder) {
3208
+ data.searchBuilder = _this._collapseArray(loadedState.searchBuilder);
3209
+ }
3210
+ });
3211
+ }
3037
3212
  // Run the remaining setup when the table is initialised
3038
3213
  if (this.s.dt.settings()[0]._bInitComplete) {
3039
3214
  this._setUp();
@@ -3050,8 +3225,9 @@
3050
3225
  */
3051
3226
  // eslint upset at empty object but that is what it is
3052
3227
  // eslint-disable-next-line @typescript-eslint/ban-types
3053
- SearchBuilder.prototype.getDetails = function () {
3054
- return this.s.topGroup.getDetails();
3228
+ SearchBuilder.prototype.getDetails = function (deFormatDates) {
3229
+ if (deFormatDates === void 0) { deFormatDates = false; }
3230
+ return this.s.topGroup.getDetails(deFormatDates);
3055
3231
  };
3056
3232
  /**
3057
3233
  * Getter for the node of the container for the searchBuilder
@@ -3072,7 +3248,10 @@
3072
3248
  if (details === undefined || details === null) {
3073
3249
  return this;
3074
3250
  }
3251
+ this.s.topGroup.s.preventRedraw = true;
3075
3252
  this.s.topGroup.rebuild(details);
3253
+ this.s.topGroup.s.preventRedraw = false;
3254
+ this.s.topGroup.redrawContents();
3076
3255
  this.s.dt.draw(false);
3077
3256
  this.s.topGroup.setListeners();
3078
3257
  return this;
@@ -3114,7 +3293,7 @@
3114
3293
  var _this = this;
3115
3294
  if (loadState === void 0) { loadState = true; }
3116
3295
  // Register an Api method for getting the column type
3117
- $.fn.DataTable.Api.registerPlural('columns().type()', 'column().type()', function (selector, opts) {
3296
+ $.fn.DataTable.Api.registerPlural('columns().type()', 'column().type()', function () {
3118
3297
  return this.iterator('column', function (settings, column) {
3119
3298
  return settings.aoColumns[column].sType;
3120
3299
  }, 1);
@@ -3123,10 +3302,17 @@
3123
3302
  // eslint-disable-next-line no-extra-parens
3124
3303
  if (!dataTable.DateTime) {
3125
3304
  var types = this.s.dt.columns().type().toArray();
3305
+ if (types === undefined || types.includes(undefined) || types.includes(null)) {
3306
+ types = [];
3307
+ for (var _i = 0, _a = this.s.dt.settings()[0].aoColumns; _i < _a.length; _i++) {
3308
+ var colInit = _a[_i];
3309
+ types.push(colInit.searchBuilderType !== undefined ? colInit.searchBuilderType : colInit.sType);
3310
+ }
3311
+ }
3126
3312
  var columnIdxs = this.s.dt.columns().toArray();
3127
3313
  // If the types are not yet set then draw to see if they can be retrieved then
3128
- if (types === undefined) {
3129
- this.s.dt.draw(false);
3314
+ if (types === undefined || types.includes(undefined) || types.includes(null)) {
3315
+ $.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
3130
3316
  types = this.s.dt.columns().type().toArray();
3131
3317
  }
3132
3318
  for (var i = 0; i < columnIdxs[0].length; i++) {
@@ -3148,18 +3334,33 @@
3148
3334
  }
3149
3335
  this.s.topGroup = new Group(this.s.dt, this.c, undefined);
3150
3336
  this._setClearListener();
3151
- this.s.dt.on('stateSaveParams', function (e, settings, data) {
3337
+ this.s.dt.on('stateSaveParams.dtsb', function (e, settings, data) {
3152
3338
  data.searchBuilder = _this.getDetails();
3153
3339
  data.page = _this.s.dt.page();
3154
3340
  });
3341
+ this.s.dt.on('stateLoadParams.dtsb', function (e, settings, data) {
3342
+ _this.rebuild(data.searchBuilder);
3343
+ });
3155
3344
  this._build();
3345
+ this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
3346
+ if (_this.s.dt.page.info().serverSide) {
3347
+ data.searchBuilder = _this._collapseArray(_this.getDetails(true));
3348
+ }
3349
+ });
3350
+ this.s.dt.on('column-reorder', function () {
3351
+ _this.rebuild(_this.getDetails());
3352
+ });
3156
3353
  if (loadState) {
3157
3354
  var loadedState = this.s.dt.state.loaded();
3158
3355
  // If the loaded State is not null rebuild based on it for statesave
3159
3356
  if (loadedState !== null && loadedState.searchBuilder !== undefined) {
3160
3357
  this.s.topGroup.rebuild(loadedState.searchBuilder);
3161
3358
  this.s.topGroup.dom.container.trigger('dtsb-redrawContents');
3162
- this.s.dt.page(loadedState.page).draw('page');
3359
+ // If using SSP we want to restrict the amount of server calls that take place
3360
+ // and this information will already have been processed
3361
+ if (!this.s.dt.page.info().serverSide) {
3362
+ this.s.dt.page(loadedState.page).draw('page');
3363
+ }
3163
3364
  this.s.topGroup.setListeners();
3164
3365
  }
3165
3366
  // Otherwise load any predefined options
@@ -3170,9 +3371,35 @@
3170
3371
  }
3171
3372
  this._setEmptyListener();
3172
3373
  this.s.dt.state.save();
3173
- this.s.dt.on('preXhr', function (e, settings, data) {
3174
- data.searchBuilder = _this.getDetails();
3175
- });
3374
+ };
3375
+ SearchBuilder.prototype._collapseArray = function (criteria) {
3376
+ if (criteria.logic === undefined) {
3377
+ if (criteria.value !== undefined) {
3378
+ criteria.value.sort(function (a, b) {
3379
+ if (!isNaN(+a)) {
3380
+ a = +a;
3381
+ b = +b;
3382
+ }
3383
+ if (a < b) {
3384
+ return -1;
3385
+ }
3386
+ else if (b < a) {
3387
+ return 1;
3388
+ }
3389
+ else {
3390
+ return 0;
3391
+ }
3392
+ });
3393
+ criteria.value1 = criteria.value[0];
3394
+ criteria.value2 = criteria.value[1];
3395
+ }
3396
+ }
3397
+ else {
3398
+ for (var i = 0; i < criteria.criteria.length; i++) {
3399
+ criteria.criteria[i] = this._collapseArray(criteria.criteria[i]);
3400
+ }
3401
+ }
3402
+ return criteria;
3176
3403
  };
3177
3404
  /**
3178
3405
  * Updates the title of the SearchBuilder
@@ -3200,7 +3427,7 @@
3200
3427
  var tableNode = this.s.dt.table(0).node();
3201
3428
  if (!$.fn.dataTable.ext.search.includes(this.s.search)) {
3202
3429
  // Custom search function for SearchBuilder
3203
- this.s.search = function (settings, searchData, dataIndex, origData) {
3430
+ this.s.search = function (settings, searchData, dataIndex) {
3204
3431
  if (settings.nTable !== tableNode) {
3205
3432
  return true;
3206
3433
  }
@@ -3209,7 +3436,7 @@
3209
3436
  // Add SearchBuilder search function to the dataTables search array
3210
3437
  $.fn.dataTable.ext.search.push(this.s.search);
3211
3438
  }
3212
- this.s.dt.on('destroy.dt', function () {
3439
+ this.s.dt.on('destroy.dtsb', function () {
3213
3440
  _this.dom.container.remove();
3214
3441
  _this.dom.clearAll.remove();
3215
3442
  var searchIdx = $.fn.dataTable.ext.search.indexOf(_this.s.search);
@@ -3217,6 +3444,8 @@
3217
3444
  $.fn.dataTable.ext.search.splice(searchIdx, 1);
3218
3445
  searchIdx = $.fn.dataTable.ext.search.indexOf(_this.s.search);
3219
3446
  }
3447
+ _this.s.dt.off('.dtsb');
3448
+ $(_this.s.dt.table().node()).off('.dtsb');
3220
3449
  });
3221
3450
  };
3222
3451
  /**
@@ -3248,7 +3477,7 @@
3248
3477
  SearchBuilder.prototype._setClearListener = function () {
3249
3478
  var _this = this;
3250
3479
  this.dom.clearAll.unbind('click');
3251
- this.dom.clearAll.on('click', function () {
3480
+ this.dom.clearAll.on('click.dtsb', function () {
3252
3481
  _this.s.topGroup = new Group(_this.s.dt, _this.c, undefined);
3253
3482
  _this._build();
3254
3483
  _this.s.dt.draw();
@@ -3265,7 +3494,7 @@
3265
3494
  SearchBuilder.prototype._setRedrawListener = function () {
3266
3495
  var _this = this;
3267
3496
  this.s.topGroup.dom.container.unbind('dtsb-redrawContents');
3268
- this.s.topGroup.dom.container.on('dtsb-redrawContents', function () {
3497
+ this.s.topGroup.dom.container.on('dtsb-redrawContents.dtsb', function () {
3269
3498
  _this._checkClear();
3270
3499
  _this.s.topGroup.redrawContents();
3271
3500
  _this.s.topGroup.setupLogic();
@@ -3273,49 +3502,49 @@
3273
3502
  var count = _this.s.topGroup.count();
3274
3503
  _this._updateTitle(count);
3275
3504
  _this._filterChanged(count);
3505
+ // If using SSP we want to restrict the amount of server calls that take place
3506
+ // and this information will already have been processed
3507
+ if (!_this.s.dt.page.info().serverSide) {
3508
+ _this.s.dt.draw();
3509
+ }
3276
3510
  _this.s.dt.state.save();
3277
3511
  });
3278
3512
  this.s.topGroup.dom.container.unbind('dtsb-redrawLogic');
3279
- this.s.topGroup.dom.container.on('dtsb-redrawLogic', function () {
3513
+ this.s.topGroup.dom.container.on('dtsb-redrawLogic.dtsb', function () {
3280
3514
  _this.s.topGroup.redrawLogic();
3281
3515
  var count = _this.s.topGroup.count();
3282
3516
  _this._updateTitle(count);
3283
3517
  _this._filterChanged(count);
3284
3518
  });
3285
3519
  this.s.topGroup.dom.container.unbind('dtsb-add');
3286
- this.s.topGroup.dom.container.on('dtsb-add', function () {
3520
+ this.s.topGroup.dom.container.on('dtsb-add.dtsb', function () {
3287
3521
  var count = _this.s.topGroup.count();
3288
3522
  _this._updateTitle(count);
3289
3523
  _this._filterChanged(count);
3290
3524
  });
3291
- this.s.dt.on('postEdit postCreate postRemove', function () {
3525
+ this.s.dt.on('postEdit.dtsb postCreate.dtsb postRemove.dtsb', function () {
3292
3526
  _this.s.topGroup.redrawContents();
3293
3527
  });
3294
3528
  this.s.topGroup.dom.container.unbind('dtsb-clearContents');
3295
- this.s.topGroup.dom.container.on('dtsb-clearContents', function () {
3529
+ this.s.topGroup.dom.container.on('dtsb-clearContents.dtsb', function () {
3296
3530
  _this._setUp(false);
3297
3531
  _this._filterChanged(0);
3298
3532
  _this.s.dt.draw();
3299
3533
  });
3300
- this.s.topGroup.dom.container.on('dtsb-updateTitle', function () {
3301
- var count = _this.s.topGroup.count();
3302
- _this._updateTitle(count);
3303
- _this._filterChanged(count);
3304
- });
3305
3534
  };
3306
3535
  /**
3307
3536
  * Sets listeners to check whether clearAll should be added or removed
3308
3537
  */
3309
3538
  SearchBuilder.prototype._setEmptyListener = function () {
3310
3539
  var _this = this;
3311
- this.s.topGroup.dom.add.on('click', function () {
3540
+ this.s.topGroup.dom.add.on('click.dtsb', function () {
3312
3541
  _this._checkClear();
3313
3542
  });
3314
- this.s.topGroup.dom.container.on('dtsb-destroy', function () {
3543
+ this.s.topGroup.dom.container.on('dtsb-destroy.dtsb', function () {
3315
3544
  _this.dom.clearAll.remove();
3316
3545
  });
3317
3546
  };
3318
- SearchBuilder.version = '1.1.0';
3547
+ SearchBuilder.version = '1.3.1';
3319
3548
  SearchBuilder.classes = {
3320
3549
  button: 'dtsb-button',
3321
3550
  clearAll: 'dtsb-clearAll',
@@ -3388,15 +3617,21 @@
3388
3617
  endsWith: 'Ends With',
3389
3618
  equals: 'Equals',
3390
3619
  not: 'Not',
3620
+ notContains: 'Does Not Contain',
3391
3621
  notEmpty: 'Not Empty',
3622
+ notEndsWith: 'Does Not End With',
3623
+ notStartsWith: 'Does Not Start With',
3392
3624
  startsWith: 'Starts With'
3393
3625
  }
3394
3626
  },
3395
3627
  data: 'Data',
3628
+ "delete": '&times',
3396
3629
  deleteTitle: 'Delete filtering rule',
3630
+ left: '<',
3397
3631
  leftTitle: 'Outdent criteria',
3398
3632
  logicAnd: 'And',
3399
3633
  logicOr: 'Or',
3634
+ right: '>',
3400
3635
  rightTitle: 'Indent criteria',
3401
3636
  title: {
3402
3637
  0: 'Custom Search Builder',
@@ -3415,7 +3650,7 @@
3415
3650
  return SearchBuilder;
3416
3651
  }());
3417
3652
 
3418
- /*! SearchBuilder 1.1.0
3653
+ /*! SearchBuilder 1.3.1
3419
3654
  * ©SpryMedia Ltd - datatables.net/license/mit
3420
3655
  */
3421
3656
  // DataTables extensions common UMD. Note that this allows for AMD, CommonJS
@@ -3471,14 +3706,17 @@
3471
3706
  };
3472
3707
  $.fn.dataTable.ext.buttons.searchBuilder = {
3473
3708
  action: function (e, dt, node, config) {
3474
- e.stopPropagation();
3475
3709
  this.popover(config._searchBuilder.getNode(), {
3476
- align: 'dt-container'
3710
+ align: 'container',
3711
+ span: 'container'
3477
3712
  });
3478
3713
  // Need to redraw the contents to calculate the correct positions for the elements
3479
3714
  if (config._searchBuilder.s.topGroup !== undefined) {
3480
3715
  config._searchBuilder.s.topGroup.dom.container.trigger('dtsb-redrawContents');
3481
3716
  }
3717
+ if (config._searchBuilder.s.topGroup.s.criteria.length === 0) {
3718
+ $('.' + $.fn.dataTable.Group.classes.add).click();
3719
+ }
3482
3720
  },
3483
3721
  config: {},
3484
3722
  init: function (dt, node, config) {
@@ -3492,11 +3730,12 @@
3492
3730
  },
3493
3731
  text: null
3494
3732
  };
3495
- apiRegister('searchBuilder.getDetails()', function () {
3733
+ apiRegister('searchBuilder.getDetails()', function (deFormatDates) {
3734
+ if (deFormatDates === void 0) { deFormatDates = false; }
3496
3735
  var ctx = this.context[0];
3497
3736
  // If SearchBuilder has not been initialised on this instance then return
3498
3737
  return ctx._searchBuilder ?
3499
- ctx._searchBuilder.getDetails() :
3738
+ ctx._searchBuilder.getDetails(deFormatDates) :
3500
3739
  null;
3501
3740
  });
3502
3741
  apiRegister('searchBuilder.rebuild()', function (details) {
@@ -3533,7 +3772,7 @@
3533
3772
  }
3534
3773
  // Attach a listener to the document which listens for DataTables initialisation
3535
3774
  // events so we can automatically initialise
3536
- $(document).on('preInit.dt.dtsp', function (e, settings, json) {
3775
+ $(document).on('preInit.dt.dtsp', function (e, settings) {
3537
3776
  if (e.namespace !== 'dt') {
3538
3777
  return;
3539
3778
  }