Projekt

Obecné

Profil

« Předchozí | Další » 

Revize b0838f58

Přidáno uživatelem Pavel Fidranský před více než 6 roky(ů)

Node filters

Zobrazit rozdíly:

sources/src/main/webapp/css/common.css
14 14
td {
15 15
	padding: 0 0.8em;
16 16
	line-height: 35px;
17
	vertical-align: top;
17 18
}
18 19

  
19 20
form input[type="text"],
20 21
form input[type="email"],
21
form input[type="password"] {
22
form input[type="number"],
23
form input[type="password"],
24
form select {
22 25
	width: 150px;
23 26
}
24 27

  
sources/src/main/webapp/js/components/filterModalWindow.js
1
class FilterModalWindow extends ModalWindow {
2
	/**
3
	 * @constructor
4
	 */
5
	constructor() {
6
		super();
7

  
8
		this._filterOptions = {
9
			nodeType: 'Node type',
10
			vertexArchetype: 'Vertex archetype',
11
			vertexAttribute: 'Vertex attribute',
12
		};
13

  
14
		this._vertexAttributeTypeOptions = function() {
15
			return app.attributeTypeList.map(attributeType => `${attributeType.name} (${attributeType.dataType.toLowerCase()})`);
16
		};
17

  
18
		this._operationOptions = {
19
			STRING: {
20
				eq: 'equals',
21
				neq: 'not equals',
22
				contains: 'contains',
23
				startsWith: 'starts with',
24
				endsWith: 'ends with',
25
				regexp: 'regular expression',
26
			},
27
			ENUM: {
28
				eq: 'equals',
29
				neq: 'not equals',
30
			},
31
			DATE: {
32
				eq: 'equals',
33
				neq: 'not equals',
34
				range: 'is in range',
35
			},
36
			NUMBER: {
37
				eq: 'equals',
38
				neq: 'not equals',
39
				lt: 'lower than',
40
				lte: 'lower than or equals',
41
				gt: 'greater than',
42
				gte: 'greater than or equals',
43
				range: 'is in range',
44
			},
45
		};
46

  
47
		this._nodeTypeEnumValues = function() {
48
			return ['Vertex', 'Group'];
49
		};
50
		this._vertexArchetypeEnumValues = function() {
51
			return app.archetype.vertex.map(archetype => archetype.name);
52
		};
53
		this._vertexAttributeEnumValues = function(index) {
54
			const values = app.possibleEnumValues[index];
55
			return Utils.isDefined(values) ? values : [];
56
		};
57
	}
58

  
59
	/**
60
	 * @inheritdoc
61
	 */
62
	render() {
63
		super.render();
64

  
65
		this._initializeFormFields();
66

  
67
		this._rootElement.classList.add('filter-modal');
68

  
69
		// value field(s) cell
70
		this._valueCell = DOM.h('td');
71

  
72
		// form
73
		this._form = DOM.h('form', {
74
			onSubmit: this._onFilterFormSubmit.bind(this),
75
			onReset: this._onFilterFormReset.bind(this),
76
		}, [
77
			DOM.h('input', {
78
				type: 'hidden',
79
				name: 'dataType',
80
			}),
81
			DOM.h('table', {}, [
82
				DOM.h('tr', {}, [
83
					DOM.h('td', {}, [
84
						DOM.h('label', {
85
							for: 'filter',
86
							innerText: 'Filter:',
87
						}),
88
					]),
89
					DOM.h('td', {}, [
90
						DOM.h('select', {
91
							name: 'filter',
92
							id: 'filter',
93
							onChange: e => this._onFilterChange(e.target.value),
94
						}),
95
						DOM.h('select', {
96
							name: 'additionalFilter',
97
							onChange: e => this._onAdditionalFilterChange(e.target.value),
98
						}),
99
					]),
100
				]),
101
				DOM.h('tr', {}, [
102
					DOM.h('td', {}, [
103
						DOM.h('label', {
104
							for: 'operation',
105
							innerText: 'Operation:',
106
						}),
107
					]),
108
					DOM.h('td', {}, [
109
						DOM.h('select', {
110
							name: 'operation',
111
							id: 'operation',
112
							onChange: e => this._onOperationChange(e.target.value),
113
						}),
114
					]),
115
				]),
116
				DOM.h('tr', {}, [
117
					DOM.h('td', {}, [
118
						DOM.h('label', {
119
							innerText: 'Value:',
120
						}),
121
					]),
122
					this._valueCell,
123
				]),
124
				DOM.h('tr', {}, [
125
					DOM.h('td'),
126
					DOM.h('td', {}, [
127
						DOM.h('button', {
128
							type: 'submit',
129
							class: 'button',
130
							innerText: 'Apply filter',
131
						}),
132
						DOM.h('button', {
133
							type: 'reset',
134
							class: 'button',
135
							innerText: 'Reset',
136
						}),
137
					]),
138
				]),
139
			]),
140
		]);
141
		this._bodyElement.appendChild(this._form);
142

  
143
		// set values
144
		for (let key in this._filterOptions) {
145
			let value = this._filterOptions[key];
146

  
147
			this._form.filter.appendChild(DOM.h('option', {
148
				value: key,
149
				innerText: value,
150
			}));
151
		}
152

  
153
		this._onFilterChange('nodeType');
154

  
155
		/*
156
			// filter mode
157
			DOM.h('select', {
158
				name: 'filterMode',
159
			}, [
160
				DOM.h('option', {
161
					value: 'and',
162
					innerText: 'and',
163
				}),
164
				DOM.h('option', {
165
					value: 'or',
166
					innerText: 'or',
167
				}),
168
				DOM.h('option', {
169
					value: 'xor',
170
					innerText: 'xor',
171
				}),
172
			]),
173

  
174
		this._filterList = DOM.h('ul');
175
		this._bodyElement.appendChild(this._filterList);
176
		*/
177

  
178
		return this._rootElement;
179
	}
180

  
181
	_initializeFormFields() {
182
		// string
183
		this._stringField = DOM.h('input', {
184
			type: 'text',
185
			name: 'value',
186
			required: 'required',
187
		});
188

  
189
		// enum
190
		this._enumField = DOM.h('select', {
191
			name: 'value',
192
			multiple: 'multiple',
193
			required: 'required',
194
		});
195

  
196
		// date
197
		this._dateField = DOM.h('input', {
198
			type: 'date',
199
			name: 'value',
200
			required: 'required',
201
		});
202
		
203
		this._dateRangeField = DOM.h('div', {}, [
204
			DOM.h('input', {
205
				type: 'date',
206
				name: 'value-from',
207
			}),
208
			DOM.t(' - '),
209
			DOM.h('input', {
210
				type: 'date',
211
				name: 'value-to',
212
			}),
213
		]);
214

  
215
		// number
216
		this._numberField = DOM.h('input', {
217
			type: 'number',
218
			name: 'value',
219
			required: 'required',
220
		});
221

  
222
		this._numberRangeField = DOM.h('div', {}, [
223
			DOM.h('input', {
224
				type: 'number',
225
				name: 'value-from',
226
			}),
227
			DOM.t(' - '),
228
			DOM.h('input', {
229
				type: 'number',
230
				name: 'value-to',
231
			}),
232
		]);
233
	}
234

  
235
	/**
236
	 * 
237
	 * @param {string} value 
238
	 */
239
	_onFilterChange(value) {
240
		switch (value) {
241
			case 'nodeType':
242
				this._form.additionalFilter.setAttribute('hidden', 'hidden');
243

  
244
				this._form.dataType.value = 'ENUM';
245

  
246
				this._setOperationOptions();
247
				this._onOperationChange('equals');
248

  
249
				this._setEnumOptions(this._nodeTypeEnumValues());
250

  
251
				break;
252

  
253
			case 'vertexArchetype':
254
				this._form.additionalFilter.setAttribute('hidden', 'hidden');
255

  
256
				this._form.dataType.value = 'ENUM';
257

  
258
				this._setOperationOptions();
259
				this._onOperationChange('equals');
260

  
261
				this._setEnumOptions(this._vertexArchetypeEnumValues());
262

  
263
				break;
264

  
265
			case 'vertexAttribute':
266
				this._form.additionalFilter.removeAttribute('hidden');
267

  
268
				this._setAdditionalFilterOptions(this._vertexAttributeTypeOptions());
269
				this._onAdditionalFilterChange(0);
270

  
271
				break;
272
		}
273
	}
274

  
275
	_setAdditionalFilterOptions(options) {
276
		this._form.additionalFilter.innerHTML = '';
277
		options.forEach((value, key) => {
278
			this._form.additionalFilter.appendChild(DOM.h('option', {
279
				value: key,
280
				innerText: value,
281
			}));
282
		});
283
	}
284

  
285
	/**
286
	 * 
287
	 * @param {int} value 
288
	 */
289
	_onAdditionalFilterChange(value) {
290
		let attributeType = app.attributeTypeList[value];
291

  
292
		this._form.dataType.value = attributeType.dataType;
293
		this._setOperationOptions();
294
		this._onOperationChange('equals');
295

  
296
		if (attributeType.dataType === 'ENUM') {
297
			this._setEnumOptions(this._vertexAttributeEnumValues(value));
298
		}
299
	}
300

  
301
	_setOperationOptions() {
302
		let dataType = this._form.dataType.value;
303

  
304
		this._form.operation.innerHTML = '';
305
		for (let key in this._operationOptions[dataType]) {
306
			let value = this._operationOptions[dataType][key];
307

  
308
			this._form.operation.appendChild(DOM.h('option', {
309
				value: key,
310
				innerText: value,
311
			}));
312
		}
313
	}
314

  
315
	/**
316
	 * 
317
	 * @param {string} value 
318
	 */
319
	_onOperationChange(value) {
320
		let dataType = this._form.dataType.value;
321

  
322
		this._valueCell.innerHTML = '';
323
		switch (dataType) {
324
			case 'STRING':
325
				this._valueCell.appendChild(this._stringField);
326
				break;
327
			case 'ENUM':
328
				this._valueCell.appendChild(this._enumField);
329
				break;
330
			case 'DATE':
331
				if (value === 'range') {
332
					this._valueCell.appendChild(this._dateRangeField);
333
				} else {
334
					this._valueCell.appendChild(this._dateField);
335
				}
336
				break;
337
			case 'NUMBER':
338
				if (value === 'range') {
339
					this._valueCell.appendChild(this._numberRangeField);
340
				} else {
341
					this._valueCell.appendChild(this._numberField);
342
				}
343
				break;
344
		}
345
	}
346

  
347
	_setEnumOptions(options) {
348
		this._enumField.innerHTML = '';
349
		options.forEach((value, key) => {
350
			this._enumField.appendChild(DOM.h('option', {
351
				value: key,
352
				innerText: value,
353
			}));
354
		});
355
	}
356

  
357
	/**
358
	 * 
359
	 * @param {*} e 
360
	 */
361
	_onFilterFormSubmit(e) {
362
		e.preventDefault();
363

  
364
		const formData = new FormData(e.target);
365

  
366
		//this._displayFilter(formData);
367
		this._applyFilter(formData);
368
	}
369

  
370
	_onFilterFormReset() {
371
		this._onFilterChange('nodeType');
372

  
373
		app.nodeList.forEach(node => {
374
			node.isFound = false;
375
		});
376
	}
377

  
378
	_displayFilter(formData) {
379
		// filter
380
		console.log(this._filterOptions[formData.get('filter')]);
381

  
382
		// additional filter
383
		switch (formData.get('filter')) {
384
			case 'vertexAttribute':
385
				console.log(app.attributeTypeList[formData.get('additionalFilter')].name);
386
				break;
387
		}
388

  
389
		// operation
390
		console.log(this._operationOptions[formData.get('dataType')][formData.get('operation')]);
391

  
392
		// value(s)
393
		switch (formData.get('dataType')) {
394
			case 'STRING':
395
				console.log(formData.get('value'));
396
				break;
397
			case 'ENUM':
398
				switch (formData.get('filter')) {
399
					case 'nodeType':
400
						console.log(formData.getAll('value'));
401
						break;
402
					case 'vertexArchetype':
403
						console.log(formData.getAll('value'));
404
						break;
405
					case 'vertexAttribute':
406
						console.log(formData.getAll('value').map(value => app.possibleEnumValues[formData.get('additionalFilter')][parseInt(value)]).join(', '));
407
						break;
408
				}
409
				break;
410
			case 'DATE':
411
				if (formData.get('operation') === 'range') {
412
					console.log(formData.get('value-from'), formData.get('value-to'));
413
				} else {
414
					console.log(formData.get('value'));
415
				}
416
				break;
417
			case 'NUMBER':
418
				if (formData.get('operation') === 'range') {
419
					console.log(formData.get('value-from'), formData.get('value-to'));
420
				} else {
421
					console.log(formData.get('value'));
422
				}
423
				break;
424
		}
425
	}
426

  
427
	_applyFilter(formData) {
428
		let nodeListCopy = app.nodeList.slice(0);
429

  
430
		const filter = formData.get('filter');
431
		const additionalFilter = formData.get('additionalFilter');
432
		const dataType = formData.get('dataType');
433
		const operation = formData.get('operation');
434

  
435
		let filterFunction;
436

  
437
		switch (filter) {
438
			case 'nodeType':
439
				var values = formData.getAll('value').map(value => parseInt(value));
440

  
441
				// datatype is always enum
442
				switch (operation) {
443
					case 'eq':
444
						filterFunction = node => {
445
							return (node instanceof Vertex && values.indexOf(0) > -1)
446
								|| (node instanceof Group && values.indexOf(1) > -1);
447
						};
448
						break;
449

  
450
					case 'neq':
451
						filterFunction = node => {
452
							return (node instanceof Vertex && values.indexOf(0) < 0)
453
								|| (node instanceof Group && values.indexOf(1) < 0);
454
						};
455
						break;
456
				}
457
				break;
458

  
459
			case 'vertexArchetype':
460
				// prefilter
461
				nodeListCopy = nodeListCopy.filter(node => {
462
					return node instanceof Vertex;
463
				});
464

  
465
				var values = formData.getAll('value').map(value => parseInt(value));
466

  
467
				// datatype is always enum
468
				switch (operation) {
469
					case 'eq':
470
						filterFunction = vertex => {
471
							return (values.indexOf(vertex.archetype) > -1);
472
						};
473
						break;
474

  
475
					case 'neq':
476
						filterFunction = vertex => {
477
							return (values.indexOf(vertex.archetype) < 0);
478
						};
479
						break;
480
				}
481
				break;
482

  
483
			case 'vertexAttribute':
484
				const filterAttributeName = app.attributeTypeList[additionalFilter].name;
485

  
486
				// prefilter
487
				nodeListCopy = nodeListCopy.filter(node => {
488
					return node instanceof Vertex;
489
				}).filter(vertex => {
490
					return vertex.attributes.some(attribute => attribute[0] === filterAttributeName);
491
				});
492

  
493
				switch (dataType) {
494
					case 'STRING':
495
						var comparatorFn;
496
						switch (operation) {
497
							case 'eq':
498
								comparatorFn = (a, b) => a === b;
499
								break;
500
							case 'neq':
501
								comparatorFn = (a, b) => a !== b;
502
								break;
503
							case 'contains':
504
								comparatorFn = (a, b) => a.includes(b);
505
								break;
506
							case 'startsWith':
507
								comparatorFn = (a, b) => a.startsWith(b);
508
								break;
509
							case 'endsWith':
510
								comparatorFn = (a, b) => a.endsWith(b);
511
								break;
512
							case 'regexp':
513
								comparatorFn = (a, b) => a.match(new RegExp(b, 'i'));
514
								break;
515
						}
516

  
517
						filterFunction = vertex => {
518
							const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
519
							return attribute != null && comparatorFn(attribute[1], formData.get('value'));
520
						};
521
						break;
522

  
523
					case 'ENUM':
524
						var values = formData.getAll('value').map(value => app.possibleEnumValues[additionalFilter][parseInt(value)]);
525

  
526
						switch (operation) {
527
							case 'eq':
528
								filterFunction = vertex => {
529
									const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
530
									const attributeValues = attribute[1].split(', ');
531

  
532
									return attributeValues.some(attributeValue => {
533
										return values.indexOf(attributeValue) > -1;
534
									});
535
								};
536
								break;
537

  
538
							case 'neq':
539
								filterFunction = vertex => {
540
									const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
541
									const attributeValues = attribute[1].split(', ');
542

  
543
									return attributeValues.some(attributeValue => {
544
										return values.indexOf(attributeValue) < 0;
545
									});
546
								};
547
								break;
548
						}
549
						break;
550

  
551
					case 'DATE':
552
						if (operation === 'range') {
553
							filterFunction = vertex => {
554
								const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
555
								const a = parseInt(attribute[1]);
556
								const b = formData.get('value-from') !== '' ? Date.parse(formData.get('value-from')) : Date.now();
557
								const c = formData.get('value-to') !== '' ? Date.parse(formData.get('value-to')) : Date.now();
558
								return (a >= b) && (a <= c);
559
							};
560

  
561
						} else {
562
							var comparatorFn;
563
							switch (operation) {
564
								case 'eq':
565
									comparatorFn = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
566
									break;
567
								case 'neq':
568
									comparatorFn = (a, b) => a.getFullYear() !== b.getFullYear() || a.getMonth() !== b.getMonth() || a.getDate() !== b.getDate();
569
									break;
570
							}
571

  
572
							filterFunction = vertex => {
573
								const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
574
								return comparatorFn(new Date(attribute[1]), new Date(formData.get('value')));
575
							};
576
						}
577
						break;
578

  
579
					case 'NUMBER':
580
						if (operation === 'range') {
581
							filterFunction = vertex => {
582
								const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
583
								const a = parseFloat(attribute[1]);
584
								const b = formData.get('value-from') !== '' ? parseFloat(formData.get('value-from')) : Number.MIN_VALUE;
585
								const c = formData.get('value-to') !== '' ? parseFloat(formData.get('value-to')) : Number.MAX_VALUE;
586
								return (a >= b) && (a <= c);
587
							};
588

  
589
						} else {
590
							var comparatorFn;
591
							switch (operation) {
592
								case 'eq':
593
									comparatorFn = (a, b) => a === b;
594
									break;
595
								case 'neq':
596
									comparatorFn = (a, b) => a !== b;
597
									break;
598
								case 'lt':
599
									comparatorFn = (a, b) => a < b;
600
									break;
601
								case 'lte':
602
									comparatorFn = (a, b) => a <= b;
603
									break;
604
								case 'gt':
605
									comparatorFn = (a, b) => a > b;
606
									break;
607
								case 'gte':
608
									comparatorFn = (a, b) => a >= b;
609
									break;
610
							}
611

  
612
							filterFunction = vertex => {
613
								const attribute = vertex.attributes.find(attribute => attribute[0] === filterAttributeName);
614
								return attribute != null && comparatorFn(parseFloat(attribute[1]), parseFloat(formData.get('value')));
615
							};
616
						}
617
						break;
618
				}
619
				break;
620
		}
621

  
622
		nodeListCopy.filter(filterFunction).forEach(node => {
623
			node.isFound = true;
624
		});
625
	}
626
}
sources/src/main/webapp/js/components/navbar.js
13 13
			this._createSearchListItem(),
14 14
			this._createSeparatorListItem(),
15 15

  
16
			this._createFilterListItem(),
17
			this._createSeparatorListItem(),
18

  
16 19
			this._createSwitchModeListItem(),
17 20
			this._createSeparatorListItem(),
18 21

  
......
165 168
		]);
