blob: 6bf7b29fe8024b6f23250d19bd1fd1a744d3851f [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)) {
16 this.elRef.nativeElement.scrollIntoView(false);
17 }
18 }
19}
20
21@Directive({
22 selector: '[checkLeafValue]'
23})
24export class CheckLeafValue {
25 @Input() node;
26 @Output() onCheckValue = new EventEmitter();
27
28 constructor(private elRef:ElementRef) {}
29 ngAfterContentInit() {
30 console.log(this.node)
31 let node = this.node;
32 let element = this.elRef.nativeElement;
33 element.value = node['value'];
34 this.onCheckValue.emit({node, element});
35 }
36}
37
38@Component({
39 selector: 'tree-indent',
40 templateUrl: 'tree-indent.html',
41 styleUrls: ['./tree.component.scss']
42})
43export class TreeIndent implements OnInit {
44 @Input() node;
45 @Input() indentation;
46 @Input() type = "current";
47 @Output() onShowEditMenu = new EventEmitter();
Radek Krejci6e772b22018-01-25 13:28:57 +010048 @Output() onDeleteSubtree = new EventEmitter();
49 @Output() onOpenCreatingDialog = new EventEmitter();
50 @Output() onCloseCreatingDialog = new EventEmitter();
51 activeSession: Session;
Radek Krejcicb890972018-01-26 10:12:45 +010052 timeout;
Radek Krejci6e772b22018-01-25 13:28:57 +010053
54 constructor(private sessionsService: SessionsService, private router: Router) {}
55
56 ngOnInit(): void {
57 this.activeSession = this.sessionsService.getActiveSession();
58 }
59
60 showEditMenu(event) {
Radek Krejcicb890972018-01-26 10:12:45 +010061 this.timeout = setTimeout(() => {
62 let menu = event.target.lastElementChild;
63 menu.style.visibility = "visible";
64 menu.style.top = event.clientY + 'px';
65 menu.style.left = event.clientX + 'px';
66 }, 300);
Radek Krejci6e772b22018-01-25 13:28:57 +010067 }
Radek Krejcicb890972018-01-26 10:12:45 +010068
Radek Krejci08ad0a82018-01-26 11:25:17 +010069 hideEditMenu(menu) {
Radek Krejcicb890972018-01-26 10:12:45 +010070 clearTimeout(this.timeout);
Radek Krejci08ad0a82018-01-26 11:25:17 +010071 menu.style.visibility = "hidden";
Radek Krejci6e772b22018-01-25 13:28:57 +010072 }
Radek Krejcicb890972018-01-26 10:12:45 +010073
Radek Krejci6e772b22018-01-25 13:28:57 +010074 deleteSubtree(node) {
75 this.onDeleteSubtree.emit(node);
76 }
77 openCreatingDialog(element, node, parent) {
78 this.onOpenCreatingDialog.emit({element, node, parent});
79 }
80 closeCreatingDialog(node) {
81 this.onCloseCreatingDialog.emit(node);
82 }
83}
84
Radek Krejcia1339602017-11-02 13:52:38 +010085@Component({
86 selector: 'tree-view',
87 templateUrl: './tree.component.html',
Radek Krejcicd1ebe12018-01-11 11:34:17 +010088 styleUrls: ['./tree.component.scss']
Radek Krejcia1339602017-11-02 13:52:38 +010089})
90
91export class TreeView implements OnInit {
Radek Krejci6e772b22018-01-25 13:28:57 +010092 @Input() node;
Radek Krejcia1339602017-11-02 13:52:38 +010093 @Input() indentation;
Radek Krejcia1339602017-11-02 13:52:38 +010094 activeSession: Session;
Radek Krejci6e772b22018-01-25 13:28:57 +010095 root: {};
Radek Krejci26bf2bc2018-01-09 15:00:54 +010096 constructor(private sessionsService: SessionsService, private changeDetector: ChangeDetectorRef) {}
Radek Krejcia1339602017-11-02 13:52:38 +010097
98 ngOnInit(): void {
Radek Krejci26bf2bc2018-01-09 15:00:54 +010099 this.activeSession = this.sessionsService.getActiveSession();
Radek Krejci6e772b22018-01-25 13:28:57 +0100100 this.root = {};
101 this.root['path'] = '/';
102 this.root['children'] = this.activeSession.data;
Radek Krejcia1339602017-11-02 13:52:38 +0100103 }
104
Radek Krejci77f77202017-11-03 15:33:50 +0100105 inheritIndentation(node) {
106 let newIndent;
107 if (node['last']) {
108 newIndent = [true];
109 } else {
110 newIndent = [false];
Radek Krejcia1339602017-11-02 13:52:38 +0100111 }
Radek Krejci77f77202017-11-03 15:33:50 +0100112
113 if (!this.indentation) {
114 return newIndent;
115 } else {
116 return this.indentation.concat(newIndent);
117 }
Radek Krejcia1339602017-11-02 13:52:38 +0100118 }
119
Radek Krejci6e772b22018-01-25 13:28:57 +0100120 startEditing(node, target) {
121 if (node['info']['key'] && !node['new']) {
122 return;
123 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100124 let parent = target.parentElement;
125
Radek Krejci6e772b22018-01-25 13:28:57 +0100126 this.setNodeEdit(node, true)
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100127 this.changeDetector.detectChanges();
128
Radek Krejci6e772b22018-01-25 13:28:57 +0100129 parent.nextElementSibling.lastElementChild.focus();
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100130 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100131
Radek Krejci4d3896c2018-01-08 17:10:43 +0100132 checkValue(node, target) {
133 let confirm = target.previousElementSibling;
Radek Krejci6e772b22018-01-25 13:28:57 +0100134 let cancel = confirm.previousElementSibling;
135 let path: string;
136 if ('creatingChild' in node) {
137 path = node['creatingChild']['path'];
138 } else {
139 path = node['info']['path'];
140 }
141 this.sessionsService.checkValue(this.activeSession.key, path, target.value).subscribe(result => {
Radek Krejci4d3896c2018-01-08 17:10:43 +0100142 if (result['success']) {
143 target.classList.remove("invalid");
144 confirm.style.visibility = "visible";
Radek Krejci6e772b22018-01-25 13:28:57 +0100145 if ('value' in node) {
146 cancel.style.visibility = "visible";
147 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100148 } else {
149 target.classList.add("invalid");
150 confirm.style.visibility = "hidden";
Radek Krejci6e772b22018-01-25 13:28:57 +0100151 if (!('value' in node)) {
152 cancel.style.visibility = "hidden";
153 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100154 }
155 });
156 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100157
158 changeValueCancel(node) {
159 if ('value' in node) {
160 node['edit'] = false;
161 }
162 }
163
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100164 changeValue(node, target) {
165 let input;
166 if (target.classList.contains('value')) {
167 if (target.classList.contains('invalid')) {
168 return;
169 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100170 input = target;
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100171 } else {
172 input = target.nextElementSibling;
173 }
174
Radek Krejci6e772b22018-01-25 13:28:57 +0100175 if (!('new' in node)) {
176 let record = this.sessionsService.createModificationsRecord(node['path']);
177 if (!('type' in record)) {
178 console.log(record);
179 /* new record */
180 if (node['value'] == input['value']) {
181 /* no change to the original value */
182 this.setNodeEdit(node, false);
183 this.sessionsService.removeModificationsRecord();
184 return;
185 }
186 record['type'] = 'change';
187 record['original'] = node['value'];
188 record['value'] = input.value;
189 node['dirty'] = true;
190 } else if (record['type'] == 'change' && record['original'] == input['value']) {
191 console.log(record);
192 /* change to the original value, remove the change record */
193 this.sessionsService.removeModificationsRecord(node['path']);
194 node['dirty'] = false;
195 } else {
196 console.log(record);
197 /* another change of existing change record */
198 record['value'] = input.value;
199 node['dirty'] = true;
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100200 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100201 console.log(this.activeSession.modifications);
202 }
203
204 node['value'] = input.value;
205 this.setNodeEdit(node, false);
206 console.log(this.activeSession.data);
207 this.sessionsService.storeData();
208 }
209
210 deleteSubtree(node) {
211 if ('new' in node) {
212 /* removing newly created subtree */
213 let parent = this.nodeParent(node);
214 if ('new' in parent) {
215 /* removing just a subtree of the created tree */
216 for (let i in parent['children']) {
217 if (parent['children'][i] == node) {
218 parent['children'].splice(i, 1);
219 break;
220 }
221 }
222 } else {
223 this.sessionsService.removeModificationsRecord(node['path']);
224 for (let i in parent['newChildren']) {
225 if (parent['newChildren'][i]['path'] == node['path']) {
226 parent['newChildren'].splice(i, 1);
227 break;
228 }
229 }
230 if (!parent['newChildren'].length) {
231 delete parent['newChildren'];
232 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100233 }
234 } else {
Radek Krejci6e772b22018-01-25 13:28:57 +0100235 let record = this.sessionsService.createModificationsRecord(node['path']);
236
237 if (!('type' in record)) {
238 /* new record */
239 record['type'] = 'delete';
240 node['deleted'] = true;
241 node['dirty'] = true;
242 } else if (record['type'] == 'change') {
243 record['type'] = 'delete';
244 node['value'] = record['original'];
245 delete record['original'];
246 delete record['value'];
247 node['deleted'] = true;
248 }
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100249 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100250 console.log(this.activeSession.modifications);
Radek Krejci26bf2bc2018-01-09 15:00:54 +0100251 this.sessionsService.storeData();
252 }
Radek Krejci4d3896c2018-01-08 17:10:43 +0100253
Radek Krejci6e772b22018-01-25 13:28:57 +0100254 nodeParent(node) {
255 let match = false;
256 let parent = null;
257
258 let children = this.activeSession.data;
259 let newChildren;
260 while (children || newChildren) {
261 match = false;
262
263 if (children) {
264 for (let iter of children) {
265 if (node['path'] == iter['path']) {
266 match = true;
267 children = null;
268 newChildren = null;
269 break;
270 } else if (node['path'].startsWith(iter['path'] + '/')) {
271 match = true;
272 parent = iter;
273 children = iter['children'];
274 if (('new' in node) && ('newChildren' in iter)) {
275 newChildren = iter['newChildren'];
276 } else {
277 newChildren = null;
278 }
279 break;
280 }
281 }
282 }
283 if (match) {
284 continue;
285 }
286 if (newChildren) {
287 for (let iter of newChildren) {
288 if (node['path'] == iter['path']) {
289 children = null;
290 newChildren = null;
291 break;
292 } else if (node['path'].startsWith(iter['path'] + '/')) {
293 parent = iter;
294 children = iter['children'];
295 if (('new' in node) && ('newChildren' in iter)) {
296 newChildren = iter['newChildren'];
297 } else {
298 newChildren = null;
299 }
300 break;
301 }
302 }
303 }
304 }
305 if (!parent) {
306 parent = this.root;
307 parent['children'] = this.activeSession.data;
308 }
309
310 return parent;
311 }
312
313 schemaInfoName(parentNode, childSchema):string {
314 if (parentNode['info']['module'] != childSchema['module']) {
315 return childSchema['module'] + ':' + childSchema['name'];
316 } else {
317 return childSchema['name'];
318 }
319 }
320
321 setNodeEdit(node, value) {
322 if (value && node['info']['datatypebase'] == 'empty') {
323 node['value'] = '';
324 return;
325 }
326 node['edit'] = value;
327 }
328
329 creatingDialogSelect(node, index, source) {
330 let newNode = {};
331 newNode['new'] = true;
332 newNode['info'] = node['schemaChildren'][index];
333 newNode['path'] = node['path'] + '/' + this.schemaInfoName(node, newNode['info']);
334 newNode['dirty'] = true;
335
336 if ('new' in node) {
337 if (!('children' in node)) {
338 node['children'] = []
339 }
340 node['children'].push(newNode)
341 } else {
342 if (!('newChildren' in node)) {
343 node['newChildren'] = [];
344 }
345 node['newChildren'].push(newNode);
346 }
347
348 switch(newNode['info']['type']) {
349 case 1: { /* container */
350 newNode['children'] = [];
351 node['schemaChildren'].splice(index, 1);
352 this.openCreatingDialog(null, newNode);
353 break;
354 }
355 case 4: { /* leaf */
356 newNode['value'] = newNode['info']['default'];
357 this.setNodeEdit(newNode, true)
358 node['schemaChildren'].splice(index, 1);
359 break;
360 }
361 case 16: { /* list */
362 let search;
363 if ('new' in node) {
364 search = node['children'];
365 } else {
366 search = node['newChildren'];
367 }
368 let pos = 1;
369 if (search.length) {
370 for (let sibling of search) {
371 console.log(sibling);
372 console.log('testing ' + sibling['path'].substr(0, newNode['path'].length + 1));
373 if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
374 console.log(sibling['path'].substring(newNode['path'].length + 1))
375 let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
376 console.log('found ' + n + ' in ' + sibling['path'])
377 if (n >= pos) {
378 pos = n + 1;
379 }
380 }
381 }
382 }
383 newNode['path'] = newNode['path'] + '[' + pos + ']';
384 console.log(newNode['path'])
385
386 newNode['children'] = [];
387 this.openCreatingDialog(null, newNode);
388
389 /* wait to receive schemaChildren of the list */
390 (function wait(context, flag: boolean) {
391 console.log(context)
392 setTimeout(() => {
393 if ('schemaChildren' in newNode) {
394 if (newNode['schemaChildren'].length) {
395 console.log(newNode);
396 for (let i in newNode['schemaChildren']) {
397 if (!newNode['schemaChildren'][i]['key']) {
398 continue;
399 }
400 let newKey = {};
401 newKey['new'] = true;
402 newKey['key'] = true;
403 newKey['info'] = newNode['schemaChildren'][i];
404 newKey['path'] = newNode['path'] + '/' + context.schemaInfoName(newNode, newKey['info']);
405 newKey['dirty'] = true;
406 context.setNodeEdit(newKey, true)
407 newNode['children'].push(newKey)
408 newNode['schemaChildren'].splice(i, 1);
409 }
410 }
411 } else {
412 this.wait(context, true);
413 }
414 }, 10);
415 })(this, true);
416 break;
417 }
418 }
419 if (!node['schemaChildren'].length) {
420 newNode['last'] = true;
421 this.closeCreatingDialog(node, 'success');
422 } else {
423 source.selectedIndex = 0;
424 }
425
426 if (!('new' in node)) {
427 let record = this.sessionsService.createModificationsRecord(newNode['path']);
428 record['type'] = 'create';
429 record['data'] = newNode;
430 }
431 console.log(newNode)
432 }
433
434 openCreatingDialog(element, node, parent = false) {
435 if (parent) {
436 node = this.nodeParent(node);
437 }
438 if (!('creatingChild' in node)) {
439 this.sessionsService.childrenSchemas(this.activeSession.key, node['info']['path'], node).then(result => {
440 console.log(result)
441 node['schemaChildren'] = result;
442 node['creatingChild'] = {};
443 });
444 } else if (element){
445 /* scroll to the existing element */
446 element.ownerDocument.getElementById(node['path'] + '_createChildDialog').scrollIntoView(false);
447 }
448 if (('children' in node) && node['children'].length) {
449 node['children'][node['children'].length - 1]['last'] = false;
450 }
451 console.log(node);
452 }
453
454 closeCreatingDialog(node, reason='abort') {
455 console.log(node)
456 if (reason == 'abort' && ('children' in node) && node['children'].length) {
457 node['children'][node['children'].length - 1]['last'] = true;
458 }
459 delete node['creatingChild'];
460 delete node['schemaChildren'];
461 if ('new' in node && !node['children'].length) {
462 let parent = this.nodeParent(node);
463 for (let i in parent['children']) {
464 if (parent['children'][i] == node) {
465 if (!('schemaChildren' in parent)) {
466 parent['schemaChildren'] = [];
467 parent['creatingChild'] = {};
468 }
469 parent['schemaChildren'].push(node['info']);
470 parent['children'].splice(i, 1);
471 break;
472 }
473 }
474 }
475 }
476
Radek Krejcia1339602017-11-02 13:52:38 +0100477 expandable(node): boolean {
478 if (node['info']['type'] == 1 || /* container */
479 node['info']['type'] == 16) { /* list */
480 return true;
481 }
482 return false;
483 }
484
485 hasHiddenChild(node, clean=false): boolean {
486 if (!clean && 'hasHiddenChild' in node) {
487 return node['hasHiddenChild'];
488 }
489 node['hasHiddenChild'] = false;
Radek Krejci0d4c8632017-11-03 13:54:21 +0100490 if (!this.expandable(node)) {
491 /* terminal node (leaf or leaf-list) */
492 return node['hasHiddenChild'];
493 } else if (!('children' in node)) {
494 /* internal node without children */
Radek Krejcia1339602017-11-02 13:52:38 +0100495 node['hasHiddenChild'] = true;
496 } else {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100497 /* go recursively */
Radek Krejcia1339602017-11-02 13:52:38 +0100498 for (let child of node['children']) {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100499 if (this.hasHiddenChild(child, clean)) {
Radek Krejcia1339602017-11-02 13:52:38 +0100500 node['hasHiddenChild'] = true;
501 break;
502 }
503 }
504 }
505 return node['hasHiddenChild'];
506 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100507
Radek Krejcia1339602017-11-02 13:52:38 +0100508 collapse(node) {
Radek Krejci0d4c8632017-11-03 13:54:21 +0100509 delete node['children'];
Radek Krejcia1339602017-11-02 13:52:38 +0100510 this.activeSession.dataVisibility = 'mixed';
511 for (let iter of this.activeSession.data) {
512 this.hasHiddenChild(iter, true);
513 }
Radek Krejci77f77202017-11-03 15:33:50 +0100514 this.sessionsService.storeData();
515 }
516
517 expand(node, all: boolean) {
518 this.sessionsService.rpcGetSubtree(this.activeSession.key, all, node['path']).subscribe(result => {
519 if (result['success']) {
520 node['children'] = result['data']['children'];
521 for (let iter of this.activeSession.data) {
522 this.hasHiddenChild(iter, true);
523 }
524 this.sessionsService.storeData();
525 }
526 });
Radek Krejcia1339602017-11-02 13:52:38 +0100527 }
Radek Krejci6e772b22018-01-25 13:28:57 +0100528
529 newChildrenToShow(node) {
530 if ('newChildren' in node) {
531 return node['newChildren'];
532 } else {
533 return [];
534 }
535 }
Radek Krejcia1339602017-11-02 13:52:38 +0100536}