datatables.net-searchbuilder 1.2.2 → 1.3.2

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.2.1
1
+ /*! SearchBuilder 1.3.1
2
2
  * ©SpryMedia Ltd - datatables.net/license/mit
3
3
  */
4
4
  (function () {
@@ -47,6 +47,7 @@
47
47
  filled: false,
48
48
  index: index,
49
49
  origData: undefined,
50
+ preventRedraw: false,
50
51
  topGroup: topGroup,
51
52
  type: '',
52
53
  value: []
@@ -60,7 +61,7 @@
60
61
  .addClass(this.classes.italic)
61
62
  .attr('autocomplete', 'hacking'),
62
63
  conditionTitle: $$2('<option value="" disabled selected hidden/>')
63
- .text(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
64
+ .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
64
65
  container: $$2('<div/>')
65
66
  .addClass(this.classes.container),
66
67
  data: $$2('<select/>')
@@ -68,24 +69,28 @@
68
69
  .addClass(this.classes.dropDown)
69
70
  .addClass(this.classes.italic),
70
71
  dataTitle: $$2('<option value="" disabled selected hidden/>')
71
- .text(this.s.dt.i18n('searchBuilder.data', i18n.data)),
72
+ .html(this.s.dt.i18n('searchBuilder.data', i18n.data)),
72
73
  defaultValue: $$2('<select disabled/>')
73
74
  .addClass(this.classes.value)
74
75
  .addClass(this.classes.dropDown)
75
- .addClass(this.classes.select),
76
- "delete": $$2('<button>&times</button>')
76
+ .addClass(this.classes.select)
77
+ .addClass(this.classes.italic),
78
+ "delete": $$2('<button/>')
79
+ .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"]))
77
80
  .addClass(this.classes["delete"])
78
81
  .addClass(this.classes.button)
79
82
  .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle))
80
83
  .attr('type', 'button'),
81
84
  // eslint-disable-next-line no-useless-escape
82
- left: $$2('<button>\<</button>')
85
+ left: $$2('<button/>')
86
+ .html(this.s.dt.i18n('searchBuilder.left', i18n.left))
83
87
  .addClass(this.classes.left)
84
88
  .addClass(this.classes.button)
85
89
  .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle))
86
90
  .attr('type', 'button'),
87
91
  // eslint-disable-next-line no-useless-escape
88
- right: $$2('<button>\></button>')
92
+ right: $$2('<button/>')
93
+ .html(this.s.dt.i18n('searchBuilder.right', i18n.right))
89
94
  .addClass(this.classes.right)
90
95
  .addClass(this.classes.button)
91
96
  .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle))
@@ -97,8 +102,8 @@
97
102
  .addClass(this.classes.italic)
98
103
  .addClass(this.classes.select)
99
104
  ],
100
- valueTitle: $$2('<option value="--valueTitle--" selected/>')
101
- .text(this.s.dt.i18n('searchBuilder.value', i18n.value))
105
+ valueTitle: $$2('<option value="--valueTitle--" disabled selected hidden/>')
106
+ .html(this.s.dt.i18n('searchBuilder.value', i18n.value))
102
107
  };
103
108
  // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder
104
109
  if (this.c.greyscale) {
@@ -111,18 +116,32 @@
111
116
  }
112
117
  }
113
118
  // For responsive design, adjust the criterias properties on the following events
