datavis-glide 4.0.0-PRE.0
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.
- package/LICENSE +45 -0
- package/README.md +129 -0
- package/datavis.js +101 -0
- package/dist/wcdatavis.css +1957 -0
- package/dist/wcdatavis.min.js +1 -0
- package/global-jquery.js +4 -0
- package/ie-fixes.js +13 -0
- package/index.js +70 -0
- package/meteor.js +1 -0
- package/package.json +102 -0
- package/src/flags.js +6 -0
- package/src/graph.js +1079 -0
- package/src/graph_renderer.js +85 -0
- package/src/grid.js +2777 -0
- package/src/grid_control.js +1957 -0
- package/src/grid_filter.js +1073 -0
- package/src/grid_renderer.js +276 -0
- package/src/group_fun_win.js +121 -0
- package/src/lang/en-US.js +188 -0
- package/src/lang/es-MX.js +188 -0
- package/src/lang/fr-FR.js +188 -0
- package/src/lang/id-ID.js +188 -0
- package/src/lang/nl-NL.js +188 -0
- package/src/lang/pt-BR.js +188 -0
- package/src/lang/ru-RU.js +188 -0
- package/src/lang/th-TH.js +188 -0
- package/src/lang/vi-VN.js +188 -0
- package/src/lang/zh-Hans-CN.js +188 -0
- package/src/operations_palette.js +176 -0
- package/src/prefs_modules.js +132 -0
- package/src/reg/graph_renderer.js +17 -0
- package/src/renderers/graph/chartjs.js +457 -0
- package/src/renderers/graph/google.js +584 -0
- package/src/renderers/graph/jit.js +61 -0
- package/src/renderers/graph/svelte-gantt.js +168 -0
- package/src/renderers/grid/dummy.js +79 -0
- package/src/renderers/grid/handlebars.js +217 -0
- package/src/renderers/grid/squirrelly.js +215 -0
- package/src/renderers/grid/table/group_detail.js +1404 -0
- package/src/renderers/grid/table/group_summary.js +380 -0
- package/src/renderers/grid/table/pivot.js +915 -0
- package/src/renderers/grid/table/plain.js +1592 -0
- package/src/renderers/grid/table.js +2510 -0
- package/src/trans.js +101 -0
- package/src/ui/collapsible.js +234 -0
- package/src/ui/filters/date.js +283 -0
- package/src/ui/grid_filter.js +398 -0
- package/src/ui/popup_menu.js +224 -0
- package/src/ui/popup_window.js +572 -0
- package/src/ui/slider.js +156 -0
- package/src/ui/tabs.js +202 -0
- package/src/ui/templates.js +131 -0
- package/src/ui/toolbar.js +63 -0
- package/src/ui/toolbars/grid.js +873 -0
- package/src/ui/windows/col_config.js +341 -0
- package/src/ui/windows/debug.js +164 -0
- package/src/ui/windows/grid_table_opts.js +139 -0
- package/src/util/handlebars.js +158 -0
- package/src/util/jquery.js +630 -0
- package/src/util/misc.js +1058 -0
- package/src/util/squirrelly.js +155 -0
- package/wcdatavis.css +1601 -0
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
import _ from 'underscore';
|
|
2
|
+
import moment from 'moment';
|
|
3
|
+
|
|
4
|
+
import jQuery from 'jquery';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
deepDefaults,
|
|
8
|
+
gensym,
|
|
9
|
+
getPropDef,
|
|
10
|
+
makeSubclass,
|
|
11
|
+
mixinEventHandling,
|
|
12
|
+
mixinLogging,
|
|
13
|
+
} from './util/misc.js';
|
|
14
|
+
|
|
15
|
+
import GridFilter from './ui/grid_filter.js';
|
|
16
|
+
import DateFilter from './ui/filters/date.js';
|
|
17
|
+
|
|
18
|
+
// Filters {{{1
|
|
19
|
+
// StringTextboxGridFilter {{{2
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a filter on a single string.
|
|
23
|
+
*
|
|
24
|
+
* @class
|
|
25
|
+
* @extends GridFilter
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
var StringTextboxGridFilter = makeSubclass('StringTextboxGridFilter', GridFilter, function () {
|
|
29
|
+
var self = this;
|
|
30
|
+
|
|
31
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
32
|
+
|
|
33
|
+
self.input = jQuery('<input type="text">');
|
|
34
|
+
self.input.on('change', function (evt) {
|
|
35
|
+
self.gridFilterSet.update(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
self.operatorDrop = self.makeOperatorDrop(/*['$eq', '$ne']*/);
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
self.strictChkbox = jQuery('<input>', {id: gensym(), type: 'checkbox'})
|
|
42
|
+
.on('change', function () {
|
|
43
|
+
self.gridFilterSet.update();
|
|
44
|
+
});
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
self.div
|
|
48
|
+
.append(self.operatorDrop)
|
|
49
|
+
.append(self.input);
|
|
50
|
+
|
|
51
|
+
if (self.removeBtn) {
|
|
52
|
+
self.div.append(self.removeBtn);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// StringDropdownGridFilterChosen {{{2
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Represents a filter for multiple strings.
|
|
60
|
+
*
|
|
61
|
+
* @class
|
|
62
|
+
* @extends GridFilter
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
var StringDropdownGridFilterChosen = makeSubclass('StringDropdownGridFilterChosen', GridFilter, function () {
|
|
66
|
+
var self = this;
|
|
67
|
+
|
|
68
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
69
|
+
|
|
70
|
+
self.limit = 1;
|
|
71
|
+
self.input = jQuery('<select>').attr({
|
|
72
|
+
'multiple': true
|
|
73
|
+
});
|
|
74
|
+
self.input.on('change', function (evt) {
|
|
75
|
+
self.gridFilterSet.update(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
self.div
|
|
79
|
+
.append(self.input);
|
|
80
|
+
|
|
81
|
+
if (self.removeBtn) {
|
|
82
|
+
self.div.append(self.removeBtn);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
self.afterAdd = function (target) {
|
|
86
|
+
self.gridFilterSet.view.getUniqueVals(function (uniqueVals) {
|
|
87
|
+
_.each(getPropDef([], uniqueVals, self.field, 'values'), function (val) {
|
|
88
|
+
jQuery('<option>').attr({
|
|
89
|
+
'value': val
|
|
90
|
+
}).text(val).appendTo(self.input);
|
|
91
|
+
});
|
|
92
|
+
self.input.chosen({'width': self.div.innerWidth() - self.removeBtn.outerWidth()});
|
|
93
|
+
self.chosen = self.input.next('div.chosen-container');
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (self.gridFilterSet.gridTable) {
|
|
98
|
+
self.gridFilterSet.gridTable.on('columnResize', function () {
|
|
99
|
+
var targetWidth = self.opts.sizingElement.innerWidth() - self.removeBtn.outerWidth() - 14;
|
|
100
|
+
self.logDebug(self.makeLogTag() + ' Adjusting Chosen widget width to ' + targetWidth + 'px to match column width');
|
|
101
|
+
self.chosen.innerWidth(targetWidth);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// #getOperator {{{3
|
|
107
|
+
|
|
108
|
+
StringDropdownGridFilterChosen.prototype.getOperator = function () {
|
|
109
|
+
return '$in';
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// #getValue {{{3
|
|
113
|
+
|
|
114
|
+
StringDropdownGridFilterChosen.prototype.getValue = function () {
|
|
115
|
+
var self = this
|
|
116
|
+
, val = self.super['GridFilter'].getValue(self);
|
|
117
|
+
|
|
118
|
+
return val === null ? undefined : val;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// StringDropdownGridFilterSumo {{{2
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Represents a filter for multiple strings.
|
|
125
|
+
*
|
|
126
|
+
* @class
|
|
127
|
+
* @extends GridFilter
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
var StringDropdownGridFilterSumo = makeSubclass('StringDropdownGridFilterSumo', GridFilter, function () {
|
|
131
|
+
var self = this;
|
|
132
|
+
|
|
133
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
134
|
+
|
|
135
|
+
self.limit = 1;
|
|
136
|
+
self.minDropdownWidth = 200;
|
|
137
|
+
self.input = jQuery('<select>').attr({
|
|
138
|
+
'multiple': true
|
|
139
|
+
})
|
|
140
|
+
.on('change', function (evt) {
|
|
141
|
+
if (self.pleaseDontFireChangeEvent) {
|
|
142
|
+
delete self.pleaseDontFireChangeEvent;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
self.gridFilterSet.update(false);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
self.operatorDrop = self.makeOperatorDrop(['$in', '$nin', '$exists', '$notexists']);
|
|
149
|
+
|
|
150
|
+
self.div.append(self.operatorDrop);
|
|
151
|
+
self.div.append(self.input);
|
|
152
|
+
|
|
153
|
+
if (self.removeBtn) {
|
|
154
|
+
self.div.append(self.removeBtn);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
self.afterAdd = function (target) {
|
|
158
|
+
self.gridFilterSet.view.getUniqueVals(function (uniqueVals) {
|
|
159
|
+
_.each(getPropDef([], uniqueVals, self.field, 'values'), function (val) {
|
|
160
|
+
var elt = jQuery('<option>').attr({
|
|
161
|
+
'value': val
|
|
162
|
+
});
|
|
163
|
+
var fcc = self.gridFilterSet.view.colConfig.get(self.field) || {};
|
|
164
|
+
elt[fcc.allowHtml ? 'html' : 'text'](val === '' ? '[blank]' : val);
|
|
165
|
+
elt.appendTo(self.input);
|
|
166
|
+
});
|
|
167
|
+
self.input.SumoSelect({
|
|
168
|
+
triggerChangeCombined: true,
|
|
169
|
+
selectAll: true,
|
|
170
|
+
search: true,
|
|
171
|
+
okCancelInMulti: true,
|
|
172
|
+
isClickAwayOk: true
|
|
173
|
+
});
|
|
174
|
+
self.optWrapper = self.input.closest('div.SumoSelect').find('div.optWrapper');
|
|
175
|
+
/*
|
|
176
|
+
optWrapper.resizable({
|
|
177
|
+
helper: 'ui-resizable-helper'
|
|
178
|
+
});
|
|
179
|
+
*/
|
|
180
|
+
//self.adjustInputWidth();
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// #adjustInputWidth {{{3
|
|
186
|
+
|
|
187
|
+
StringDropdownGridFilterSumo.prototype.adjustInputWidth = function (opts) {
|
|
188
|
+
var self = this;
|
|
189
|
+
|
|
190
|
+
if (opts === undefined) {
|
|
191
|
+
opts = {};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
opts.input = self.input.closest('div.SumoSelect');
|
|
195
|
+
opts.callback = function (width) {
|
|
196
|
+
self.optWrapper.outerWidth(Math.max(width, self.minDropdownWidth));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
self.super['GridFilter'].adjustInputWidth(opts);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// #getValue {{{3
|
|
203
|
+
|
|
204
|
+
StringDropdownGridFilterSumo.prototype.getValue = function () {
|
|
205
|
+
var self = this
|
|
206
|
+
, val = self.super['GridFilter'].getValue();
|
|
207
|
+
|
|
208
|
+
return val === null ? undefined : val;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// #setValue {{{3
|
|
212
|
+
|
|
213
|
+
StringDropdownGridFilterSumo.prototype.setValue = function (val) {
|
|
214
|
+
var self = this;
|
|
215
|
+
|
|
216
|
+
if (!_.isArray(val)) {
|
|
217
|
+
val = [val];
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
val = _.flatten(val);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
_.each(val, function (v) {
|
|
224
|
+
if (typeof v === 'string') {
|
|
225
|
+
self.pleaseDontFireChangeEvent = true;
|
|
226
|
+
self.input.get(0).sumo.selectItem(v);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// #setOperator {{{3
|
|
232
|
+
|
|
233
|
+
StringDropdownGridFilterSumo.prototype.setOperator = function (op) {
|
|
234
|
+
var self = this;
|
|
235
|
+
|
|
236
|
+
if (op === '$eq') {
|
|
237
|
+
op = '$in';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return self.super['GridFilter'].setOperator(op);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// #showInput {{{3
|
|
244
|
+
|
|
245
|
+
StringDropdownGridFilterSumo.prototype.showInput = function (op) {
|
|
246
|
+
var self = this;
|
|
247
|
+
|
|
248
|
+
self.input.closest('div.SumoSelect').show();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// #hideInput {{{3
|
|
252
|
+
|
|
253
|
+
StringDropdownGridFilterSumo.prototype.hideInput = function (op) {
|
|
254
|
+
var self = this;
|
|
255
|
+
|
|
256
|
+
self.input.closest('div.SumoSelect').hide();
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// NumberTextboxGridFilter {{{2
|
|
260
|
+
|
|
261
|
+
var NumberTextboxGridFilter = makeSubclass('NumberTextboxGridFilter', GridFilter, function () {
|
|
262
|
+
var self = this;
|
|
263
|
+
|
|
264
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
265
|
+
|
|
266
|
+
self.input = jQuery('<input>', {
|
|
267
|
+
'type': 'text'
|
|
268
|
+
});
|
|
269
|
+
self.input.attr('size', '10');
|
|
270
|
+
self.input.on('change', function (evt) {
|
|
271
|
+
self.gridFilterSet.update(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
self.operatorDrop = self.makeOperatorDrop(['$eq', '$ne', '$lt', '$lte', '$gt', '$gte', '$exists', '$notexists']);
|
|
275
|
+
|
|
276
|
+
self.div.append(self.operatorDrop);
|
|
277
|
+
self.div.append(self.input);
|
|
278
|
+
|
|
279
|
+
if (self.removeBtn) {
|
|
280
|
+
self.div.append(self.removeBtn);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// NumberCheckboxGridFilter {{{2
|
|
285
|
+
|
|
286
|
+
var NumberCheckboxGridFilter = makeSubclass('NumberCheckboxGridFilter', GridFilter, function () {
|
|
287
|
+
var self = this;
|
|
288
|
+
|
|
289
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
290
|
+
|
|
291
|
+
self.input = jQuery('<input>', {'id': gensym(), 'type': 'checkbox'});
|
|
292
|
+
self.input.on('change', function () {
|
|
293
|
+
self.gridFilterSet.update(false);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
self.div
|
|
297
|
+
.append(jQuery('<label>')
|
|
298
|
+
.append(self.input)
|
|
299
|
+
.append(' Filter'));
|
|
300
|
+
|
|
301
|
+
if (self.removeBtn) {
|
|
302
|
+
self.div.append(self.removeBtn);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
//self.applyImmediately = true;
|
|
306
|
+
self.limit = 1;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// #getValue {{{3
|
|
310
|
+
|
|
311
|
+
NumberCheckboxGridFilter.prototype.getValue = function () {
|
|
312
|
+
return this.input[0].checked ? 1 : 0;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// #getOperator {{{3
|
|
316
|
+
|
|
317
|
+
NumberCheckboxGridFilter.prototype.getOperator = function () {
|
|
318
|
+
return '$eq';
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// NumberTriBoolGridFilter {{{2
|
|
322
|
+
|
|
323
|
+
var NumberTriBoolGridFilter = makeSubclass('NumberTriBoolGridFilter', GridFilter, function () {
|
|
324
|
+
var self = this;
|
|
325
|
+
|
|
326
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
327
|
+
|
|
328
|
+
self.inputName = gensym();
|
|
329
|
+
|
|
330
|
+
var trueRadio = jQuery('<input>', {'type': 'radio', 'name': self.inputName, 'value': 'true'});
|
|
331
|
+
var falseRadio = jQuery('<input>', {'type': 'radio', 'name': self.inputName, 'value': 'false'});
|
|
332
|
+
var bothRadio = jQuery('<input>', {'type': 'radio', 'name': self.inputName, 'value': 'both'});
|
|
333
|
+
|
|
334
|
+
self.inputs = jQuery([trueRadio.get(0), falseRadio.get(0), bothRadio.get(0)]);
|
|
335
|
+
|
|
336
|
+
self.inputs.css('margin-right', '0.4em');
|
|
337
|
+
|
|
338
|
+
self.inputs.each(function (i, elt) {
|
|
339
|
+
elt = jQuery(elt);
|
|
340
|
+
|
|
341
|
+
elt.on('change', function (evt) {
|
|
342
|
+
self.gridFilterSet.update(false);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
self.div
|
|
347
|
+
.append(jQuery('<label>')
|
|
348
|
+
.append(trueRadio)
|
|
349
|
+
.append('True'))
|
|
350
|
+
.append(jQuery('<label>')
|
|
351
|
+
.css('padding-left', '0.8em')
|
|
352
|
+
.append(falseRadio)
|
|
353
|
+
.append('False'))
|
|
354
|
+
.append(jQuery('<label>')
|
|
355
|
+
.css('padding-left', '0.8em')
|
|
356
|
+
.append(bothRadio)
|
|
357
|
+
.append('Both'))
|
|
358
|
+
;
|
|
359
|
+
|
|
360
|
+
if (self.removeBtn) {
|
|
361
|
+
self.div.append(self.removeBtn);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
//self.applyImmediately = true;
|
|
365
|
+
self.limit = 1;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// #getValue {{{3
|
|
369
|
+
|
|
370
|
+
NumberTriBoolGridFilter.prototype.getValue = function () {
|
|
371
|
+
var self = this;
|
|
372
|
+
|
|
373
|
+
var val = self.inputs.filter(':checked').val();
|
|
374
|
+
|
|
375
|
+
switch (val) {
|
|
376
|
+
case 'true':
|
|
377
|
+
return 1;
|
|
378
|
+
case 'false':
|
|
379
|
+
return 0;
|
|
380
|
+
case 'both':
|
|
381
|
+
return undefined;
|
|
382
|
+
default:
|
|
383
|
+
throw new Error('Impossible');
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// #setValue {{{3
|
|
388
|
+
|
|
389
|
+
NumberTriBoolGridFilter.prototype.setValue = function (val) {
|
|
390
|
+
var self = this;
|
|
391
|
+
|
|
392
|
+
var internalVal;
|
|
393
|
+
|
|
394
|
+
switch (val) {
|
|
395
|
+
case 0:
|
|
396
|
+
internalVal = 'false';
|
|
397
|
+
break;
|
|
398
|
+
case 1:
|
|
399
|
+
internalVal = 'true';
|
|
400
|
+
break;
|
|
401
|
+
default:
|
|
402
|
+
internalVal = 'both';
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
self.inputs.filter('[value="' + internalVal + '"]').prop('checked', true);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// #getOperator {{{3
|
|
409
|
+
|
|
410
|
+
NumberTriBoolGridFilter.prototype.getOperator = function () {
|
|
411
|
+
return '$eq';
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// DateSingleGridFilter {{{2
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Represents a filter for a single date.
|
|
418
|
+
*
|
|
419
|
+
* @class
|
|
420
|
+
* @extends GridFilter
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
var DateSingleGridFilter = makeSubclass('DateSingleGridFilter', GridFilter, function () {
|
|
424
|
+
var self = this;
|
|
425
|
+
|
|
426
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
427
|
+
|
|
428
|
+
self.input = jQuery('<input>').attr({
|
|
429
|
+
'type': 'text',
|
|
430
|
+
'placeholder': 'Select date...'
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
self.input.flatpickr({
|
|
434
|
+
'altInput': false,
|
|
435
|
+
'allowInput': true,
|
|
436
|
+
'onChange': function (selectedDates, dateStr, instance) {
|
|
437
|
+
console.log(selectedDates, dateStr);
|
|
438
|
+
//self.gridFilterSet.update();
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
self.div
|
|
443
|
+
.append(self.input);
|
|
444
|
+
|
|
445
|
+
if (self.removeBtn) {
|
|
446
|
+
self.div.append(self.removeBtn);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// DateRangeGridFilter {{{2
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Represents a filter for a range of dates.
|
|
454
|
+
*
|
|
455
|
+
* @class
|
|
456
|
+
* @extends GridFilter
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
var DateRangeGridFilter = makeSubclass('DateRangeGridFilter', GridFilter, function () {
|
|
460
|
+
var self = this;
|
|
461
|
+
|
|
462
|
+
self.super['GridFilter'].ctor.apply(self, arguments);
|
|
463
|
+
|
|
464
|
+
self.limit = 1;
|
|
465
|
+
|
|
466
|
+
self.input = jQuery('<input>').attr({
|
|
467
|
+
'type': 'text',
|
|
468
|
+
'placeholder': 'Click here; pick start/end dates.',
|
|
469
|
+
'size': 28
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
self.widget = self.input.flatpickr({
|
|
473
|
+
'altInput': false,
|
|
474
|
+
'allowInput': true,
|
|
475
|
+
'mode': 'range',
|
|
476
|
+
'onChange': function (selectedDates, dateStr, instance) {
|
|
477
|
+
self.selectedDates = selectedDates;
|
|
478
|
+
self.gridFilterSet.update(false);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
self.div
|
|
483
|
+
.append(self.input);
|
|
484
|
+
|
|
485
|
+
if (self.removeBtn) {
|
|
486
|
+
self.div.append(self.removeBtn);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// #getValue {{{3
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Get the value(s) for this date range filter. After you bring up the calendar, when you select
|
|
494
|
+
* the start date, the "onChange" event handler is run. When you select the end date, the event is
|
|
495
|
+
* fired again. So, we use #isRange() to decide if you've only selected one date, or if you've just
|
|
496
|
+
* picked the second. When it's a range, we need to produce an object, instead of a simple value.
|
|
497
|
+
*
|
|
498
|
+
* @returns {GridFilter~Value|GridFilter~RangeValue} The value that should be used for filtering all
|
|
499
|
+
* the data in the grid.
|
|
500
|
+
*/
|
|
501
|
+
|
|
502
|
+
DateRangeGridFilter.prototype.getValue = function () {
|
|
503
|
+
var self = this
|
|
504
|
+
, result;
|
|
505
|
+
|
|
506
|
+
if (self.selectedDates == null) {
|
|
507
|
+
return undefined;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (self.isRange()) {
|
|
511
|
+
result = {
|
|
512
|
+
'start': moment(self.selectedDates[0]),
|
|
513
|
+
'end': moment(self.selectedDates[1]).hour(23).minute(59).second(59)
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
if (self.typeInfo.internalType === 'string') {
|
|
517
|
+
if (self.typeInfo.type === 'date') {
|
|
518
|
+
result = _.mapObject(result, function (m) {
|
|
519
|
+
return m.format('YYYY-MM-DD');
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
else if (self.typeInfo.type === 'datetime') {
|
|
523
|
+
result = _.mapObject(result, function (m) {
|
|
524
|
+
return m.format('YYYY-MM-DD HH:mm:ss');
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
result = moment(self.selectedDates[0]);
|
|
531
|
+
|
|
532
|
+
if (self.typeInfo.internalType === 'string') {
|
|
533
|
+
if (self.typeInfo.type === 'date') {
|
|
534
|
+
result = result.format('YYYY-MM-DD');
|
|
535
|
+
}
|
|
536
|
+
else if (self.typeInfo.type === 'datetime') {
|
|
537
|
+
result = result.format('YYYY-MM-DD HH:mm:ss');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return result;
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
// #setValue {{{3
|
|
546
|
+
|
|
547
|
+
DateRangeGridFilter.prototype.setValue = function (val) {
|
|
548
|
+
var self = this;
|
|
549
|
+
|
|
550
|
+
self.selectedDates = val;
|
|
551
|
+
self.widget.setDate(val);
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// #getOperator {{{3
|
|
555
|
+
|
|
556
|
+
DateRangeGridFilter.prototype.getOperator = function () {
|
|
557
|
+
var self = this;
|
|
558
|
+
|
|
559
|
+
if (self.isRange()) {
|
|
560
|
+
return ['$gte', '$lte'];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return '$gte';
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// #isRange {{{3
|
|
567
|
+
|
|
568
|
+
DateRangeGridFilter.prototype.isRange = function () {
|
|
569
|
+
var self = this;
|
|
570
|
+
|
|
571
|
+
return self.selectedDates != null && self.selectedDates.length > 1;
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// BooleanCheckboxGridFilter {{{2
|
|
575
|
+
|
|
576
|
+
var BooleanCheckboxGridFilter = makeSubclass('BooleanCheckboxGridFilter', GridFilter, function (field, gridFilter) {
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// #getValue {{{3
|
|
580
|
+
|
|
581
|
+
BooleanCheckboxGridFilter.prototype.getValue = function () {
|
|
582
|
+
return this.input.val();
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
// #getOperator {{{3
|
|
586
|
+
|
|
587
|
+
BooleanCheckboxGridFilter.prototype.getOperator = function () {
|
|
588
|
+
return '$eq';
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// #getOperator {{{3
|
|
592
|
+
|
|
593
|
+
BooleanCheckboxGridFilter.prototype.getId = function () {
|
|
594
|
+
return this.input.attr('id');
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// Widget Map {{{2
|
|
598
|
+
|
|
599
|
+
// Type -> Filter Widget -> Constructor
|
|
600
|
+
|
|
601
|
+
GridFilter.widgets = {
|
|
602
|
+
'string': {
|
|
603
|
+
'textbox': StringTextboxGridFilter,
|
|
604
|
+
'dropdown': StringDropdownGridFilterSumo,
|
|
605
|
+
},
|
|
606
|
+
'number': {
|
|
607
|
+
'textbox': NumberTextboxGridFilter,
|
|
608
|
+
'checkbox': NumberCheckboxGridFilter,
|
|
609
|
+
'tribool': NumberTriBoolGridFilter,
|
|
610
|
+
},
|
|
611
|
+
'currency': {
|
|
612
|
+
'textbox': NumberTextboxGridFilter,
|
|
613
|
+
},
|
|
614
|
+
'date': {
|
|
615
|
+
'single': DateSingleGridFilter,
|
|
616
|
+
'range': DateRangeGridFilter,
|
|
617
|
+
'neon': DateFilter,
|
|
618
|
+
},
|
|
619
|
+
'datetime': {
|
|
620
|
+
'single': DateSingleGridFilter,
|
|
621
|
+
'range': DateRangeGridFilter,
|
|
622
|
+
'neon': DateFilter,
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
GridFilter.defaultWidgets = {
|
|
627
|
+
'string': 'dropdown',
|
|
628
|
+
'number': 'textbox',
|
|
629
|
+
'currency': 'textbox',
|
|
630
|
+
'date': 'neon',
|
|
631
|
+
'datetime': 'neon'
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// GridFilterSet {{{1
|
|
635
|
+
// Constructor {{{2
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Create a new collection of filters.
|
|
639
|
+
*
|
|
640
|
+
* @param {View} view
|
|
641
|
+
* The view that we will be updating the filter for.
|
|
642
|
+
*
|
|
643
|
+
* @param {Prefs} prefs
|
|
644
|
+
*
|
|
645
|
+
*
|
|
646
|
+
* @param {GridTable} gridTable A reference to the table that this filter set is displayed on. This
|
|
647
|
+
* is used only to make sure that the widgets shown in the columns are resized correctly when the
|
|
648
|
+
* table's columns change width.
|
|
649
|
+
*
|
|
650
|
+
* @param {object} progress An object describing how to show a progress dialog when the view is
|
|
651
|
+
* updated.
|
|
652
|
+
*
|
|
653
|
+
* @class
|
|
654
|
+
* @property {View} view
|
|
655
|
+
* The view that we will be updating the filter for.
|
|
656
|
+
*
|
|
657
|
+
* @property {Prefs} prefs
|
|
658
|
+
*
|
|
659
|
+
* @property {object} progress An object describing how to show a progress dialog when the view is
|
|
660
|
+
* updated.
|
|
661
|
+
*
|
|
662
|
+
* @property {Element} thead
|
|
663
|
+
*
|
|
664
|
+
* @property {Object} filters Stores the filters that are within this set, with different properties
|
|
665
|
+
* to facilitate different lookup methods.
|
|
666
|
+
*
|
|
667
|
+
* @property {Array} filters.all An array of all the filters.
|
|
668
|
+
*
|
|
669
|
+
* @property {Object} filters.byId An object indexing all the filters by its internal ID.
|
|
670
|
+
*
|
|
671
|
+
* @property {Object.<Array>} filters.byCol An object indexing all the filters by the column that
|
|
672
|
+
* they're filtering.
|
|
673
|
+
*
|
|
674
|
+
* @property {boolean} delayUpdate If true, calls to the update() method do nothing. This is used
|
|
675
|
+
* internally when loading preferences to avoid updating for every single filter.
|
|
676
|
+
*/
|
|
677
|
+
|
|
678
|
+
var GridFilterSet = makeSubclass('GridFilterSet', Object, function (view, prefs, gridTable, progress, opts) {
|
|
679
|
+
var self = this;
|
|
680
|
+
|
|
681
|
+
self.view = view;
|
|
682
|
+
self.prefs = prefs;
|
|
683
|
+
self.gridTable = gridTable;
|
|
684
|
+
self.progress = progress;
|
|
685
|
+
self.opts = deepDefaults(opts, {
|
|
686
|
+
sendEvent: true,
|
|
687
|
+
dontSendEventTo: [],
|
|
688
|
+
updateData: true
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
self.filters = {
|
|
692
|
+
all: [],
|
|
693
|
+
byId: {},
|
|
694
|
+
byCol: {}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
self.delayUpdate = false;
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
mixinLogging(GridFilterSet);
|
|
701
|
+
|
|
702
|
+
// Events {{{2
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Fired when a filter has been added.
|
|
706
|
+
*
|
|
707
|
+
* @event GridFilterSet#filterAdded
|
|
708
|
+
*/
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Fired when a filter has been removed.
|
|
712
|
+
*
|
|
713
|
+
* @event GridFilterSet#filterRemoved
|
|
714
|
+
*/
|
|
715
|
+
|
|
716
|
+
mixinEventHandling(GridFilterSet, [
|
|
717
|
+
'filterAdded'
|
|
718
|
+
, 'filterRemoved'
|
|
719
|
+
, 'widgetResizedHoriz'
|
|
720
|
+
, 'widgetResizedVert'
|
|
721
|
+
]);
|
|
722
|
+
|
|
723
|
+
// #add {{{2
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Add a new filter to this set. This creates the user interface elements and places them in the
|
|
727
|
+
* appropriate place in the grid.
|
|
728
|
+
*
|
|
729
|
+
* @param {string} field Name of the column to filter on.
|
|
730
|
+
*
|
|
731
|
+
* @param {Element} target Where to place the filter widget.
|
|
732
|
+
*
|
|
733
|
+
* @param {string} [filterType] The developer's requested filter type. If missing, we use the first
|
|
734
|
+
* one from the "allowed" list. If present, and not in the allowed list, you'll get an error.
|
|
735
|
+
*
|
|
736
|
+
* @param {Element} filterBtn The "add filter" button from the column header. Needed so we can hide
|
|
737
|
+
* it, if we've reached the maximum number of filters allowed on the column.
|
|
738
|
+
*/
|
|
739
|
+
|
|
740
|
+
GridFilterSet.prototype.add = function (field, target, opts) {
|
|
741
|
+
var self = this
|
|
742
|
+
, filter;
|
|
743
|
+
|
|
744
|
+
opts = opts || {};
|
|
745
|
+
filter = self.build(field, target, opts);
|
|
746
|
+
|
|
747
|
+
if (filter == null) {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Make sure that requisite data structures are there.
|
|
752
|
+
|
|
753
|
+
if (self.filters.byCol[field] === undefined) {
|
|
754
|
+
self.filters.byCol[field] = [];
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Add the filter to all of our data structures.
|
|
758
|
+
|
|
759
|
+
self.filters.all.push(filter);
|
|
760
|
+
self.filters.byCol[field].push(filter);
|
|
761
|
+
self.filters.byId[filter.getId()] = filter;
|
|
762
|
+
|
|
763
|
+
// Add the filter to the user interface.
|
|
764
|
+
|
|
765
|
+
target.append(filter.div);
|
|
766
|
+
|
|
767
|
+
if (typeof filter.afterAdd === 'function') {
|
|
768
|
+
filter.afterAdd(target);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
filter.adjustInputWidth();
|
|
772
|
+
|
|
773
|
+
// Hide the "add filter" button if we've reached the limit of the number of filters we're allowed
|
|
774
|
+
// to have for this column.
|
|
775
|
+
|
|
776
|
+
if (opts.filterBtn && self.filters.byCol[field].length === filter.limit) {
|
|
777
|
+
opts.filterBtn.hide();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
self.fire(GridFilterSet.events.filterAdded);
|
|
781
|
+
|
|
782
|
+
// Check to see if this filter should take effect as soon as it is created.
|
|
783
|
+
|
|
784
|
+
if (filter.applyImmediately) {
|
|
785
|
+
self.update();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return filter;
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// #build {{{2
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Create a new GridFilter instance.
|
|
795
|
+
*
|
|
796
|
+
* @param {string} field
|
|
797
|
+
* Name of the field to apply the filter to. Passed to the View.
|
|
798
|
+
*
|
|
799
|
+
* @param {string} filterType
|
|
800
|
+
* What type of widget to use for the filter (e.g. dropdown, text box, checkbox).
|
|
801
|
+
*
|
|
802
|
+
* @param {Element} filterBtn
|
|
803
|
+
* Button to add a new filter item. Might be shown/hidden depending on how many items are allowed
|
|
804
|
+
* (e.g. a multi-select dropdown only allows one "item" as that's all you need).
|
|
805
|
+
*
|
|
806
|
+
* @param {Element} target
|
|
807
|
+
* Where the filter should be placed.
|
|
808
|
+
*
|
|
809
|
+
* @param {function} onRemove
|
|
810
|
+
* Function to call when the filter is removed.
|
|
811
|
+
*
|
|
812
|
+
* @param {Element} [sizingElement]
|
|
813
|
+
* If present, the element to use to calculate the width of the filter widget. When absent, the
|
|
814
|
+
* div which is placed within the `target` is used.
|
|
815
|
+
*/
|
|
816
|
+
|
|
817
|
+
GridFilterSet.prototype.build = function (field, target, opts) {//filterType, filterBtn, target, onRemove, sizingElement, noRemoveBtn) {
|
|
818
|
+
var self = this;
|
|
819
|
+
|
|
820
|
+
// We use a data source to get the type information, so if the grid was built without a data
|
|
821
|
+
// source, this isn't going to work.
|
|
822
|
+
//
|
|
823
|
+
// FIXME Don't rely on the cache, do it right.
|
|
824
|
+
|
|
825
|
+
var fti = self.view.typeInfo.get(field);
|
|
826
|
+
|
|
827
|
+
if (fti == null) {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
var colType = fti.type;
|
|
832
|
+
|
|
833
|
+
// Make sure that we are able to get the column type.
|
|
834
|
+
|
|
835
|
+
if (colType == null) {
|
|
836
|
+
self.logError(self.makeLogTag() + ' Unable to determine type of column "' + field + '"');
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Make sure that we know what kinds of filters are allowed for the column type.
|
|
841
|
+
|
|
842
|
+
if (GridFilter.widgets[colType] === undefined) {
|
|
843
|
+
self.logError(self.makeLogTag() + ' Unknown type "' + colType + '" for column "' + field + '"');
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// When the user didn't request a filter type, just use the first one in the allowed list.
|
|
848
|
+
// Otherwise, make sure that the filter type they asked for makes sense for the column type.
|
|
849
|
+
|
|
850
|
+
var filterType = opts.filterType || GridFilter.defaultWidgets[colType];
|
|
851
|
+
var ctor = GridFilter.widgets[colType][filterType];
|
|
852
|
+
|
|
853
|
+
if (ctor === undefined) {
|
|
854
|
+
throw new Error('Invalid filter type "' + filterType + '" for type "' + colType + '" of column "' + field + '"');
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
self.logDebug(self.makeLogTag() + ' Creating new widget: column type = "' + colType + '" ; filter type = "' + filterType + '"');
|
|
858
|
+
|
|
859
|
+
return new ctor(field, self, fti, opts);
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// #remove {{{2
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Remove a filter.
|
|
866
|
+
*
|
|
867
|
+
* @param {string} id
|
|
868
|
+
* The unique ID of the filter to remove.
|
|
869
|
+
*
|
|
870
|
+
* @param {Element} filterBtn
|
|
871
|
+
* Button to add a new filter item. Might be shown/hidden depending on how many items are allowed
|
|
872
|
+
* (e.g. a multi-select dropdown only allows one "item" as that's all you need).
|
|
873
|
+
*/
|
|
874
|
+
|
|
875
|
+
GridFilterSet.prototype.remove = function (id, filterBtn, noEvent) {
|
|
876
|
+
var self = this
|
|
877
|
+
, filter = self.filters.byId[id];
|
|
878
|
+
|
|
879
|
+
// Make sure that a filter with that ID exists.
|
|
880
|
+
|
|
881
|
+
if (filter === undefined) {
|
|
882
|
+
self.logWarning(self.makeLogTag() + ' Attempted to remove filter with ID "' + id + '" from the grid, but it doesn\'t exist');
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
var sameId = function (elt) { return elt.getId() === id; };
|
|
887
|
+
var allIndex = _.findIndex(self.filters.all, sameId);
|
|
888
|
+
var colIndex = _.findIndex(self.filters.byCol[filter.field], sameId);
|
|
889
|
+
|
|
890
|
+
delete self.filters.byId[id];
|
|
891
|
+
self.filters.all.splice(allIndex, 1);
|
|
892
|
+
self.filters.byCol[filter.field].splice(colIndex, 1);
|
|
893
|
+
|
|
894
|
+
filter.remove();
|
|
895
|
+
|
|
896
|
+
// Show the "add filter" button if we're below the limit of the number of filters we're allowed to
|
|
897
|
+
// have for this column.
|
|
898
|
+
|
|
899
|
+
if (filterBtn && self.filters.byCol[filter.field].length < filter.limit) {
|
|
900
|
+
filterBtn.show();
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (!noEvent) {
|
|
904
|
+
self.fire(GridFilterSet.events.filterRemoved);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
// #removeField {{{2
|
|
909
|
+
|
|
910
|
+
GridFilterSet.prototype.removeField = function (fieldName, filterBtn) {
|
|
911
|
+
var self = this;
|
|
912
|
+
|
|
913
|
+
_.each(self.filters.byCol[fieldName], function (filter) {
|
|
914
|
+
self.remove(filter.getId(), filterBtn, true);
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
self.fire(GridFilterSet.events.filterRemoved);
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
// #reset {{{2
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Clear all filters.
|
|
924
|
+
*/
|
|
925
|
+
|
|
926
|
+
GridFilterSet.prototype.reset = function (opts) {
|
|
927
|
+
var self = this;
|
|
928
|
+
|
|
929
|
+
opts = opts || {};
|
|
930
|
+
_.defaults(opts, {
|
|
931
|
+
updateView: true
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
self.delayUpdate = true;
|
|
935
|
+
|
|
936
|
+
// Remove every filter from the user interface.
|
|
937
|
+
|
|
938
|
+
_.each(self.filters.all, function (filter) {
|
|
939
|
+
filter.remove();
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
// Reset our internal data structures.
|
|
943
|
+
|
|
944
|
+
self.filters = {
|
|
945
|
+
all: [],
|
|
946
|
+
byId: {},
|
|
947
|
+
byCol: {}
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
if (opts.updateView) {
|
|
951
|
+
self.view.clearFilter();
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
self.delayUpdate = false;
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
// #update {{{2
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Set the filters on the View based on what the user has entered into the user interface.
|
|
961
|
+
*/
|
|
962
|
+
|
|
963
|
+
GridFilterSet.prototype.update = function () {
|
|
964
|
+
var self = this
|
|
965
|
+
, spec = {};
|
|
966
|
+
|
|
967
|
+
// Check for the "don't actually update" property, set when we're loading prefs to prevent any
|
|
968
|
+
// `applyImmediately` filters from causing unnecessary updates until we're done.
|
|
969
|
+
|
|
970
|
+
if (self.delayUpdate) {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
if (self.filters.all.length === 0) {
|
|
975
|
+
self.view.setFilter(null);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
_.each(self.filters.byCol, function (filterList, field) {
|
|
980
|
+
_.each(filterList, function (filter) {
|
|
981
|
+
var operator = filter.getOperator();
|
|
982
|
+
var value = filter.getValue();
|
|
983
|
+
|
|
984
|
+
if (value === undefined && ['$exists', '$notexists'].indexOf(operator) < 0) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
if (spec[field] === undefined) {
|
|
989
|
+
spec[field] = {};
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (operator === '$exists') {
|
|
993
|
+
spec[field]['$exists'] = true;
|
|
994
|
+
}
|
|
995
|
+
else if (operator === '$notexists') {
|
|
996
|
+
spec[field]['$notexists'] = true;
|
|
997
|
+
}
|
|
998
|
+
else if (operator === '$bet') {
|
|
999
|
+
spec[field]['$gte'] = value[0];
|
|
1000
|
+
spec[field]['$lte'] = value[1];
|
|
1001
|
+
}
|
|
1002
|
+
else if (spec[field][operator] === undefined) {
|
|
1003
|
+
spec[field][operator] = value;
|
|
1004
|
+
}
|
|
1005
|
+
else if (_.isArray(spec[field][operator])) {
|
|
1006
|
+
spec[field][operator].push(value);
|
|
1007
|
+
}
|
|
1008
|
+
else if (['$eq', '$ne', '$contains'].indexOf(operator) >= 0) {
|
|
1009
|
+
spec[field][operator] = [spec[field][operator], value];
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
spec[field][operator] = value;
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
self.logDebug(self.makeLogTag() + ' Updating with ' + self.filters.all.length + ' filters: ', spec);
|
|
1018
|
+
|
|
1019
|
+
self.view.setFilter(spec, self.progress, self.opts);
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
// #set {{{2
|
|
1023
|
+
|
|
1024
|
+
GridFilterSet.prototype.set = function (field, fieldSpec, opts) {
|
|
1025
|
+
var self = this;
|
|
1026
|
+
opts = deepDefaults(opts, {
|
|
1027
|
+
updateView: true
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
if (typeof fieldSpec !== 'object') {
|
|
1031
|
+
fieldSpec = { '$eq': fieldSpec };
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
var filters = self.filters.byCol[field];
|
|
1035
|
+
|
|
1036
|
+
if (filters == null || filters.length == null || filters.length === 0) {
|
|
1037
|
+
// ERROR: No filter exists for that field.
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
var widget = filters[0];
|
|
1042
|
+
|
|
1043
|
+
if (!opts.updateView) {
|
|
1044
|
+
self.delayUpdate = true;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (widget instanceof DateRangeGridFilter && '$lte' in fieldSpec && '$gte' in fieldSpec) {
|
|
1048
|
+
widget.setValue([fieldSpec['$gte'], fieldSpec['$lte']]);
|
|
1049
|
+
}
|
|
1050
|
+
else if (widget instanceof DateFilter && '$lte' in fieldSpec && '$gte' in fieldSpec) {
|
|
1051
|
+
widget.setOperator('$bet');
|
|
1052
|
+
widget.setValue(fieldSpec['$gte'], fieldSpec['$lte']);
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
_.each(fieldSpec, function (val, op) {
|
|
1056
|
+
self.logDebug(self.makeLogTag() + ' Setting filter: { field = %s ; operator = %s ; value = %s }',
|
|
1057
|
+
field, op, typeof val === 'object' ? JSON.stringify(val) : val);
|
|
1058
|
+
|
|
1059
|
+
widget.setOperator(op);
|
|
1060
|
+
widget.setValue(val);
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (!opts.updateView) {
|
|
1065
|
+
self.delayUpdate = false;
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
// Exports {{{1
|
|
1070
|
+
|
|
1071
|
+
export {
|
|
1072
|
+
GridFilterSet,
|
|
1073
|
+
};
|