166 169
	}
167 170

  
171
	_createFilterListItem() {
172
		const filterButton = DOM.h('button', {
173
			class: 'filter',
174
			id: 'filterButton',
175
			innerText: 'Filter nodes',
176
			onClick: () => app.filterModalWindowComponent.open(),
177
		});
178

  
179
		return DOM.h('li', {}, [
180
			filterButton,
181
		]);
182
	}
183

  
168 184
	_createSwitchModeListItem() {
169 185
		return DOM.h('li', {}, [
170 186
			DOM.h('form', {
......
353 369
			DOM.h('button', {
354 370
				class: 'btn save-diagram',
355 371
				title: 'Save diagram',
356
				onClick: () => app.modalWindowComponent.open(),
372
				onClick: () => app.saveDiagramModalWindowComponent.open(),
357 373
			}, [
358 374
				DOM.h('img', {
359 375
					src: 'images/icon_save.png',
sources/src/main/webapp/js/services/filter.js
1
class Filter {
2

  
3
	constructor() {
4
		this._filterList = [];
5
	}
6

  
7
	run() {
8
		console.log(this._filterList);
9
	}
10

  
11
	/**
12
     * Function add values from input list to archetype selector.
13
     * @param vertexArchetypes list of vertex archetypes
14
     */
15
    initializeSelectors(vertexArchetypes) {
16
        let vertexArchetypeSelection = document.getElementById('vertexArchetypeSelection');
17

  
18
        vertexArchetypes.forEach(function(archetype) {
19
            let option = document.createElement("option");
20
            option.text = archetype.name;
21
            option.value = archetype.name;
22
            vertexArchetypeSelection.add(option);
23
        });
24
    }
25

  
26
    /**
27
     * Function remove all options from archetype selector.
28
     */
29
    resetSelectors() {
30
        let vertexArchetypeSelection = document.getElementById('vertexArchetypeSelection');
31

  
32
        while (vertexArchetypeSelection.length > 0){
33
            vertexArchetypeSelection.remove(0);
34
        }
35
    }
36

  
37
}
sources/src/main/webapp/js/showGraphApp.js
16 16
		this.graphExporter = new GraphExporter;
17 17
		this.zoom = new Zoom(0.8);
18 18
		this.markSymbol = new MarkSymbol;
19
		this.filter = new Filter;
19 20

  
20 21
		/** @prop {array<Edge>} edgeList */
21 22
		this.edgeList = [];
......
94 95
		this.navbarComponent = new Navbar;
95 96
		this.viewportComponent = new Viewport;
96 97
		this.sidebarComponent = new Sidebar;
97
		this.modalWindowComponent = new SaveDiagramModalWindow;
98
		this.saveDiagramModalWindowComponent = new SaveDiagramModalWindow;
99
		this.filterModalWindowComponent = new FilterModalWindow;
98 100
		this.spinLoaderComponent = new SpinLoader;
99 101

  
100 102
		const appElement = document.getElementById('app');
......
107 109
			this.viewportComponent.render(),
108 110
			this.sidebarComponent.render(),
109 111
		]));
110
		appElement.appendChild(this.modalWindowComponent.render());
112
		appElement.appendChild(this.saveDiagramModalWindowComponent.render());
113
		appElement.appendChild(this.filterModalWindowComponent.render());
111 114
		appElement.appendChild(this.spinLoaderComponent.render());
112 115

  
113 116
		this.sidebarComponent.minimapComponent.viewportSize = this.viewportComponent.size;
sources/src/main/webapp/showGraph.jsp
44 44
		<script src="js/components/attribute.js"></script>
45 45
		<script src="js/components/edge.js"></script>
46 46
		<script src="js/components/edgePopover.js"></script>
47
		<script src="js/components/filterModalWindow.js"></script>
47 48
		<script src="js/components/group.js"></script>
48 49
		<script src="js/components/groupVertexList.js"></script>
49 50
		<script src="js/components/header.js"></script>
......
74 75
		<script src="js/events/loggedOutEvent.js"></script>
75 76
		<script src="js/events/registeredEvent.js"></script>
76 77

  
78
		<script src="js/services/filter.js"></script>
77 79
		<script src="js/services/forceDirected.js"></script>
78 80
		<script src="js/services/graphLoader.js"></script>
79 81
		<script src="js/services/graphExporter.js"></script>

Také k dispozici: Unified diff