114
- this.s.dt.on('draw.dtsp', function () {
119
+ this.s.dt.on('draw.dtsb', function () {
115
120
  _this._adjustCriteria();
116
121
  });
117
- this.s.dt.on('buttons-action', function () {
122
+ this.s.dt.on('buttons-action.dtsb', function () {
118
123
  _this._adjustCriteria();
119
124
  });
120
- $$2(window).on('resize.dtsp', dataTable$2.util.throttle(function () {
125
+ $$2(window).on('resize.dtsb', dataTable$2.util.throttle(function () {
121
126
  _this._adjustCriteria();
122
127
  }));
123
128
  this._buildCriteria();
124
129
  return this;
125
130
  }
131
+ /**
132
+ * Escape html characters within a string
133
+ *
134
+ * @param txt the string to be escaped
135
+ * @returns the escaped string
136
+ */
137
+ Criteria._escapeHTML = function (txt) {
138
+ return txt
139
+ .toString()
140
+ .replace(/&amp;/g, '&')
141
+ .replace(/&lt;/g, '<')
142
+ .replace(/&gt;/g, '>')
143
+ .replace(/&quot;/g, '"');
144
+ };
126
145
  /**
127
146
  * Adds the left button to the criteria
128
147
  */
@@ -217,15 +236,15 @@
217
236
  filter.sort();
218
237
  for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
219
238
  var filt = filter_1[_i];
220
- if (filt) {
239
+ if (filt && typeof filt === 'string') {
221
240
  filt = filt.replace(/[\r\n\u2028]/g, ' ');
222
241
  }
223
242
  }
224
243
  }
225
- else if (filter !== null) {
244
+ else if (filter !== null && typeof filter === 'string') {
226
245
  filter = filter.replace(/[\r\n\u2028]/g, ' ');
227
246
  }
228
- if (this.s.type.includes('html')) {
247
+ if (this.s.type.includes('html') && typeof filter === 'string') {
229
248
  filter = filter.replace(/(<([^>]+)>)/ig, '');
230
249
  }
231
250
  // Not ideal, but jqueries .val() returns an empty string even
@@ -269,12 +288,20 @@
269
288
  }
270
289
  else if (this.s.type.includes('moment')) {
271
290
  for (var i = 0; i < this.s.value.length; i++) {
272
- this.s.value[i] = moment(this.s.value[i], this.s.dateFormat).toISOString();
291
+ if (this.s.value[i] &&
292
+ this.s.value[i].length > 0 &&
293
+ moment(this.s.value[i], this.s.dateFormat, true).isValid()) {
294
+ this.s.value[i] = moment(this.s.value[i], this.s.dateFormat).toISOString();
295
+ }
273
296
  }
274
297
  }
275
298
  else if (this.s.type.includes('luxon')) {
276
299
  for (var i = 0; i < this.s.value.length; i++) {
277
- this.s.value[i] = luxon.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toISO();
300
+ if (this.s.value[i] &&
301
+ this.s.value[i].length > 0 &&
302
+ luxon.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).invalid === null) {
303
+ this.s.value[i] = luxon.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toISO();
304
+ }
278
305
  }
279
306
  }
280
307
  }
@@ -288,7 +315,7 @@
288
315
  data: this.s.data,
289
316
  origData: this.s.origData,
290
317
  type: this.s.type,
291
- value: this.s.value.map(function (a) { return a.toString(); })
318
+ value: this.s.value.map(function (a) { return a !== null && a !== undefined ? a.toString() : a; })
292
319
  };
293
320
  };
294
321
  /**
@@ -328,7 +355,9 @@
328
355
  var italic_1 = this.classes.italic;
329
356
  var data_1 = this.dom.data;
330
357
  this.dom.data.children('option').each(function () {
331
- if ($$2(this).text() === loadedCriteria.data) {
358
+ if (!foundData &&
359
+ ($$2(this).text() === loadedCriteria.data ||
360
+ loadedCriteria.origData && $$2(this).prop('origData') === loadedCriteria.origData)) {
332
361
  $$2(this).prop('selected', true);
333
362
  data_1.removeClass(italic_1);
334
363
  foundData = true;
@@ -391,7 +420,7 @@
391
420
  var _this = this;
392
421
  this.dom.data
393
422
  .unbind('change')
394
- .on('change', function () {
423
+ .on('change.dtsb', function () {
395
424
  _this.dom.dataTitle.removeProp('selected');
396
425
  // Need to go over every option to identify the correct selection
397
426
  var options = _this.dom.data.children('option.' + _this.classes.option);
@@ -426,7 +455,7 @@
426
455
  });
427
456
  this.dom.condition
428
457
  .unbind('change')
429
- .on('change', function () {
458
+ .on('change.dtsb', function () {
430
459
  _this.dom.conditionTitle.removeProp('selected');
431
460
  // Need to go over every option to identify the correct selection
432
461
  var options = _this.dom.condition.children('option.' + _this.classes.option);
@@ -505,7 +534,7 @@
505
534
  hasRight && rightOffset.top !== clearOffset.top) &&
506
535
  !this.dom.container.parent().hasClass(this.classes.vertical)) {
507
536
  this.dom.container.parent().addClass(this.classes.vertical);
508
- this.s.topGroup.trigger('dtsb-redrawContents');
537
+ this.s.topGroup.trigger('dtsb-redrawContents-noDraw');
509
538
  }
510
539
  else if (buttonsLeft -
511
540
  (this.dom.data.offset().left +
@@ -514,7 +543,7 @@
514
543
  valWidth) > 15
515
544
  && this.dom.container.parent().hasClass(this.classes.vertical)) {
516
545
  this.dom.container.parent().removeClass(this.classes.vertical);
517
- this.s.topGroup.trigger('dtsb-redrawContents');
546
+ this.s.topGroup.trigger('dtsb-redrawContents-noDraw');
518
547
  }
519
548
  };
520
549
  /**
@@ -875,8 +904,12 @@
875
904
  this.s.filled = this.s.conditions[this.s.condition].isInputValid(this.dom.value, this);
876
905
  this.setListeners();
877
906
  // If it can and this is different to before then trigger a draw
878
- if (prevFilled !== this.s.filled) {
879
- this.s.dt.draw();
907
+ if (!this.s.preventRedraw && prevFilled !== this.s.filled) {
908
+ // If using SSP we want to restrict the amount of server calls that take place
909
+ // and this will already have taken place
910
+ if (!this.s.dt.page.info().serverSide) {
911
+ this.s.dt.draw();
912
+ }
880
913
  this.setListeners();
881
914
  }
882
915
  };
@@ -943,6 +976,7 @@
943
976
  var column = that.dom.data.children('option:selected').val();
944
977
  var indexArray = that.s.dt.rows().indexes().toArray();
945
978
  var settings = that.s.dt.settings()[0];
979
+ that.dom.valueTitle.prop('selected', true);
946
980
  // Declare select element to be used with all of the default classes and listeners.
947
981
  var el = $$2('<select/>')
948
982
  .addClass(Criteria.classes.value)
@@ -950,7 +984,7 @@
950
984
  .addClass(Criteria.classes.italic)
951
985
  .addClass(Criteria.classes.select)
952
986
  .append(that.dom.valueTitle)
953
- .on('change', function () {
987
+ .on('change.dtsb', function () {
954
988
  $$2(this).removeClass(Criteria.classes.italic);
955
989
  fn(that, this);
956
990
  });
@@ -977,22 +1011,20 @@
977
1011
  };
978
1012
  // If we are dealing with an array type, either make sure we are working with arrays, or sort them
979
1013
  if (that.s.type === 'array') {
980
- value.filter = !Array.isArray(value.filter) ?
981
- [value.filter] :
982
- value.filter = value.filter.sort();
983
- value.text = !Array.isArray(value.text) ?
984
- [value.text] :
985
- value.text = value.text.sort();
1014
+ value.filter = !Array.isArray(value.filter) ? [value.filter] : value.filter;
1015
+ value.text = !Array.isArray(value.text) ? [value.text] : value.text;
986
1016
  }
987
1017
  // Function to add an option to the select element
988
1018
  var addOption = function (filt, text) {
1019
+ if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
1020
+ filt.replace(/(<([^>]+)>)/ig, '');
1021
+ }
989
1022
  // Add text and value, stripping out any html if that is the column type
990
1023
  var opt = $$2('<option>', {
991
1024
  type: Array.isArray(filt) ? 'Array' : 'String',
992
- value: that.s.type.includes('html') && filt !== null && typeof filt === 'string' ?
993
- filt.replace(/(<([^>]+)>)/ig, '') :
994
- filt
1025
+ value: filt
995
1026
  })
1027
+ .data('sbv', filt)
996
1028
  .addClass(that.classes.option)
997
1029
  .addClass(that.classes.notItalic)
998
1030
  // Have to add the text this way so that special html characters are not escaped - &amp; etc.
@@ -1011,6 +1043,7 @@
1011
1043
  if (preDefined !== null && opt.val() === preDefined[0]) {
1012
1044
  opt.prop('selected', true);
1013
1045
  el.removeClass(Criteria.classes.italic);
1046
+ that.dom.valueTitle.removeProp('selected');
1014
1047
  }
1015
1048
  }
1016
1049
  };
@@ -1022,7 +1055,7 @@
1022
1055
  }
1023
1056
  // Otherwise the value that is in the cell is to be added
1024
1057
  else {
1025
- addOption(value.filter, value.text);
1058
+ addOption(value.filter, Array.isArray(value.text) ? value.text.join(', ') : value.text);
1026
1059
  }
1027
1060
  }
1028
1061
  options.sort(function (a, b) {
@@ -1088,14 +1121,9 @@
1088
1121
  var el = $$2('<input/>')
1089
1122
  .addClass(Criteria.classes.value)
1090
1123
  .addClass(Criteria.classes.input)
1091
- .on('input keypress', that._throttle(function (e) {
1124
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1092
1125
  var code = e.keyCode || e.which;
1093
- if (!that.c.enterSearch &&
1094
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
1095
- that.s.dt.settings()[0].oInit.search["return"]) ||
1096
- code === 13) {
1097
- return fn(that, this);
1098
- }
1126
+ return fn(that, this, code);
1099
1127
  }, searchDelay === null ? 100 : searchDelay));
1100
1128
  if (that.c.greyscale) {
1101
1129
  el.addClass(Criteria.classes.greyscale);
@@ -1105,7 +1133,7 @@
1105
1133
  el.val(preDefined[0]);
1106
1134
  }
1107
1135
  // This is add responsive functionality to the logic button without redrawing everything else
1108
- that.s.dt.one('draw', function () {
1136
+ that.s.dt.one('draw.dtsb', function () {
1109
1137
  that.s.topGroup.trigger('dtsb-redrawLogic');
1110
1138
  });
1111
1139
  return el;
@@ -1121,29 +1149,19 @@
1121
1149
  $$2('<input/>')
1122
1150
  .addClass(Criteria.classes.value)
1123
1151
  .addClass(Criteria.classes.input)
1124
- .on('input keypress', that._throttle(function (e) {
1152
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1125
1153
  var code = e.keyCode || e.which;
1126
- if (!that.c.enterSearch &&
1127
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
1128
- that.s.dt.settings()[0].oInit.search["return"]) ||
1129
- code === 13) {
1130
- return fn(that, this);
1131
- }
1154
+ return fn(that, this, code);
1132
1155
  }, searchDelay === null ? 100 : searchDelay)),
1133
1156
  $$2('<span>')
1134
1157
  .addClass(that.classes.joiner)
1135
- .text(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1158
+ .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1136
1159
  $$2('<input/>')
1137
1160
  .addClass(Criteria.classes.value)
1138
1161
  .addClass(Criteria.classes.input)
1139
- .on('input keypress', that._throttle(function (e) {
1162
+ .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
1140
1163
  var code = e.keyCode || e.which;
1141
- if (!that.c.enterSearch &&
1142
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
1143
- that.s.dt.settings()[0].oInit.search["return"]) ||
1144
- code === 13) {
1145
- return fn(that, this);
1146
- }
1164
+ return fn(that, this, code);
1147
1165
  }, searchDelay === null ? 100 : searchDelay))
1148
1166
  ];
1149
1167
  if (that.c.greyscale) {
@@ -1156,7 +1174,7 @@
1156
1174
  els[2].val(preDefined[1]);
1157
1175
  }
1158
1176
  // This is add responsive functionality to the logic button without redrawing everything else
1159
- that.s.dt.one('draw', function () {
1177
+ that.s.dt.one('draw.dtsb', function () {
1160
1178
  that.s.topGroup.trigger('dtsb-redrawLogic');
1161
1179
  });
1162
1180
  return els;
@@ -1175,23 +1193,15 @@
1175
1193
  attachTo: 'input',
1176
1194
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1177
1195
  })
1178
- .on('change', that._throttle(function () {
1196
+ .on('change.dtsb', that._throttle(function () {
1179
1197
  return fn(that, this);
1180
1198
  }, searchDelay === null ? 100 : searchDelay))
1181
- .on('input keypress', that.c.enterSearch ||
1182
- that.s.dt.settings()[0].oInit.search !== undefined &&
1183
- that.s.dt.settings()[0].oInit.search["return"] ?
1184
- function (e) {
1185
- that._throttle(function () {
1186
- var code = e.keyCode || e.which;
1187
- if (code === 13) {
1188
- return fn(that, this);
1189
- }
1190
- }, searchDelay === null ? 100 : searchDelay);
1191
- } :
1199
+ .on('input.dtsb keypress.dtsb', function (e) {
1192
1200
  that._throttle(function () {
1193
- return fn(that, this);
1194
- }, searchDelay === null ? 100 : searchDelay));
1201
+ var code = e.keyCode || e.which;
1202
+ return fn(that, this, code);
1203
+ }, searchDelay === null ? 100 : searchDelay);
1204
+ });
1195
1205
  if (that.c.greyscale) {
1196
1206
  el.addClass(Criteria.classes.greyscale);
1197
1207
  }
@@ -1200,14 +1210,14 @@
1200
1210
  el.val(preDefined[0]);
1201
1211
  }
1202
1212
  // This is add responsive functionality to the logic button without redrawing everything else
1203
- that.s.dt.one('draw', function () {
1213
+ that.s.dt.one('draw.dtsb', function () {
1204
1214
  that.s.topGroup.trigger('dtsb-redrawLogic');
1205
1215
  });
1206
1216
  return el;
1207
1217
  };
1208
1218
  Criteria.initNoValue = function (that) {
1209
1219
  // This is add responsive functionality to the logic button without redrawing everything else
1210
- that.s.dt.one('draw', function () {
1220
+ that.s.dt.one('draw.dtsb', function () {
1211
1221
  that.s.topGroup.trigger('dtsb-redrawLogic');
1212
1222
  });
1213
1223
  };
@@ -1224,35 +1234,22 @@
1224
1234
  attachTo: 'input',
1225
1235
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1226
1236
  })
1227
- .on('change', searchDelay !== null ?
1237
+ .on('change.dtsb', searchDelay !== null ?
1228
1238
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1229
1239
  return fn(that, this);
1230
1240
  }, searchDelay) :
1231
1241
  function () {
1232
1242
  fn(that, _this);
1233
1243
  })
1234
- .on('input keypress', !that.c.enterSearch &&
1235
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
1236
- that.s.dt.settings()[0].oInit.search["return"]) &&
1237
- searchDelay !== null ?
1244
+ .on('input.dtsb keypress.dtsb', function (e) {
1238
1245
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1239
- return fn(that, this);
1240
- }, searchDelay) :
1241
- that.c.enterSearch ||
1242
- that.s.dt.settings()[0].oInit.search !== undefined &&
1243
- that.s.dt.settings()[0].oInit.search["return"] ?
1244
- function (e) {
1245
- var code = e.keyCode || e.which;
1246
- if (code === 13) {
1247
- fn(that, _this);
1248
- }
1249
- } :
1250
- function () {
1251
- fn(that, _this);
1252
- }),
1246
+ var code = e.keyCode || e.which;
1247
+ return fn(that, this, code);
1248
+ }, searchDelay === null ? 0 : searchDelay);
1249
+ }),
1253
1250
  $$2('<span>')
1254
1251
  .addClass(that.classes.joiner)
1255
- .text(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1252
+ .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
1256
1253
  $$2('<input/>')
1257
1254
  .addClass(Criteria.classes.value)
1258
1255
  .addClass(Criteria.classes.input)
@@ -1260,32 +1257,24 @@
1260
1257
  attachTo: 'input',
1261
1258
  format: that.s.dateFormat ? that.s.dateFormat : undefined
1262
1259
  })
1263
- .on('change', searchDelay !== null ?
1260
+ .on('change.dtsb', searchDelay !== null ?
1264
1261
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1265
1262
  return fn(that, this);
1266
1263
  }, searchDelay) :
1267
1264
  function () {
1268
1265
  fn(that, _this);
1269
1266
  })
1270
- .on('input keypress', !that.c.enterSearch &&
1267
+ .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
1271
1268
  !(that.s.dt.settings()[0].oInit.search !== undefined &&
1272
1269
  that.s.dt.settings()[0].oInit.search["return"]) &&
1273
1270
  searchDelay !== null ?
1274
1271
  that.s.dt.settings()[0].oApi._fnThrottle(function () {
1275
1272
  return fn(that, this);
1276
1273
  }, searchDelay) :
1277
- that.c.enterSearch ||
1278
- that.s.dt.settings()[0].oInit.search !== undefined &&
1279
- that.s.dt.settings()[0].oInit.search["return"] ?
1280
- function (e) {
1281
- var code = e.keyCode || e.which;
1282
- if (code === 13) {
1283
- fn(that, _this);
1284
- }
1285
- } :
1286
- function () {
1287
- fn(that, _this);
1288
- })
1274
+ function (e) {
1275
+ var code = e.keyCode || e.which;
1276
+ fn(that, _this, code);
1277
+ })
1289
1278
  ];
1290
1279
  if (that.c.greyscale) {
1291
1280
  els[0].addClass(Criteria.classes.greyscale);
@@ -1297,7 +1286,7 @@
1297
1286
  els[2].val(preDefined[1]);
1298
1287
  }
1299
1288
  // This is add responsive functionality to the logic button without redrawing everything else
1300
- that.s.dt.one('draw', function () {
1289
+ that.s.dt.one('draw.dtsb', function () {
1301
1290
  that.s.topGroup.trigger('dtsb-redrawLogic');
1302
1291
  });
1303
1292
  return els;
@@ -1314,7 +1303,7 @@
1314
1303
  element.children('option').length -
1315
1304
  element.children('option.' + Criteria.classes.notItalic).length &&
1316
1305
  element.children('option:selected').length === 1 &&
1317
- element.children('option:selected')[0] === element.children('option:hidden')[0]) {
1306
+ element.children('option:selected')[0] === element.children('option')[0]) {
1318
1307
  allFilled = false;
1319
1308
  }
1320
1309
  }
@@ -1343,11 +1332,7 @@
1343
1332
  for (var _i = 0, el_3 = el; _i < el_3.length; _i++) {
1344
1333
  var element = el_3[_i];
1345
1334
  if (element.is('select')) {
1346
- var val = element.children('option:selected').val();
1347
- // If the type of the option is an array we need to split it up and sort it
1348
- values.push(element.children('option:selected').attr('type') === 'Array' ?
1349
- val.split(',').sort() :
1350
- val);
1335
+ values.push(Criteria._escapeHTML(element.children('option:selected').data('sbv')));
1351
1336
  }
1352
1337
  }
1353
1338
  return values;
@@ -1361,7 +1346,7 @@
1361
1346
  for (var _i = 0, el_4 = el; _i < el_4.length; _i++) {
1362
1347
  var element = el_4[_i];
1363
1348
  if (element.is('input')) {
1364
- values.push(element.val());
1349
+ values.push(Criteria._escapeHTML(element.val()));
1365
1350
  }
1366
1351
  }
1367
1352
  return values;
@@ -1369,14 +1354,19 @@
1369
1354
  /**
1370
1355
  * Function that is run on each element as a call back when a search should be triggered
1371
1356
  */
1372
- Criteria.updateListener = function (that, el) {
1357
+ Criteria.updateListener = function (that, el, code) {
1373
1358
  // When the value is changed the criteria is now complete so can be included in searches
1374
1359
  // Get the condition from the map based on the key that has been selected for the condition
1375
1360
  var condition = that.s.conditions[that.s.condition];
1376
1361
  that.s.filled = condition.isInputValid(that.dom.value, that);
1377
1362
  that.s.value = condition.inputValue(that.dom.value, that);
1378
1363
  if (!that.s.filled) {
1379
- that.s.dt.draw();
1364
+ if (!that.c.enterSearch &&
1365
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1366
+ that.s.dt.settings()[0].oInit.search["return"]) ||
1367
+ code === 13) {
1368
+ that.s.dt.draw();
1369
+ }
1380
1370
  return;
1381
1371
  }
1382
1372
  if (!Array.isArray(that.s.value)) {
@@ -1414,8 +1404,13 @@
1414
1404
  }
1415
1405
  }
1416
1406
  }
1417
- // Trigger a search
1418
- that.s.dt.draw();
1407
+ if (!that.c.enterSearch &&
1408
+ !(that.s.dt.settings()[0].oInit.search !== undefined &&
1409
+ that.s.dt.settings()[0].oInit.search["return"]) ||
1410
+ code === 13) {
1411
+ // Trigger a search
1412
+ that.s.dt.draw();
1413
+ }
1419
1414
  // Refocus the element and set the correct cursor position
1420
1415
  if (idx !== null) {
1421
1416
  that.dom.value[idx].removeClass(that.classes.italic);
@@ -2164,6 +2159,18 @@
2164
2159
  }
2165
2160
  },
