blob: 3c2dc9ca8de557528f634cb70f4f97cb15ce3553 [file] [log] [blame]
Radek Krejci6e772b22018-01-25 13:28:57 +01001import {Component, Directive, ElementRef, EventEmitter, Input, Output, OnInit, ChangeDetectorRef} from '@angular/core';
2import {Router} from '@angular/router';
Radek Krejcia1339602017-11-02 13:52:38 +01003
4import {Session} from './session';
5import {SessionsService} from './sessions.service';
6
Radek Krejci6e772b22018-01-25 13:28:57 +01007@Directive({
8 selector: '[treeScrollTo]'
9})
10export class TreeScrollTo {
11 @Input() node;
12
13 constructor(private elRef:ElementRef) {}
14 ngAfterContentInit() {
15 if (!('new' in this.node)) {
Radek Krejci77088442018-01-26 11:32:12 +010016 let middle = this.elRef.nativeElement.getBoundingClientRect().top + window.pageYOffset - (window.innerHeight / 2);
17 window.scrollTo(0, middle);
Radek Krejci6e772b22018-01-25 13:28:57 +010018 }
19 }
20}
21
22@Directive({
23 selector: '[checkLeafValue]'
24})
25export class CheckLeafValue {
26 @Input() node;
27 @Output() onCheckValue = new EventEmitter();
28
29 constructor(private elRef:ElementRef) {}
30 ngAfterContentInit() {
31 console.log(this.node)
32 let node = this.node;
33 let element = this.elRef.nativeElement;
34 element.value = node['value'];
35 this.onCheckValue.emit({node, element});
36 }
37}
38
39@Component({
40 selector: 'tree-indent',
41 templateUrl: 'tree-indent.html',
42 styleUrls: ['./tree.component.scss']
43})
44export class TreeIndent implements OnInit {
45 @Input() node;
46 @Input() indentation;
47 @Input() type = "current";
48 @Output() onShowEditMenu = new EventEmitter();
Radek Krejci6e772b22018-01-25 13:28:57 +010049 @Output() onDeleteSubtree = new EventEmitter();
50 @Output() onOpenCreatingDialog = new EventEmitter();
51 @Output() onCloseCreatingDialog = new EventEmitter();
52 activeSession: Session;
Radek Krejcicb890972018-01-26 10:12:45 +010053 timeout;
Radek Krejci6e772b22018-01-25 13:28:57 +010054
55 constructor(private sessionsService: SessionsService, private router: Router) {}
56
57 ngOnInit(): void {
58 this.activeSession = this.sessionsService.getActiveSession();
59 }
60
61 showEditMenu(event) {
Radek Krejcicb890972018-01-26 10:12:45 +010062 this.timeout = setTimeout(() => {
63 let menu = event.target.lastElementChild;
64 menu.style.visibility = "visible";
65 menu.style.top = event.clientY + 'px';
66 menu.style.left = event.clientX + 'px';
67 }, 300);
Radek Krejci6e772b22018-01-25 13:28:57 +010068 }
Radek Krejcicb890972018-01-26 10:12:45 +010069
Radek Krejci08ad0a82018-01-26 11:25:17 +010070 hideEditMenu(menu) {
Radek Krejcicb890972018-01-26 10:12:45 +010071 clearTimeout(this.timeout);
Radek Krejci08ad0a82018-01-26 11:25:17 +010072 menu.style.visibility = "hidden";
Radek Krejci6e772b22018-01-25 13:28:57 +010073 }
Radek Krejcicb890972018-01-26 10:12:45 +010074
Radek Krejci6e772b22018-01-25 13:28:57 +010075 deleteSubtree(node) {
76 this.onDeleteSubtree.emit(node);
77 }
78 openCreatingDialog(element, node, parent) {
79 this.onOpenCreatingDialog.emit({element, node, parent});
80 }
81 closeCreatingDialog(node) {
82 this.onCloseCreatingDialog.emit(node);
83 }
84}
85
Radek Krejcia1339602017-11-02 13:52:38 +010086@Component({
87 selector: 'tree-view',
88 templateUrl: './tree.component.html',
Radek Krejcicd1ebe12018-01-11 11:34:17 +010089 styleUrls: ['./tree.component.scss']
Radek Krejcia1339602017-11-02 13:52:38 +010090})
91
92export class TreeView implements OnInit {
Radek Krejci6e772b22018-01-25 13:28:57 +010093 @Input() node;
Radek Krejcia1339602017-11-02 13:52:38 +010094 @Input() indentation;
Radek Krejcia1339602017-11-02 13:52:38 +010095 activeSession: Session;
Radek Krejci6e772b22018-01-25 13:28:57 +010096 root: {};
Radek Krejci26bf2bc2018-01-09 15:00:54 +010097 constructor(private sessionsService: SessionsService, private changeDetector: ChangeDetectorRef) {}
Radek Krejcia1339602017-11-02 13:52:38 +010098
99 ngOnInit(): void {
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100100 this.activeSession = this.sessionsService.getActiveSession();
Radek Krejci6e772b22018-01-25 13:28:57 +0100101 this.root = {};
102 this.root['path'] = '/';
103 this.root['children'] = this.activeSession.data;
Radek Krejcia1339602017-11-02 13:52:38 +0100104 }
105
Radek Krejci77f77202017-11-03 15:33:50 +0100106 inheritIndentation(node) {
107 let newIndent;
108 if (node['last']) {
109 newIndent = [true];
110 } else {
111 newIndent = [false];
Radek Krejcia1339602017-11-02 13:52:38 +0100112 }
Radek Krejci77f77202017-11-03 15:33:50 +0100113
114 if (!this.indentation) {
115 return newIndent;
116 } else {
117 return this.indentation.concat(newIndent);
118 }
Radek Krejcia1339602017-11-02 13:52:38 +0100119 }
120
Radek Krejci6e772b22018-01-25 13:28:57 +0100121 startEditing(node, target) {
122 if (node['info']['key'] && !node['new']) {
123 return;
124 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100125 let parent = target.parentElement;
126
Radek Krejci6e772b22018-01-25 13:28:57 +0100127 this.setNodeEdit(node, true)
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100128 this.changeDetector.detectChanges();
129
Radek Krejci6e772b22018-01-25 13:28:57 +0100130 parent.nextElementSibling.lastElementChild.focus();
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100131 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100132
Radek Krejci4d3896c2018-01-08 17:10:43 +0100133 checkValue(node, target) {
134 let confirm = target.previousElementSibling;
Radek Krejci6e772b22018-01-25 13:28:57 +0100135 let cancel = confirm.previousElementSibling;
136 let path: string;
137 if ('creatingChild' in node) {
138 path = node['creatingChild']['path'];
139 } else {
140 path = node['info']['path'];
141 }
142 this.sessionsService.checkValue(this.activeSession.key, path, target.value).subscribe(result => {
Radek Krejci4d3896c2018-01-08 17:10:43 +0100143 if (result['success']) {
144 target.classList.remove("invalid");
145 confirm.style.visibility = "visible";
Radek Krejci6e772b22018-01-25 13:28:57 +0100146 if ('value' in node) {
147 cancel.style.visibility = "visible";
148 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100149 } else {
150 target.classList.add("invalid");
151 confirm.style.visibility = "hidden";
Radek Krejci6e772b22018-01-25 13:28:57 +0100152 if (!('value' in node)) {
153 cancel.style.visibility = "hidden";
154 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100155 }
156 });
157 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100158
159 changeValueCancel(node) {
160 if ('value' in node) {
161 node['edit'] = false;
162 }
163 }
164
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100165 changeValue(node, target) {
166 let input;
167 if (target.classList.contains('value')) {
168 if (target.classList.contains('invalid')) {
169 return;
170 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100171 input = target;
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100172 } else {
173 input = target.nextElementSibling;
174 }
175
Radek Krejci6e772b22018-01-25 13:28:57 +0100176 if (!('new' in node)) {
177 let record = this.sessionsService.createModificationsRecord(node['path']);
178 if (!('type' in record)) {
179 console.log(record);
180 /* new record */
181 if (node['value'] == input['value']) {
182 /* no change to the original value */
183 this.setNodeEdit(node, false);
184 this.sessionsService.removeModificationsRecord();
185 return;
186 }
187 record['type'] = 'change';
188 record['original'] = node['value'];
189 record['value'] = input.value;
190 node['dirty'] = true;
191 } else if (record['type'] == 'change' && record['original'] == input['value']) {
192 console.log(record);
193 /* change to the original value, remove the change record */
194 this.sessionsService.removeModificationsRecord(node['path']);
195 node['dirty'] = false;
196 } else {
197 console.log(record);
198 /* another change of existing change record */
199 record['value'] = input.value;
200 node['dirty'] = true;
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100201 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100202 console.log(this.activeSession.modifications);
203 }
204
205 node['value'] = input.value;
206 this.setNodeEdit(node, false);
207 console.log(this.activeSession.data);
208 this.sessionsService.storeData();
209 }
210
211 deleteSubtree(node) {
212 if ('new' in node) {
213 /* removing newly created subtree */
214 let parent = this.nodeParent(node);
215 if ('new' in parent) {
216 /* removing just a subtree of the created tree */
217 for (let i in parent['children']) {
218 if (parent['children'][i] == node) {
219 parent['children'].splice(i, 1);
220 break;
221 }
222 }
223 } else {
224 this.sessionsService.removeModificationsRecord(node['path']);
225 for (let i in parent['newChildren']) {
226 if (parent['newChildren'][i]['path'] == node['path']) {
227 parent['newChildren'].splice(i, 1);
228 break;
229 }
230 }
231 if (!parent['newChildren'].length) {
232 delete parent['newChildren'];
233 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100234 }
235 } else {
Radek Krejci6e772b22018-01-25 13:28:57 +0100236 let record = this.sessionsService.createModificationsRecord(node['path']);
237
238 if (!('type' in record)) {
239 /* new record */
240 record['type'] = 'delete';
241 node['deleted'] = true;
242 node['dirty'] = true;
243 } else if (record['type'] == 'change') {
244 record['type'] = 'delete';
245 node['value'] = record['original'];
246 delete record['original'];
247 delete record['value'];
248 node['deleted'] = true;
249 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100250 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100251 console.log(this.activeSession.modifications);
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100252 this.sessionsService.storeData();
253 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100254
Radek Krejci6e772b22018-01-25 13:28:57 +0100255 nodeParent(node) {
256 let match = false;
257 let parent = null;
258
259 let children = this.activeSession.data;
260 let newChildren;
261 while (children || newChildren) {
262 match = false;
263
264 if (children) {
265 for (let iter of children) {
266 if (node['path'] == iter['path']) {
267 match = true;
268 children = null;
269 newChildren = null;
270 break;
271 } else if (node['path'].startsWith(iter['path'] + '/')) {
272 match = true;
273 parent = iter;
274 children = iter['children'];
275 if (('new' in node) && ('newChildren' in iter)) {
276 newChildren = iter['newChildren'];
277 } else {
278 newChildren = null;
279 }
280 break;
281 }
282 }
283 }
284 if (match) {
285 continue;
286 }
287 if (newChildren) {
288 for (let iter of newChildren) {
289 if (node['path'] == iter['path']) {
290 children = null;
291 newChildren = null;
292 break;
293 } else if (node['path'].startsWith(iter['path'] + '/')) {
294 parent = iter;
295 children = iter['children'];
296 if (('new' in node) && ('newChildren' in iter)) {
297 newChildren = iter['newChildren'];
298 } else {
299 newChildren = null;
300 }
301 break;
302 }
303 }
304 }
305 }
306 if (!parent) {
307 parent = this.root;
308 parent['children'] = this.activeSession.data;
309 }
310
311 return parent;
312 }
313
314 schemaInfoName(parentNode, childSchema):string {
315 if (parentNode['info']['module'] != childSchema['module']) {
316 return childSchema['module'] + ':' + childSchema['name'];
317 } else {
318 return childSchema['name'];
319 }
320 }
321
322 setNodeEdit(node, value) {
323 if (value && node['info']['datatypebase'] == 'empty') {
324 node['value'] = '';
325 return;
326 }
327 node['edit'] = value;
328 }
329
330 creatingDialogSelect(node, index, source) {
331 let newNode = {};
332 newNode['new'] = true;
333 newNode['info'] = node['schemaChildren'][index];
334 newNode['path'] = node['path'] + '/' + this.schemaInfoName(node, newNode['info']);
335 newNode['dirty'] = true;
336
337 if ('new' in node) {
338 if (!('children' in node)) {
339 node['children'] = []
340 }
341 node['children'].push(newNode)
342 } else {
343 if (!('newChildren' in node)) {
344 node['newChildren'] = [];
345 }
346 node['newChildren'].push(newNode);
347 }
348
349 switch(newNode['info']['type']) {
350 case 1: { /* container */
351 newNode['children'] = [];
352 node['schemaChildren'].splice(index, 1);
353 this.openCreatingDialog(null, newNode);
354 break;
355 }
356 case 4: { /* leaf */
357 newNode['value'] = newNode['info']['default'];
358 this.setNodeEdit(newNode, true)
359 node['schemaChildren'].splice(index, 1);
360 break;
361 }
362 case 16: { /* list */
363 let search;
364 if ('new' in node) {
365 search = node['children'];
366 } else {
367 search = node['newChildren'];
368 }
369 let pos = 1;
370 if (search.length) {
371 for (let sibling of search) {
372 console.log(sibling);
373 console.log('testing ' + sibling['path'].substr(0, newNode['path'].length + 1));
374 if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
375 console.log(sibling['path'].substring(newNode['path'].length + 1))
376 let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
377 console.log('found ' + n + ' in ' + sibling['path'])
378 if (n >= pos) {
379 pos = n + 1;
380 }
381 }
382 }
383 }
384 newNode['path'] = newNode['path'] + '[' + pos + ']';
385 console.log(newNode['path'])
386
387 newNode['children'] = [];
388 this.openCreatingDialog(null, newNode);
389
390 /* wait to receive schemaChildren of the list */
391 (function wait(context, flag: boolean) {
392 console.log(context)
393 setTimeout(() => {
394 if ('schemaChildren' in newNode) {
395 if (newNode['schemaChildren'].length) {
396 console.log(newNode);
397 for (let i in newNode['schemaChildren']) {
398 if (!newNode['schemaChildren'][i]['key']) {
399 continue;
400 }
401 let newKey = {};
402 newKey['new'] = true;
403 newKey['key'] = true;
404 newKey['info'] = newNode['schemaChildren'][i];
405 newKey['path'] = newNode['path'] + '/' + context.schemaInfoName(newNode, newKey['info']);
406 newKey['dirty'] = true;
407 context.setNodeEdit(newKey, true)
408 newNode['children'].push(newKey)
409 newNode['schemaChildren'].splice(i, 1);
410 }
411 }
412 } else {
413 this.wait(context, true);
414 }
415 }, 10);
416 })(this, true);
417 break;
418 }
419 }
420 if (!node['schemaChildren'].length) {
421 newNode['last'] = true;
422 this.closeCreatingDialog(node, 'success');
423 } else {
424 source.selectedIndex = 0;
425 }
426
427 if (!('new' in node)) {
428 let record = this.sessionsService.createModificationsRecord(newNode['path']);
429 record['type'] = 'create';
430 record['data'] = newNode;
431 }
432 console.log(newNode)
433 }
434
435 openCreatingDialog(element, node, parent = false) {
436 if (parent) {
437 node = this.nodeParent(node);
438 }
439 if (!('creatingChild' in node)) {
440 this.sessionsService.childrenSchemas(this.activeSession.key, node['info']['path'], node).then(result => {
441 console.log(result)
442 node['schemaChildren'] = result;
443 node['creatingChild'] = {};
444 });
445 } else if (element){
446 /* scroll to the existing element */
447 element.ownerDocument.getElementById(node['path'] + '_createChildDialog').scrollIntoView(false);
448 }
449 if (('children' in node) && node['children'].length) {
450 node['children'][node['children'].length - 1]['last'] = false;
451 }
452 console.log(node);
453 }
454
455 closeCreatingDialog(node, reason='abort') {
456 console.log(node)
457 if (reason == 'abort' && ('children' in node) && node['children'].length) {
458 node['children'][node['children'].length - 1]['last'] = true;
459 }
460 delete node['creatingChild'];
461 delete node['schemaChildren'];
462 if ('new' in node && !node['children'].length) {
463 let parent = this.nodeParent(node);
464 for (let i in parent['children']) {
465 if (parent['children'][i] == node) {
466 if (!('schemaChildren' in parent)) {
467 parent['schemaChildren'] = [];
468 parent['creatingChild'] = {};
469 }
470 parent['schemaChildren'].push(node['info']);
471 parent['children'].splice(i, 1);
472 break;
473 }
474 }
475 }
476 }
477
Radek Krejcia1339602017-11-02 13:52:38 +0100478 expandable(node): boolean {
479 if (node['info']['type'] == 1 || /* container */
480 node['info']['type'] == 16) { /* list */
481 return true;
482 }
483 return false;
484 }
485
486 hasHiddenChild(node, clean=false): boolean {
487 if (!clean && 'hasHiddenChild' in node) {
488 return node['hasHiddenChild'];
489 }
490 node['hasHiddenChild'] = false;
Radek Krejci0d4c8632017-11-03 13:54:21 +0100491 if (!this.expandable(node)) {
492 /* terminal node (leaf or leaf-list) */
493 return node['hasHiddenChild'];
494 } else if (!('children' in node)) {
495 /* internal node without children */
Radek Krejcia1339602017-11-02 13:52:38 +0100496 node['hasHiddenChild'] = true;
497 } else {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100498 /* go recursively */
Radek Krejcia1339602017-11-02 13:52:38 +0100499 for (let child of node['children']) {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100500 if (this.hasHiddenChild(child, clean)) {
Radek Krejcia1339602017-11-02 13:52:38 +0100501 node['hasHiddenChild'] = true;
502 break;
503 }
504 }
505 }
506 return node['hasHiddenChild'];
507 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100508
Radek Krejcia1339602017-11-02 13:52:38 +0100509 collapse(node) {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100510 delete node['children'];
Radek Krejcia1339602017-11-02 13:52:38 +0100511 this.activeSession.dataVisibility = 'mixed';
512 for (let iter of this.activeSession.data) {
513 this.hasHiddenChild(iter, true);
514 }
Radek Krejci77f77202017-11-03 15:33:50 +0100515 this.sessionsService.storeData();
516 }
517
518 expand(node, all: boolean) {
519 this.sessionsService.rpcGetSubtree(this.activeSession.key, all, node['path']).subscribe(result => {
520 if (result['success']) {
521 node['children'] = result['data']['children'];
522 for (let iter of this.activeSession.data) {
523 this.hasHiddenChild(iter, true);
524 }
525 this.sessionsService.storeData();
526 }
527 });
Radek Krejcia1339602017-11-02 13:52:38 +0100528 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100529
530 newChildrenToShow(node) {
531 if ('newChildren' in node) {
532 return node['newChildren'];
533 } else {
534 return [];
535 }
536 }
Radek Krejcia1339602017-11-02 13:52:38 +0100537}