2166
2161
  // eslint-disable-next-line sort-keys
2162
+ '!starts': {
2163
+ conditionName: function (dt, i18n) {
2164
+ return dt.i18n('searchBuilder.conditions.string.notStartsWith', i18n.conditions.string.notStartsWith);
2165
+ },
2166
+ init: Criteria.initInput,
2167
+ inputValue: Criteria.inputValueInput,
2168
+ isInputValid: Criteria.isInputValidInput,
2169
+ search: function (value, comparison) {
2170
+ return value.toLowerCase().indexOf(comparison[0].toLowerCase()) !== 0;
2171
+ }
2172
+ },
2173
+ // eslint-disable-next-line sort-keys
2167
2174
  'contains': {
2168
2175
  conditionName: function (dt, i18n) {
2169
2176
  return dt.i18n('searchBuilder.conditions.string.contains', i18n.conditions.string.contains);
@@ -2175,6 +2182,18 @@
2175
2182
  return value.toLowerCase().includes(comparison[0].toLowerCase());
2176
2183
  }
2177
2184
  },
2185
+ // eslint-disable-next-line sort-keys
2186
+ '!contains': {
2187
+ conditionName: function (dt, i18n) {
2188
+ return dt.i18n('searchBuilder.conditions.string.notContains', i18n.conditions.string.notContains);
2189
+ },
2190
+ init: Criteria.initInput,
2191
+ inputValue: Criteria.inputValueInput,
2192
+ isInputValid: Criteria.isInputValidInput,
2193
+ search: function (value, comparison) {
2194
+ return !value.toLowerCase().includes(comparison[0].toLowerCase());
2195
+ }
2196
+ },
2178
2197
  'ends': {
2179
2198
  conditionName: function (dt, i18n) {
2180
2199
  return dt.i18n('searchBuilder.conditions.string.endsWith', i18n.conditions.string.endsWith);
@@ -2186,6 +2205,18 @@
2186
2205
  return value.toLowerCase().endsWith(comparison[0].toLowerCase());
2187
2206
  }
2188
2207
  },
2208
+ // eslint-disable-next-line sort-keys
2209
+ '!ends': {
2210
+ conditionName: function (dt, i18n) {
2211
+ return dt.i18n('searchBuilder.conditions.string.notEndsWith', i18n.conditions.string.notEndsWith);
2212
+ },
2213
+ init: Criteria.initInput,
2214
+ inputValue: Criteria.inputValueInput,
2215
+ isInputValid: Criteria.isInputValidInput,
2216
+ search: function (value, comparison) {
2217
+ return !value.toLowerCase().endsWith(comparison[0].toLowerCase());
2218
+ }
2219
+ },
2189
2220
  'null': {
2190
2221
  conditionName: function (dt, i18n) {
2191
2222
  return dt.i18n('searchBuilder.conditions.string.empty', i18n.conditions.string.empty);
@@ -2346,10 +2377,13 @@
2346
2377
  clearAll: 'Clear All',
2347
2378
  condition: 'Condition',
2348
2379
  data: 'Data',
2380
+ "delete": '&times',
2349
2381
  deleteTitle: 'Delete filtering rule',
2382
+ left: '<',
2350
2383
  leftTitle: 'Outdent criteria',
2351
2384
  logicAnd: 'And',
2352
2385
  logicOr: 'Or',
2386
+ right: '>',
2353
2387
  rightTitle: 'Indent criteria',
2354
2388
  title: {
2355
2389
  0: 'Custom Search Builder',
@@ -2486,7 +2520,7 @@
2486
2520
  return;
2487
2521
  }
2488
2522
  this.s.logic = loadedDetails.logic;
2489
- this.dom.logic.children().first().text(this.s.logic === 'OR'
2523
+ this.dom.logic.children().first().html(this.s.logic === 'OR'
2490
2524
  ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
2491
2525
  : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
2492
2526
  // Add all of the criteria, be it a sub group or a criteria
@@ -2543,7 +2577,9 @@
2543
2577
  this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
2544
2578
  // Set listeners for various points
2545
2579
  this._setCriteriaListeners(crit);
2580
+ this.s.criteria[i].criteria.s.preventRedraw = this.s.preventRedraw;
2546
2581
  this.s.criteria[i].criteria.rebuild(this.s.criteria[i].criteria.getDetails());
2582
+ this.s.criteria[i].criteria.s.preventRedraw = false;
2547
2583
  }
2548
2584
  else if (crit instanceof Group && crit.s.criteria.length > 0) {
2549
2585
  // Reset the index to the new value
@@ -2552,7 +2588,9 @@
2552
2588
  // Add the sub group to the group
2553
2589
  this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
2554
2590
  // Redraw the contents of the group
2591
+ crit.s.preventRedraw = this.s.preventRedraw;
2555
2592
  crit.redrawContents();
2593
+ crit.s.preventRedraw = false;
2556
2594
  this._setGroupListeners(crit);
2557
2595
  }
2558
2596
  else {
@@ -2638,7 +2676,7 @@
2638
2676
  Group.prototype.setListeners = function () {
2639
2677
  var _this = this;
2640
2678
  this.dom.add.unbind('click');
2641
- this.dom.add.on('click', function () {
2679
+ this.dom.add.on('click.dtsb', function () {
2642
2680
  // If this is the parent group then the logic button has not been added yet
2643
2681
  if (!_this.s.isChild) {
2644
2682
  _this.dom.container.prepend(_this.dom.logicContainer);
@@ -2771,9 +2809,13 @@
2771
2809
  index: idx
2772
2810
  });
2773
2811
  // Rebuild it with the previous conditions for that criteria
2812
+ criteria.s.preventRedraw = this.s.preventRedraw;
2774
2813
  criteria.rebuild(loadedCriteria);
2814
+ criteria.s.preventRedraw = false;
2775
2815
  this.s.criteria[idx].criteria = criteria;
2776
- this.s.topGroup.trigger('dtsb-redrawContents');
2816
+ if (!this.s.preventRedraw) {
2817
+ this.s.topGroup.trigger('dtsb-redrawContents');
2818
+ }
2777
2819
  };
2778
2820
  /**
2779
2821
  * Checks And the criteria using AND logic
@@ -2873,7 +2915,7 @@
2873
2915
  var _this = this;
2874
2916
  criteria.dom["delete"]
2875
2917
  .unbind('click')
2876
- .on('click', function () {
2918
+ .on('click.dtsb', function () {
2877
2919
  _this._removeCriteria(criteria);
2878
2920
  criteria.dom.container.remove();
2879
2921
  for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
@@ -2885,12 +2927,11 @@
2885
2927
  criteria.destroy();
2886
2928
  _this.s.dt.draw();
2887
2929
  _this.s.topGroup.trigger('dtsb-redrawContents');
2888
- _this.s.topGroup.trigger('dtsb-updateTitle');
2889
2930
  return false;
2890
2931
  });
2891
2932
  criteria.dom.right
2892
2933
  .unbind('click')
2893
- .on('click', function () {
2934
+ .on('click.dtsb', function () {
2894
2935
  var idx = criteria.s.index;
2895
2936
  var group = new Group(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, true, _this.s.depth + 1);
2896
2937
  // Add the criteria that is to be moved to the new group
@@ -2904,7 +2945,7 @@
2904
2945
  });
2905
2946
  criteria.dom.left
2906
2947
  .unbind('click')
2907
- .on('click', function () {
2948
+ .on('click.dtsb', function () {
2908
2949
  _this.s.toDrop = new Criteria(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index);
2909
2950
  _this.s.toDrop.s = criteria.s;
2910
2951
  _this.s.toDrop.c = criteria.c;
@@ -2929,13 +2970,12 @@
2929
2970
  var _this = this;
2930
2971
  this.dom.clear
2931
2972
  .unbind('click')
2932
- .on('click', function () {
2973
+ .on('click.dtsb', function () {
2933
2974
  if (!_this.s.isChild) {
2934
2975
  _this.dom.container.trigger('dtsb-clearContents');
2935
2976
  return false;
2936
2977
  }
2937
2978
  _this.destroy();
2938
- _this.s.topGroup.trigger('dtsb-updateTitle');
2939
2979
  _this.s.topGroup.trigger('dtsb-redrawContents');
2940
2980
  return false;
2941
2981
  });
@@ -2950,21 +2990,21 @@
2950
2990
  // Set listeners for the new group
2951
2991
  group.dom.add
2952
2992
  .unbind('click')
2953
- .on('click', function () {
2993
+ .on('click.dtsb', function () {
2954
2994
  _this.setupLogic();
2955
2995
  _this.dom.container.trigger('dtsb-add');
2956
2996
  return false;
2957
2997
  });
2958
2998
  group.dom.container
2959
2999
  .unbind('dtsb-add')
2960
- .on('dtsb-add', function () {
3000
+ .on('dtsb-add.dtsb', function () {
2961
3001
  _this.setupLogic();
2962
3002
  _this.dom.container.trigger('dtsb-add');
2963
3003
  return false;
2964
3004
  });
2965
3005
  group.dom.container
2966
3006
  .unbind('dtsb-destroy')
2967
- .on('dtsb-destroy', function () {
3007
+ .on('dtsb-destroy.dtsb', function () {
2968
3008
  _this._removeCriteria(group, true);
2969
3009
  group.dom.container.remove();
2970
3010
  _this.setupLogic();
@@ -2972,7 +3012,7 @@
2972
3012
  });
2973
3013
  group.dom.container
2974
3014
  .unbind('dtsb-dropCriteria')
2975
- .on('dtsb-dropCriteria', function () {
3015
+ .on('dtsb-dropCriteria.dtsb', function () {
2976
3016
  var toDrop = group.s.toDrop;
2977
3017
  toDrop.s.index = group.s.index;
2978
3018
  toDrop.updateArrows(_this.s.criteria.length > 1, false);
@@ -2986,8 +3026,8 @@
2986
3026
  */
2987
3027
  Group.prototype._setup = function () {
2988
3028
  this.setListeners();
2989
- this.dom.add.text(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
2990
- this.dom.logic.children().first().text(this.c.logic === 'OR'
3029
+ this.dom.add.html(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
3030
+ this.dom.logic.children().first().html(this.c.logic === 'OR'
2991
3031
  ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
2992
3032
  : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
2993
3033
  this.s.logic = this.c.logic === 'OR' ? 'OR' : 'AND';
@@ -3009,7 +3049,7 @@
3009
3049
  var _this = this;
3010
3050
  this.dom.logic
3011
3051
  .unbind('click')
3012
- .on('click', function () {
3052
+ .on('click.dtsb', function () {
3013
3053
  _this._toggleLogic();
3014
3054
  _this.s.dt.draw();
3015
3055
  for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
@@ -3024,11 +3064,11 @@
3024
3064
  Group.prototype._toggleLogic = function () {
3025
3065
  if (this.s.logic === 'OR') {
3026
3066
  this.s.logic = 'AND';
3027
- this.dom.logic.children().first().text(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
3067
+ this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
3028
3068
  }
3029
3069
  else if (this.s.logic === 'AND') {
3030
3070
  this.s.logic = 'OR';
3031
- this.dom.logic.children().first().text(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
3071
+ this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
3032
3072
  }
3033
3073
  };
3034
3074
  Group.version = '1.1.0';
@@ -3068,10 +3108,13 @@
3068
3108
  clearAll: 'Clear All',
3069
3109
  condition: 'Condition',
3070
3110
  data: 'Data',
3111
+ "delete": '&times',
3071
3112
  deleteTitle: 'Delete filtering rule',
3113
+ left: '<',
3072
3114
  leftTitle: 'Outdent criteria',
3073
3115
  logicAnd: 'And',
3074
3116
  logicOr: 'Or',
3117
+ right: '>',
3075
3118
  rightTitle: 'Indent criteria',
3076
3119
  title: {
3077
3120
  0: 'Custom Search Builder',
@@ -3140,6 +3183,15 @@
3140
3183
  return;
3141
3184
  }
3142
3185
  table.settings()[0]._searchBuilder = this;
3186
+ // If using SSP we want to include the previous state in the very first server call
3187
+ if (this.s.dt.page.info().serverSide) {
3188
+ this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
3189
+ var loadedState = _this.s.dt.state.loaded();
3190
+ if (loadedState && loadedState.searchBuilder) {
3191
+ data.searchBuilder = _this._collapseArray(loadedState.searchBuilder);
3192
+ }
3193
+ });
3194
+ }
3143
3195
  // Run the remaining setup when the table is initialised
3144
3196
  if (this.s.dt.settings()[0]._bInitComplete) {
3145
3197
  this._setUp();
@@ -3182,6 +3234,8 @@
3182
3234
  this.s.topGroup.s.preventRedraw = true;
3183
3235
  this.s.topGroup.rebuild(details);
3184
3236
  this.s.topGroup.s.preventRedraw = false;
3237
+ this._checkClear();
3238
+ this._updateTitle(this.s.topGroup.count());
3185
3239
  this.s.topGroup.redrawContents();
3186
3240
  this.s.dt.draw(false);
3187
3241
  this.s.topGroup.setListeners();
@@ -3265,23 +3319,43 @@
3265
3319
  }
3266
3320
  this.s.topGroup = new Group(this.s.dt, this.c, undefined);
3267
3321
  this._setClearListener();
3268
- this.s.dt.on('stateSaveParams', function (e, settings, data) {
3322
+ this.s.dt.on('stateSaveParams.dtsb', function (e, settings, data) {
3269
3323
  data.searchBuilder = _this.getDetails();
3270
- data.page = _this.s.dt.page();
3324
+ if (!data.scroller) {
3325
+ data.page = _this.s.dt.page();
3326
+ }
3327
+ else {
3328
+ data.start = _this.s.dt.state().start;
3329
+ }
3330
+ });
3331
+ this.s.dt.on('stateLoadParams.dtsb', function (e, settings, data) {
3332
+ _this.rebuild(data.searchBuilder);
3271
3333
  });
3272
3334
  this._build();
3273
- this.s.dt.on('preXhr', function (e, settings, data) {
3335
+ this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
3274
3336
  if (_this.s.dt.page.info().serverSide) {
3275
3337
  data.searchBuilder = _this._collapseArray(_this.getDetails(true));
3276
3338
  }
3277
3339
  });
3340
+ this.s.dt.on('column-reorder', function () {
3341
+ _this.rebuild(_this.getDetails());
3342
+ });
3278
3343
  if (loadState) {
3279
3344
  var loadedState = this.s.dt.state.loaded();
3280
3345
  // If the loaded State is not null rebuild based on it for statesave
3281
3346
  if (loadedState !== null && loadedState.searchBuilder !== undefined) {
3282
3347
  this.s.topGroup.rebuild(loadedState.searchBuilder);
3283
3348
  this.s.topGroup.dom.container.trigger('dtsb-redrawContents');
3284
- this.s.dt.page(loadedState.page).draw('page');
3349
+ // If using SSP we want to restrict the amount of server calls that take place
3350
+ // and this information will already have been processed
3351
+ if (!this.s.dt.page.info().serverSide) {
3352
+ if (loadedState.page) {
3353
+ this.s.dt.page(loadedState.page).draw('page');
3354
+ }
3355
+ else if (this.s.dt.scroller) {
3356
+ this.s.dt.scroller().scrollToRow(loadedState.scroller.topRow);
3357
+ }
3358
+ }
3285
3359
  this.s.topGroup.setListeners();
3286
3360
  }
3287
3361
  // Otherwise load any predefined options
@@ -3357,7 +3431,7 @@
3357
3431
  // Add SearchBuilder search function to the dataTables search array
3358
3432
  $.fn.dataTable.ext.search.push(this.s.search);
3359
3433
  }
3360
- this.s.dt.on('destroy.dt', function () {
3434
+ this.s.dt.on('destroy.dtsb', function () {
3361
3435
  _this.dom.container.remove();
3362
3436
  _this.dom.clearAll.remove();
3363
3437
  var searchIdx = $.fn.dataTable.ext.search.indexOf(_this.s.search);
@@ -3365,6 +3439,8 @@
3365
3439
  $.fn.dataTable.ext.search.splice(searchIdx, 1);
3366
3440
  searchIdx = $.fn.dataTable.ext.search.indexOf(_this.s.search);
3367
3441
  }
3442
+ _this.s.dt.off('.dtsb');
3443
+ $(_this.s.dt.table().node()).off('.dtsb');
3368
3444
  });
3369
3445
  };
3370
3446
  /**
@@ -3396,7 +3472,7 @@
3396
3472
  SearchBuilder.prototype._setClearListener = function () {
3397
3473
  var _this = this;
3398
3474
  this.dom.clearAll.unbind('click');
3399
- this.dom.clearAll.on('click', function () {
3475
+ this.dom.clearAll.on('click.dtsb', function () {
3400
3476
  _this.s.topGroup = new Group(_this.s.dt, _this.c, undefined);
3401
3477
  _this._build();
3402
3478
  _this.s.dt.draw();
@@ -3413,7 +3489,7 @@
3413
3489
  SearchBuilder.prototype._setRedrawListener = function () {
3414
3490
  var _this = this;
3415
3491
  this.s.topGroup.dom.container.unbind('dtsb-redrawContents');
3416
- this.s.topGroup.dom.container.on('dtsb-redrawContents', function () {
3492
+ this.s.topGroup.dom.container.on('dtsb-redrawContents.dtsb', function () {
3417
3493
  _this._checkClear();
3418
3494
  _this.s.topGroup.redrawContents();
3419
3495
  _this.s.topGroup.setupLogic();
@@ -3421,50 +3497,61 @@
3421
3497
  var count = _this.s.topGroup.count();
3422
3498
  _this._updateTitle(count);
3423
3499
  _this._filterChanged(count);
3424
- _this.s.dt.draw();
3500
+ // If using SSP we want to restrict the amount of server calls that take place
3501
+ // and this information will already have been processed
3502
+ if (!_this.s.dt.page.info().serverSide) {
3503
+ _this.s.dt.draw();
3504
+ }
3425
3505
  _this.s.dt.state.save();
3426
3506
  });
3507
+ this.s.topGroup.dom.container.unbind('dtsb-redrawContents-noDraw');
3508
+ this.s.topGroup.dom.container.on('dtsb-redrawContents-noDraw.dtsb', function () {
3509
+ _this._checkClear();
3510
+ _this.s.topGroup.s.preventRedraw = true;
3511
+ _this.s.topGroup.redrawContents();
3512
+ _this.s.topGroup.s.preventRedraw = false;
3513
+ _this.s.topGroup.setupLogic();
3514
+ _this._setEmptyListener();
3515
+ var count = _this.s.topGroup.count();
3516
+ _this._updateTitle(count);
3517
+ _this._filterChanged(count);
3518
+ });
3427
3519
  this.s.topGroup.dom.container.unbind('dtsb-redrawLogic');
3428
- this.s.topGroup.dom.container.on('dtsb-redrawLogic', function () {
3520
+ this.s.topGroup.dom.container.on('dtsb-redrawLogic.dtsb', function () {
3429
3521
  _this.s.topGroup.redrawLogic();
3430
3522
  var count = _this.s.topGroup.count();
3431
3523
  _this._updateTitle(count);
3432
3524
  _this._filterChanged(count);
3433
3525
  });
3434
3526
  this.s.topGroup.dom.container.unbind('dtsb-add');
3435
- this.s.topGroup.dom.container.on('dtsb-add', function () {
3527
+ this.s.topGroup.dom.container.on('dtsb-add.dtsb', function () {
3436
3528
  var count = _this.s.topGroup.count();
3437
3529
  _this._updateTitle(count);
3438
3530
  _this._filterChanged(count);
3439
3531
  });
3440
- this.s.dt.on('postEdit postCreate postRemove', function () {
3532
+ this.s.dt.on('postEdit.dtsb postCreate.dtsb postRemove.dtsb', function () {
3441
3533
  _this.s.topGroup.redrawContents();
3442
3534
  });
3443
3535
  this.s.topGroup.dom.container.unbind('dtsb-clearContents');
3444
- this.s.topGroup.dom.container.on('dtsb-clearContents', function () {
3536
+ this.s.topGroup.dom.container.on('dtsb-clearContents.dtsb', function () {
3445
3537
  _this._setUp(false);
3446
3538
  _this._filterChanged(0);
3447
3539
  _this.s.dt.draw();
3448
3540
  });
3449
- this.s.topGroup.dom.container.on('dtsb-updateTitle', function () {
3450
- var count = _this.s.topGroup.count();
3451
- _this._updateTitle(count);
3452
- _this._filterChanged(count);
3453
- });
3454
3541
  };
3455
3542
  /**
3456
3543
  * Sets listeners to check whether clearAll should be added or removed
3457
3544
  */
3458
3545
  SearchBuilder.prototype._setEmptyListener = function () {
3459
3546
  var _this = this;
3460
- this.s.topGroup.dom.add.on('click', function () {
3547
+ this.s.topGroup.dom.add.on('click.dtsb', function () {
3461
3548
  _this._checkClear();
3462
3549
  });
3463
- this.s.topGroup.dom.container.on('dtsb-destroy', function () {
3550
+ this.s.topGroup.dom.container.on('dtsb-destroy.dtsb', function () {
3464
3551
  _this.dom.clearAll.remove();
3465
3552
  });
3466
3553
  };
3467
- SearchBuilder.version = '1.2.1';
3554
+ SearchBuilder.version = '1.3.1';
3468
3555
  SearchBuilder.classes = {
3469
3556
  button: 'dtsb-button',
3470
3557
  clearAll: 'dtsb-clearAll',
@@ -3537,15 +3624,21 @@
3537
3624
  endsWith: 'Ends With',
3538
3625
  equals: 'Equals',
3539
3626
  not: 'Not',
3627
+ notContains: 'Does Not Contain',
3540
3628
  notEmpty: 'Not Empty',
3629
+ notEndsWith: 'Does Not End With',
3630
+ notStartsWith: 'Does Not Start With',
3541
3631
  startsWith: 'Starts With'
3542
3632
  }
3543
3633
  },
3544
3634
  data: 'Data',
3635
+ "delete": '&times',
3545
3636
  deleteTitle: 'Delete filtering rule',
3637
+ left: '<',
3546
3638
  leftTitle: 'Outdent criteria',
3547
3639
  logicAnd: 'And',
3548
3640
  logicOr: 'Or',
3641
+ right: '>',
3549
3642
  rightTitle: 'Indent criteria',
3550
3643
  title: {
3551
3644
  0: 'Custom Search Builder',
@@ -3564,7 +3657,7 @@
3564
3657
  return SearchBuilder;
3565
3658
  }());
3566
3659
 
3567
- /*! SearchBuilder 1.2.1
3660
+ /*! SearchBuilder 1.3.1
3568
3661
  * ©SpryMedia Ltd - datatables.net/license/mit
3569
3662
  */
3570
3663
  // DataTables extensions common UMD. Note that this allows for AMD, CommonJS
@@ -3621,11 +3714,16 @@
3621
3714
  $.fn.dataTable.ext.buttons.searchBuilder = {
3622
3715
  action: function (e, dt, node, config) {
3623
3716
  this.popover(config._searchBuilder.getNode(), {
3624
- align: 'dt-container'
3717
+ align: 'container',
3718
+ span: 'container'
3625
3719
  });
3720
+ var topGroup = config._searchBuilder.s.topGroup;
3626
3721
  // Need to redraw the contents to calculate the correct positions for the elements
3627
- if (config._searchBuilder.s.topGroup !== undefined) {
3628
- config._searchBuilder.s.topGroup.dom.container.trigger('dtsb-redrawContents');
3722
+ if (topGroup !== undefined) {
3723
+ topGroup.dom.container.trigger('dtsb-redrawContents');
3724
+ }
3725
+ if (topGroup.s.criteria.length === 0) {
3726
+ $('.' + $.fn.dataTable.Group.classes.add.replace(/ /g, '.')).click();
3629
3727
  }
3630
3728
  },
3631
3729
  config: {},