gui CHANGE rewrite list and leaf-list manipulation
To prepare for ordering, group list and leaf-list instances together
when displaying (providing them from backend) and also in manipulating
(creating, deleting in frontend)
diff --git a/frontend/config/config.component.html b/frontend/config/config.component.html
index 1918263..9645b70 100644
--- a/frontend/config/config.component.html
+++ b/frontend/config/config.component.html
@@ -49,7 +49,7 @@
<th class="item_right" >Data</th>
</tr>
</table>
- <div class="loading" *ngIf="treeService.loading">
+ <div class="loading" *ngIf="activeSession.loading">
<span>Retrieving data ...</span>
<div><netopeer-loading spinner="true"></netopeer-loading></div>
</div>
@@ -62,8 +62,8 @@
<tree-view [node]="activeSession.data"></tree-view>
<!-- <pre *ngIf="activeSession.data['schemaChildren']">{{activeSession.data['schemaChildren'] | json}}</pre> -->
</div>
- <div #modificationsStatus *ngIf="activeSession.modifications" class="modifications-status">
- <div *ngIf="activeSession.modifications" class="msg-rounded" [style.width.px]="modificationsStatus.offsetWidth">
+ <div #modificationsStatus *ngIf="activeSession.modifications" class="modifications-status">
+ <div *ngIf="activeSession.modifications" class="msg-rounded" [style.width.px]="modificationsStatus.offsetWidth">
<div>Configuration data were changed. Do you wish to
<button (click)="applyChanges()">apply</button> /
<button (click)="cancelChanges()">cancel</button>
@@ -72,8 +72,8 @@
<ng-container *ngFor="let err of commit_error; let i = index">
<div class="msg-rounded msg-failure"><span class="msg-close" (click)="commit_error.splice(i,1)">x</span>{{err['message']}}</div>
</ng-container>
- </div>
- </div>
+ </div>
+ </div>
</ng-container>
</div>
diff --git a/frontend/config/config.component.ts b/frontend/config/config.component.ts
index c31acfd..f57db9a 100644
--- a/frontend/config/config.component.ts
+++ b/frontend/config/config.component.ts
@@ -1,131 +1,16 @@
-import {Component, Injectable, OnInit} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
+import {TreeService} from './tree.service';
import {ModificationsService} from './modifications.service';
import {SessionsService} from './sessions.service';
import {Session} from './session';
-@Injectable()
-export class TreeService {
- loading = false;
-
- constructor(private sessionsService: SessionsService, private modsService: ModificationsService) {}
-
- rpcGet(activeSession, all: boolean) {
- if (activeSession.data) {
- if ((all && activeSession.dataVisibility == 'all') ||
- (!all && activeSession.dataVisibility == 'root')) {
- return;
- }
- }
- this.loading = true;
- delete activeSession.data;
- this.sessionsService.rpcGetSubtree(activeSession.key, all).subscribe(result => {
- if (result['success']) {
- for (let iter of result['data']) {
- this.modsService.setDirty(activeSession, iter);
- }
- activeSession.data = {};
- activeSession.data['path'] = '/';
- activeSession.data['info'] = {};
- activeSession.data['info']['path'] = '/';
- activeSession.data['children'] = result['data'];
- if (all) {
- activeSession.dataVisibility = 'all';
- } else {
- activeSession.dataVisibility = 'root';
- }
- }
- this.sessionsService.storeData();
- this.loading = false;
- });
- }
-
- expandable(node): boolean {
- if (node['info']['type'] == 1 || /* container */
- node['info']['type'] == 16) { /* list */
- return true;
- }
- return false;
- }
-
- hasHiddenChild(node, clean=false): boolean {
- if (!clean && 'hasHiddenChild' in node) {
- return node['hasHiddenChild'];
- }
- node['hasHiddenChild'] = false;
- if (!this.expandable(node)) {
- /* terminal node (leaf or leaf-list) */
- return node['hasHiddenChild'];
- } else if (!('children' in node)) {
- /* internal node without children */
- node['hasHiddenChild'] = true;
- } else {
- /* go recursively */
- for (let child of node['children']) {
- if (this.hasHiddenChild(child, clean)) {
- node['hasHiddenChild'] = true;
- break;
- }
- }
- }
- return node['hasHiddenChild'];
- }
-
- updateHiddenFlags(activeSession) {
- let mixed = false;
- let rootsonly = true;
- for (let root of activeSession.data['children']) {
- if (this.hasHiddenChild(root, true)) {
- mixed = true;
- } else {
- rootsonly = false;
- }
- }
- if (mixed) {
- if (rootsonly) {
- activeSession.dataVisibility = 'root';
- } else {
- activeSession.dataVisibility = 'mixed';
- }
- }
- }
-
- collapse(activeSession, node = null) {
- if (node) {
- delete node['children'];
- activeSession.dataVisibility = 'mixed';
- } else {
- for (let root of activeSession.data['children']) {
- delete root['children'];
- }
- activeSession.dataVisibility = 'root';
- }
- this.updateHiddenFlags(activeSession);
- this.sessionsService.storeData();
- }
-
- expand(activeSession, node, all: boolean) {
- node['loading'] = true;
- this.sessionsService.rpcGetSubtree(activeSession.key, all, node['path']).subscribe(result => {
- if (result['success']) {
- for (let iter of result['data']['children']) {
- this.modsService.setDirty(activeSession, iter);
- }
- node['children'] = result['data']['children'];
- this.updateHiddenFlags(activeSession);
- delete node['loading'];
- this.sessionsService.storeData();
- }
- });
- }
-}
-
@Component({
selector: 'netopeer-config',
templateUrl: './config.component.html',
styleUrls: ['./config.component.scss'],
- providers: [ModificationsService, TreeService]
+ providers: [ModificationsService]
})
export class ConfigComponent implements OnInit {
@@ -136,7 +21,6 @@
constructor(private sessionsService: SessionsService,
private modsService: ModificationsService,
- private treeService: TreeService,
private router: Router) {}
addSession() {
@@ -146,9 +30,9 @@
reloadData() {
this.activeSession.data = null;
if (this.activeSession.dataVisibility == 'root') {
- this.treeService.rpcGet(this.activeSession, false);
+ this.sessionsService.rpcGet(this.activeSession, false);
} else {
- this.treeService.rpcGet(this.activeSession, true);
+ this.sessionsService.rpcGet(this.activeSession, true);
}
}
@@ -255,6 +139,7 @@
}
applyChanges() {
+ //console.log(JSON.stringify(this.activeSession.modifications))
this.modsService.applyModification(this.activeSession).then(result => {
if (result['success']) {
this.reloadData();
@@ -269,7 +154,7 @@
this.sessionsService.checkSessions();
this.activeSession = this.sessionsService.getActiveSession();
if (this.activeSession && !this.activeSession.data) {
- this.treeService.rpcGet(this.activeSession, false);
+ this.sessionsService.rpcGet(this.activeSession, false);
}
}
diff --git a/frontend/config/modifications.service.ts b/frontend/config/modifications.service.ts
index e553df8..eb95d2b 100644
--- a/frontend/config/modifications.service.ts
+++ b/frontend/config/modifications.service.ts
@@ -2,11 +2,12 @@
import { Session} from './session';
import { SessionsService } from './sessions.service';
+import { TreeService } from './tree.service';
@Injectable()
export class ModificationsService {
- constructor(private sessionsService: SessionsService) {}
+ constructor(private sessionsService: SessionsService, private treeService: TreeService) {}
createModificationsRecord(activeSession, path) {
if (!activeSession.modifications) {
@@ -44,23 +45,11 @@
}
}
- setDirty(activeSession, node) {
- if (!activeSession.modifications) {
- return;
- }
-
- if (node['path'] in activeSession.modifications) {
- node['dirty'] = true;
- if (activeSession.modifications[node['path']]['type'] == 'change') {
- activeSession.modifications[node['path']]['original'] = node['value'];
- }
- node['value'] = activeSession.modifications[node['path']]['value'];
- }
- /* recursion */
- if ('children' in node) {
- for (let child of node['children']) {
- this.setDirty(activeSession, child);
- }
+ renameModificationsRecord(activeSession, oldPath, newPath) {
+ let record = this.getModificationsRecord(activeSession, oldPath);
+ if (record) {
+ activeSession.modifications[newPath] = record;
+ delete activeSession.modifications[oldPath];
}
}
@@ -101,76 +90,6 @@
last['last'] = true;
}
- nodeParent(activeSession, node) {
- if (node['path'] =='/') {
- return null;
- }
-
- let match = false;
- let parent = null;
- let children = activeSession.data['children'];
- let newChildren = activeSession.data['newChildren'];
-
- while (children || newChildren) {
- match = false;
-
- if (children) {
- for (let iter of children) {
- if (node['path'] == iter['path']) {
- match = true;
- children = null;
- newChildren = null;
- break;
- } else if (node['path'].startsWith(iter['path'] + '/')) {
- match = true;
- parent = iter;
- children = iter['children'];
- if (('new' in node) && ('newChildren' in iter)) {
- newChildren = iter['newChildren'];
- } else {
- newChildren = null;
- }
- break;
- }
- }
- if (!match) {
- children = null;
- }
- }
- if (match) {
- continue;
- }
- if (newChildren) {
- for (let iter of newChildren) {
- if (node['path'] == iter['path']) {
- match = true;
- children = null;
- newChildren = null;
- break;
- } else if (node['path'].startsWith(iter['path'] + '/')) {
- match = true;
- parent = iter;
- children = iter['children'];
- if (('new' in node) && ('newChildren' in iter)) {
- newChildren = iter['newChildren'];
- } else {
- newChildren = null;
- }
- break;
- }
- }
- if (!match) {
- children = null;
- }
- }
- }
-
- if (!parent) {
- parent = activeSession.data;
- }
- return parent;
- }
-
schemaName(parent, child):string {
if (parent['module'] != child['module']) {
return child['module'] + ':' + child['name'];
@@ -179,46 +98,39 @@
}
}
- isDeleted(node, value = null): boolean {
+ isDeleted(node): boolean {
if ('deleted' in node) {
- if (typeof node['deleted'] === 'boolean') {
- return node['deleted'];
- } else if (value) {
- if (node['deleted'].indexOf(value) != -1) {
- return true;
- }
- }
+ return node['deleted'];
}
return false;
}
- delete(activeSession, node, value = null) {
+ private deleteChild(activeSession, parent, childArray, node) {
+ for (let i in parent[childArray]) {
+ if (parent[childArray][i]['path'] == node['path']) {
+ parent[childArray].splice(i, 1);
+ break;
+ }
+ }
+ if (childArray != 'children' && !parent[childArray].length) {
+ delete parent[childArray];
+ }
+ }
+
+ delete(activeSession, node) {
if ('new' in node) {
/* removing newly created subtree */
- let parent = this.nodeParent(activeSession, node);
+ let parent = this.treeService.nodeParent(activeSession, node);
if ('new' in parent) {
/* removing just a subtree of the created tree */
- for (let i in parent['children']) {
- if (parent['children'][i] == node) {
- parent['children'].splice(i, 1);
- break;
- }
- }
+ this.deleteChild(activeSession, parent, 'children', node);
} else {
this.removeModificationsRecord(activeSession, node['path']);
- for (let i in parent['newChildren']) {
- if (parent['newChildren'][i]['path'] == node['path']) {
- parent['newChildren'].splice(i, 1);
- break;
- }
- }
- if (!parent['newChildren'].length) {
- delete parent['newChildren'];
- }
+ this.deleteChild(activeSession, parent, 'newChildren', node);
}
} else {
let record = this.createModificationsRecord(activeSession, node['path']);
-
+ node['deleted'] = true;
if (!('type' in record)) {
/* new record */
record['type'] = 'delete';
@@ -230,24 +142,14 @@
delete record['original'];
delete record['value'];
}
- if (value) {
- if (!('deleted' in node) || typeof node['deleted'] === 'boolean') {
- node['deleted'] = [];
- }
- node['deleted'].push(value);
- } else if (node['info']['type'] == 8) {
- node['deleted'] = node['value'].slice(0);
- } else {
- node['deleted'] = true;
- }
}
}
change(activeSession, node, leafValue) {
+ let record = null;
if (!('new' in node)) {
- let record = this.createModificationsRecord(activeSession, node['path']);
+ record = this.createModificationsRecord(activeSession, node['path']);
if (!('type' in record)) {
- console.log(record);
/* new record */
if (node['value'] == leafValue) {
/* no change to the original value */
@@ -260,43 +162,71 @@
record['value'] = leafValue;
node['dirty'] = true;
} else if (record['type'] == 'change' && record['original'] == leafValue) {
- console.log(record);
/* change back to the original value, remove the change record */
this.removeModificationsRecord(activeSession, node['path']);
node['dirty'] = false;
} else {
- console.log(record);
/* another change of existing change record */
record['value'] = leafValue;
node['dirty'] = true;
}
+ } else if (node['info']['type'] == 8) {
+ record = this.getModificationsRecord(activeSession, node['path']);
+ let newPath = node['path'].slice(0, node['path'].lastIndexOf('[')) + '[.=\'' + leafValue + '\']';
+ this.renameModificationsRecord(activeSession, node['path'], newPath);
+ node['path'] = newPath
}
node['value'] = leafValue;
this.setEdit(activeSession, node, false);
}
- createOpen(schemas, node) {
+ createOpen(activeSession, schemas, node) {
//console.trace();
node['schemaChildren'] = schemas;
node['creatingChild'] = {};
if (schemas.length) {
- if (('newChildren' in node) && node['newChildren'].length) {
- delete node['newChildren'][node['newChildren'].length - 1]['last']
- } else if (('children' in node) && node['children'].length) {
- delete node['children'][node['children'].length - 1]['last'];
+ let children = this.treeService.childrenToShow(node);
+ console.log(children)
+ if (children.length) {
+ let last = children[children.length - 1];
+ if (last['info']['type'] == 16) {
+ let instances = this.treeService.getInstances(activeSession, last)
+ last = instances[instances.length - 1];
+ }
+ delete last['last'];
+ if (last['info']['type'] == 8) {
+ for (let sibling of this.treeService.getInstances(activeSession, last)) {
+ if ('last' in sibling) {
+ continue;
+ }
+ delete sibling["lastLeafList"];
+ }
+ }
}
}
}
- createClose(node, reason='abort') {
+ createClose(activeSession, node, reason='abort') {
//console.trace();
if (reason == 'abort' && node['schemaChildren'].length) {
- if (('newChildren' in node) && node['newChildren'].length) {
- node['newChildren'][node['newChildren'].length - 1]['last'] = true;
- } else if (('children' in node) && node['children'].length) {
- node['children'][node['children'].length - 1]['last'] = true;
+ let children = this.treeService.childrenToShow(node);
+ if (children.length) {
+ let last = children[children.length - 1];
+ if (last['info']['type'] == 16) {
+ let instances = this.treeService.getInstances(activeSession, last)
+ last = instances[instances.length - 1];
+ }
+ last['last'] = true;
+ if (last['info']['type'] == 8) {
+ for (let sibling of this.treeService.getInstances(activeSession, last)) {
+ if ('last' in sibling) {
+ continue;
+ }
+ sibling["lastLeafList"] = true;
+ }
+ }
}
}
delete node['creatingChild'];
@@ -338,7 +268,7 @@
if ('new' in node) {
if (!('children' in node)) {
- node['children'] = []
+ node['children'] = [];
}
node['children'].push(newNode)
} else {
@@ -355,7 +285,7 @@
newNode['children'] = [];
/* open creation dialog for nodes inside the created container */
this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
- this.createOpen(result, newNode);
+ this.createOpen(activeSession, result, newNode);
});
break;
case 4: /* leaf */
@@ -367,6 +297,16 @@
this.setEdit(activeSession, newNode, true)
break;
case 8: /* leaf-list */
+ /* find the first instance, if not, mark this as the first leaf-list instance */
+ for (let sibling of this.treeService.childrenToShow(node)) {
+ if (sibling == newNode) {
+ newNode['first'] = true;
+ break;
+ }
+ if (sibling['info']['name'] == newNode['info']['name'] && sibling['info']['module'] == newNode['info']['module']) {
+ break;
+ }
+ }
newNode['path'] = newNode['path'] + '[' + this.list_nextpos(node, newNode['path']) + ']';
this.setEdit(activeSession, newNode, true)
console.log(newNode);
@@ -377,7 +317,7 @@
/* open creation dialog for nodes inside the created list */
this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
if (result && result.length) {
- this.createOpen(result, newNode);
+ this.createOpen(activeSession, result, newNode);
}
if (newNode['schemaChildren'].length) {
@@ -395,7 +335,8 @@
newNode['schemaChildren'].splice(i, 1);
if (!newNode['schemaChildren'].length) {
newKey['last'] = true;
- this.createClose(newNode, 'success');
+ this.createClose(activeSession, newNode, 'success');
+ console.log(JSON.stringify(newNode));
}
}
}
@@ -406,7 +347,7 @@
if (!node['schemaChildren'].length) {
newNode['last'] = true;
- this.createClose(node, 'success');
+ this.createClose(activeSession, node, 'success');
}
if (!('new' in node)) {
@@ -440,7 +381,7 @@
if ('new' in node) {
/* removing newly created subtree */
- let parent = this.nodeParent(activeSession, node);
+ let parent = this.treeService.nodeParent(activeSession, node);
if ('new' in parent) {
/* removing just a subtree of the created tree */
for (let i in parent['children']) {
@@ -477,7 +418,7 @@
schemas = parent['schemaChildren'];
}
schemas.push(node['info']);
- this.createOpen(schemas, parent)
+ this.createOpen(activeSession, schemas, parent)
}
} else if (activeSession.modifications) {
let record = this.getModificationsRecord(activeSession, node['path']);
diff --git a/frontend/config/session.ts b/frontend/config/session.ts
index 5bdda36..09a3050 100644
--- a/frontend/config/session.ts
+++ b/frontend/config/session.ts
@@ -4,6 +4,7 @@
constructor (
public key: string,
public device: Device,
+ public loading = false,
public data = null,
public modifications = null,
public cpblts: string = "",
diff --git a/frontend/config/sessions.service.ts b/frontend/config/sessions.service.ts
index 1c6b5cd..d3b25d0 100644
--- a/frontend/config/sessions.service.ts
+++ b/frontend/config/sessions.service.ts
@@ -5,15 +5,16 @@
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
+import { TreeService } from './tree.service';
import { Device } from '../inventory/device';
import { Session } from './session';
@Injectable()
export class SessionsService implements OnInit {
public sessions: Session[];
- public activeSession;
+ public activeSession: string;
- constructor(private http: Http) {
+ constructor(private http: Http, private treeService: TreeService) {
this.activeSession = localStorage.getItem('activeSession');
if (!this.activeSession) {
this.activeSession = "";
@@ -85,6 +86,35 @@
}
}
+ collapse( activeSession, node = null ) {
+ if ( node ) {
+ delete node['children'];
+ activeSession.dataVisibility = 'mixed';
+ } else {
+ for ( let root of activeSession.data['children'] ) {
+ delete root['children'];
+ }
+ activeSession.dataVisibility = 'root';
+ }
+ this.treeService.updateHiddenFlags( activeSession );
+ this.storeData();
+ }
+
+ expand( activeSession, node, all: boolean ) {
+ node['loading'] = true;
+ this.rpcGetSubtree( activeSession.key, all, node['path'] ).subscribe( result => {
+ if ( result['success'] ) {
+ for ( let iter of result['data']['children'] ) {
+ this.treeService.setDirty( activeSession, iter );
+ }
+ node['children'] = result['data']['children'];
+ this.treeService.updateHiddenFlags( activeSession );
+ delete node['loading'];
+ this.storeData();
+ }
+ } );
+ }
+
checkValue(key: string, path: string, value: string): Observable<string[]> {
let params = new URLSearchParams();
params.set('key', key);
@@ -203,6 +233,35 @@
})
.catch((err: Response | any) => Observable.throw(err));
}
+ rpcGet( activeSession: Session, all: boolean ) {
+ if ( activeSession.data ) {
+ if ( ( all && activeSession.dataVisibility == 'all' ) ||
+ ( !all && activeSession.dataVisibility == 'root' ) ) {
+ return;
+ }
+ }
+ activeSession.loading = true;
+ delete activeSession.data;
+ this.rpcGetSubtree( activeSession.key, all ).subscribe( result => {
+ if ( result['success'] ) {
+ for ( let iter of result['data'] ) {
+ this.treeService.setDirty( activeSession, iter );
+ }
+ activeSession.data = {};
+ activeSession.data['path'] = '/';
+ activeSession.data['info'] = {};
+ activeSession.data['info']['path'] = '/';
+ activeSession.data['children'] = result['data'];
+ if ( all ) {
+ activeSession.dataVisibility = 'all';
+ } else {
+ activeSession.dataVisibility = 'root';
+ }
+ }
+ activeSession.loading = false;
+ this.storeData();
+ } );
+ }
commit(key: string) {
let activeSession = this.getActiveSession(key);
diff --git a/frontend/config/tree-indent.html b/frontend/config/tree-indent.html
index 0c0978d..7bfe142 100644
--- a/frontend/config/tree-indent.html
+++ b/frontend/config/tree-indent.html
@@ -15,12 +15,12 @@
(click)="cancelModification(node)"/>
</ng-container>
<ng-container *ngIf="getType() == 'value'">
- <ng-container [ngSwitch]="modsService.isDeleted(node, value)">
+ <ng-container [ngSwitch]="modsService.isDeleted(node)">
<img *ngSwitchCase="false" title="delete value"
class="icon_action" src="assets/netopeer/icons/close.svg"
onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
onmouseout="this.src='assets/netopeer/icons/close.svg'"
- (click)="deleteSubtree(node, value)"/>
+ (click)="deleteInstance(node)"/>
<img *ngSwitchCase="true" title="cancel modification"
class="icon_action" src="assets/netopeer/icons/close.svg"
onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
@@ -65,7 +65,7 @@
<img *ngIf="indent" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
</ng-container>
<ng-container [ngSwitch]="getType()">
- <img *ngSwitchCase="'value'" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
+ <img *ngSwitchCase="'value'" class="indentation value" src="assets/netopeer/icons/tree_empty.svg" />
<img *ngSwitchCase="'root'" class="indentation" src="assets/netopeer/icons/tree_root.svg" />
<img *ngSwitchCase="'edit'" class="indentation" src="assets/netopeer/icons/tree_empty.svg" />
<img *ngSwitchCase="'create'" class="indentation" src="assets/netopeer/icons/tree_last_branch.svg" />
diff --git a/frontend/config/tree-node.html b/frontend/config/tree-node.html
new file mode 100644
index 0000000..6cefbcc
--- /dev/null
+++ b/frontend/config/tree-node.html
@@ -0,0 +1,120 @@
+<div *ngIf="node['info']['type'] != 8 || node['first']" class="node" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)"
+ [class.yang-container]="node['info']['type'] == 1"
+ [class.yang-leaf]="node['info']['type'] == 4"
+ [class.yang-leaflist]="node['info']['type'] == 8"
+ [class.yang-list]="node['info']['type'] == 16">
+ <tree-indent [node]="node" [indentation]="indentation"></tree-indent>
+ <img class="icon" src="assets/netopeer/icons/info.svg" alt="info" title="{{node['info']['dsc']}}"
+ onmouseover="this.src='assets/netopeer/icons/info_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/info.svg'"/>
+
+ <!-- BEGIN nodetype-specific code -->
+ <!-- leaf -->
+ <ng-container *ngIf="node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)">
+ <ng-container *ngIf="node['info']['key']">
+ <img class="icon" src="assets/netopeer/icons/key.svg" alt="key" title="list key"/>
+ </ng-container>
+ <ng-container *ngIf="node['info']['config'] && !node['deleted'] && (!node['info']['key'] || node['new']) && node['info']['datatypebase'] != 'empty'">
+ <img *ngIf="!node['edit']" class="icon_action" src="assets/netopeer/icons/edit.svg"
+ alt="edit" title="edit value" tabindex=0
+ (click)="startEditing(node, $event.target);" (keyup.enter)="startEditing(node, $event.target);"
+ onmouseover="this.src='assets/netopeer/icons/edit_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/edit.svg'"/>
+ <img *ngIf="node['edit']" class="icon" src="assets/netopeer/icons/edit.svg" alt="edit" title="editing value"/>
+ </ng-container>
+ </ng-container>
+
+ <!-- container and lists -->
+ <ng-container *ngIf="!node['new'] && !node['deleted'] && ((node['info']['type'] == 16 || node['info']['type'] == 1)) && treeService.expandable(node)">
+ <img *ngIf="treeService.hasHiddenChild(node)" (click)="sessionsService.expand(activeSession, node, true)"
+ class="icon_action" src="assets/netopeer/icons/show_all.svg"
+ onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/show_all.svg'" alt="show-all" title="expand subtree"/>
+ <img *ngIf="!node['children']" (click)="sessionsService.expand(activeSession, node, false)"
+ class="icon_action" src="assets/netopeer/icons/show_children.svg"
+ onmouseover="this.src='assets/netopeer/icons/show_children_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/show_children.svg'" alt="show-children" title="expand children"/>
+ <img *ngIf="node['children']" (click)="sessionsService.collapse(activeSession, node)"
+ class="icon_action" src="assets/netopeer/icons/collapse.svg" alt="collapse" title="collapse"
+ onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
+ </ng-container>
+
+ <div class="node_info"><span class="node_name">{{node['info']['name']}}</span>
+ <!-- list's keys -->
+ <span *ngIf="node['info']['type'] == 16" class="keys">* [{{node['keys']}}]</span>
+
+ <!-- leaf and leaflists -->
+ <span *ngIf="node['info']['type'] == 8 || node['info']['type'] == 4">:</span>
+
+ <!-- leaf -->
+ <ng-container *ngIf="node['info']['type'] == 4">
+ <span class="value_inline" *ngIf="!node['edit']" (click)="startEditing(node, $event.target);">{{node['value']}}</span>
+ <span class="value_inline" *ngIf="node['edit']">{{node['info']['datatype']}}
+ <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
+ </span>
+ </ng-container>
+ <ng-container *ngIf="node['info']['type'] == 8">
+ <span class="value_inline">{{node['info']['datatype']}}
+ <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
+ </span>
+ </ng-container>
+ </div>
+ <!-- END nodetype-specific code -->
+
+ <div class="module_name" (click)="showSchema(node)" title="open schema {{node['info']['module']}} in YANG Explorer">{{moduleName(node)}}</div>
+</div>
+
+<!-- BEGIN nodetype-specific code -->
+<!-- leaflist's values -->
+<ng-container *ngIf="node['info']['type'] == 8">
+ <div class="node yang-leaflist-value" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)">
+ <tree-indent [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)" [type]="'value'" [value]="value"></tree-indent>
+ <div class="value_standalone">{{node['value']}}</div>
+ </div>
+</ng-container>
+
+<!-- leaf/leaf-list's edit value -->
+<ng-container *ngIf="node['info']['type'] == 4 || node['info']['type'] == 8">
+ <div class="node_edit" [class.dirty]="node['dirty']" *ngIf="node['edit']">
+ <tree-indent [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)" [type]="'edit'"></tree-indent>
+ <img *ngIf="node['edit'] " class="icon_action" src="assets/netopeer/icons/close.svg"
+ alt="cancel" title="cancel editing" tabindex=0
+ (click)="changeValueCancel(node)" (keyup.enter)="changeValueCancel(node)"
+ onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/close.svg'" />
+ <img *ngIf="node['edit']" class="icon_action icon_hidden" src="assets/netopeer/icons/confirm.svg"
+ id="{{node['path']}}_value_confirm" alt="done" title="store changes" tabindex=0
+ (click)="changeValue(node, $event.target)" (keyup.enter)="changeValue(node, $event.target)"
+ onmouseover="this.src='assets/netopeer/icons/confirm_active.svg'"
+ onmouseout="this.src='assets/netopeer/icons/confirm.svg'"/>
+ <select *ngIf="node['info']['values']"
+ id="{{node['path']}}_input" type="text" class="value_inline"
+ (change)="checkValue(node, $event.target, true)"
+ checkLeafValue [node]="node" [trusted]="true" (onCheckValue)="checkValue($event.node, $event.element, $event.trusted)">
+ <ng-container *ngFor="let value of node['info']['values']; let i='index'">
+ <option value="{{value}}">{{value}}</option>
+ </ng-container>
+ </select>
+ <input *ngIf="!node['info']['values']"
+ id="{{node['path']}}_input" type="text" class="value_inline" value="{{nodeValue(node)}}" tabindex=0
+ (keyup)="checkValue(node, $event.target)" (change)="checkValue(node, $event.target)"
+ (keyup.enter)="changeValue(node, $event.target)" (keyup.escape)="changeValueCancel(node)"
+ checkLeafValue [node]="node" (onCheckValue)="checkValue($event.node, $event.element)"/>
+ </div>
+</ng-container>
+
+<!-- END nodetype-specific code -->
+
+<!-- recursion - show children -->
+<div class="loading" *ngIf="node['loading']">
+ <netopeer-loading></netopeer-loading>
+</div>
+<div class="children" *ngIf="(node['children'] || node['newChildren']) && !node['deleted']">
+ <ng-container *ngFor="let child of treeService.childrenToShow(node)">
+ <tree-view [node]="child" [indentation]="treeService.inheritIndentation(indentation, node)"></tree-view>
+ </ng-container>
+</div>
+
+<!-- create new child dialog -->
+<tree-create [node]="node" [indentation]="treeService.inheritIndentation(indentation, node)"></tree-create>
diff --git a/frontend/config/tree.component.html b/frontend/config/tree.component.html
index f2415ab..b4a5492 100644
--- a/frontend/config/tree.component.html
+++ b/frontend/config/tree.component.html
@@ -2,20 +2,17 @@
<!-- recursion - show children -->
<div class="node">
<tree-indent [node]="node" [indentation]="indentation" [type]="'root'"></tree-indent>
- <img *ngIf="activeSession.dataVisibility!='all'" class="icon_action" (click)="treeService.rpcGet(activeSession, true)"
+ <img *ngIf="activeSession.dataVisibility!='all'" class="icon_action" (click)="sessionsService.rpcGet(activeSession, true)"
src="assets/netopeer/icons/show_all.svg" alt="w" title="expand all"
onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
onmouseout="this.src='assets/netopeer/icons/show_all.svg'"/>
- <img *ngIf="activeSession.dataVisibility!='root'" class="icon_action" (click)="treeService.collapse(activeSession)"
+ <img *ngIf="activeSession.dataVisibility!='root'" class="icon_action" (click)="sessionsService.collapse(activeSession)"
src="assets/netopeer/icons/collapse.svg" alt="x" title="collapse"
onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
</div>
<div class="children" *ngIf="node['children'] || node['newChildren']">
- <ng-container *ngFor="let child of node['children']">
- <tree-view [node]="child"></tree-view>
- </ng-container>
- <ng-container *ngFor="let child of newChildrenToShow(node)">
+ <ng-container *ngFor="let child of treeService.childrenToShow(node)">
<tree-view [node]="child"></tree-view>
</ng-container>
</div>
@@ -26,127 +23,7 @@
<div class="subtree" *ngIf="node['path'] != '/' && (node['info']['config'] || activeSession.statusVisibility)"
[class.status]="!node['info']['config']">
- <div class="node" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node)"
- [class.yang-container]="node['info']['type'] == 1"
- [class.yang-leaf]="node['info']['type'] == 4"
- [class.yang-leaflist]="node['info']['type'] == 8"
- [class.yang-list]="node['info']['type'] == 16">
- <tree-indent [node]="node" [indentation]="indentation"></tree-indent>
- <img class="icon" src="assets/netopeer/icons/info.svg" alt="info" title="{{node['info']['dsc']}}"
- onmouseover="this.src='assets/netopeer/icons/info_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/info.svg'"/>
-
- <!-- BEGIN nodetype-specific code -->
- <!-- leaf -->
- <ng-container *ngIf="node['info']['type'] == 4 || (node['new'] && node['info']['type'] == 8)">
- <ng-container *ngIf="node['info']['key']">
- <img class="icon" src="assets/netopeer/icons/key.svg" alt="key" title="list key"/>
- </ng-container>
- <ng-container *ngIf="node['info']['config'] && !node['deleted'] && (!node['info']['key'] || node['new']) && node['info']['datatypebase'] != 'empty'">
- <img *ngIf="!node['edit']" class="icon_action" src="assets/netopeer/icons/edit.svg"
- alt="edit" title="edit value" tabindex=0
- (click)="startEditing(node, $event.target);" (keyup.enter)="startEditing(node, $event.target);"
- onmouseover="this.src='assets/netopeer/icons/edit_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/edit.svg'"/>
- <img *ngIf="node['edit']" class="icon" src="assets/netopeer/icons/edit.svg" alt="edit" title="editing value"/>
- </ng-container>
- </ng-container>
-
- <!-- container and lists -->
- <ng-container *ngIf="!node['new'] && !node['deleted'] && ((node['info']['type'] == 16 || node['info']['type'] == 1)) && treeService.expandable(node)">
- <img *ngIf="treeService.hasHiddenChild(node)" (click)="treeService.expand(activeSession, node, true)"
- class="icon_action" src="assets/netopeer/icons/show_all.svg"
- onmouseover="this.src='assets/netopeer/icons/show_all_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/show_all.svg'" alt="show-all" title="expand subtree"/>
- <img *ngIf="!node['children']" (click)="treeService.expand(activeSession, node, false)"
- class="icon_action" src="assets/netopeer/icons/show_children.svg"
- onmouseover="this.src='assets/netopeer/icons/show_children_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/show_children.svg'" alt="show-children" title="expand children"/>
- <img *ngIf="node['children']" (click)="treeService.collapse(activeSession, node)"
- class="icon_action" src="assets/netopeer/icons/collapse.svg" alt="collapse" title="collapse"
- onmouseover="this.src='assets/netopeer/icons/collapse_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/collapse.svg'"/>
- </ng-container>
-
- <div class="node_info"><span class="node_name">{{node['info']['name']}}</span>
- <!-- list's keys -->
- <span *ngIf="node['info']['type'] == 16" class="keys">* [{{node['keys']}}]</span>
-
- <!-- leaf and leaflists -->
- <span *ngIf="node['info']['type'] == 8 || node['info']['type'] == 4">:</span>
-
- <!-- leaf -->
- <ng-container *ngIf="node['info']['type'] == 4">
- <span class="value" *ngIf="!node['edit']" (click)="startEditing(node, $event.target);">{{node['value']}}</span>
- <span class="value" *ngIf="node['edit']">{{node['info']['datatype']}}
- <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
- </span>
- </ng-container>
- <ng-container *ngIf="node['info']['type'] == 8">
- <span class="value">{{node['info']['datatype']}}
- <span *ngIf="node['info']['datatype'] != node['info']['datatypebase']">({{node['info']['datatypebase']}})</span>
- </span>
- </ng-container>
- </div>
- <!-- END nodetype-specific code -->
-
- <div class="module_name" (click)="showSchema(node)" title="open schema {{node['info']['module']}} in YANG Explorer">{{moduleName(node)}}</div>
- </div>
-
- <!-- BEGIN nodetype-specific code -->
- <!-- leaf/leaf-list's edit value -->
- <ng-container *ngIf="node['info']['type'] == 4 || node['info']['type'] == 8">
- <div class="node_edit" [class.dirty]="node['dirty']" *ngIf="node['edit']">
- <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'edit'"></tree-indent>
- <img *ngIf="node['edit'] " class="icon_action" src="assets/netopeer/icons/close.svg"
- alt="cancel" title="cancel editing" tabindex=0
- (click)="changeValueCancel(node)" (keyup.enter)="changeValueCancel(node)"
- onmouseover="this.src='assets/netopeer/icons/close_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/close.svg'" />
- <img *ngIf="node['edit']" class="icon_action icon_hidden" src="assets/netopeer/icons/confirm.svg"
- id="{{node['path']}}_value_confirm" alt="done" title="store changes" tabindex=0
- (click)="changeValue(node, $event.target)" (keyup.enter)="changeValue(node, $event.target)"
- onmouseover="this.src='assets/netopeer/icons/confirm_active.svg'"
- onmouseout="this.src='assets/netopeer/icons/confirm.svg'"/>
- <select *ngIf="node['info']['values']"
- id="{{node['path']}}_input" type="text" class="value"
- (change)="checkValue(node, $event.target, true)"
- checkLeafValue [node]="node" [trusted]="true" (onCheckValue)="checkValue($event.node, $event.element, $event.trusted)">
- <ng-container *ngFor="let value of node['info']['values']; let i='index'">
- <option value="{{value}}">{{value}}</option>
- </ng-container>
- </select>
- <input *ngIf="!node['info']['values']"
- id="{{node['path']}}_input" type="text" class="value" value="{{nodeValue(node)}}" tabindex=0
- (keyup)="checkValue(node, $event.target)" (change)="checkValue(node, $event.target)"
- (keyup.enter)="changeValue(node, $event.target)" (keyup.escape)="changeValueCancel(node)"
- checkLeafValue [node]="node" (onCheckValue)="checkValue($event.node, $event.element)"/>
- </div>
- </ng-container>
-
- <!-- leaflist's values -->
- <ng-container *ngIf="node['info']['type'] == 8 && !node['edit'] && !modsService.isDeleted(node)">
- <div *ngFor="let value of node['value']"
- class="node yang-leaflist-value" [class.dirty]="node['dirty']" [class.deleted]="modsService.isDeleted(node, value)">
- <tree-indent [node]="node" [indentation]="inheritIndentation(node)" [type]="'value'" [value]="value"></tree-indent>
- <div class="value_standalone">{{value}}</div>
- </div>
+ <ng-container *ngFor="let item of treeService.nodesToShow(activeSession, node)">
+ <tree-node [activeSession]="activeSession" [node]="item" [indentation]="indentation"></tree-node>
</ng-container>
- <!-- END nodetype-specific code -->
-
- <!-- recursion - show children -->
- <div class="loading" *ngIf="node['loading']">
- <netopeer-loading></netopeer-loading>
- </div>
- <div class="children" *ngIf="(node['children'] || node['newChildren']) && !node['deleted']">
- <ng-container *ngFor="let child of node['children']">
- <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
- </ng-container>
- <ng-container *ngFor="let child of newChildrenToShow(node)">
- <tree-view [node]="child" [indentation]="inheritIndentation(node)"></tree-view>
- </ng-container>
- </div>
-
- <!-- create new child dialog -->
- <tree-create [node]="node" [indentation]="inheritIndentation(node)"></tree-create>
</div>
diff --git a/frontend/config/tree.component.scss b/frontend/config/tree.component.scss
index 3e8bd4a..8a2ced3 100644
--- a/frontend/config/tree.component.scss
+++ b/frontend/config/tree.component.scss
@@ -108,10 +108,15 @@
flex-grow: 1;
}
-.value {
+.value_inline {
margin-left: 1.5em;
}
+.indentation.value {
+ width: 0.8em;
+ height: 100%;
+}
+
.indentation {
width: 1.7em;
height: 100%;
diff --git a/frontend/config/tree.component.ts b/frontend/config/tree.component.ts
index bcb4da4..2810095 100644
--- a/frontend/config/tree.component.ts
+++ b/frontend/config/tree.component.ts
@@ -5,7 +5,7 @@
import {Schema} from '../inventory/schema';
import {ModificationsService} from './modifications.service';
import {SessionsService} from './sessions.service';
-import {TreeService} from './config.component';
+import {TreeService} from './tree.service';
import {SchemasService} from '../yang/schemas.service';
@Directive({
@@ -60,10 +60,11 @@
}
closeCreatingDialog(node, reason='abort') {
- this.modsService.createClose(node, reason);
+ this.modsService.createClose(this.activeSession, node, reason);
}
creatingDialogSelect(node, index, source) {
+ console.log(node)
this.modsService.create(this.activeSession, node, index);
this.sessionsService.storeData();
if (('schemaChildren' in node) && node['schemaChildren'].length) {
@@ -85,7 +86,9 @@
activeSession: Session;
private timeout;
- constructor(private modsService: ModificationsService, private sessionsService: SessionsService) {}
+ constructor(private treeService: TreeService,
+ private modsService: ModificationsService,
+ private sessionsService: SessionsService) {}
ngOnInit(): void {
this.activeSession = this.sessionsService.getActiveSession();
@@ -119,36 +122,31 @@
menu.style.visibility = "hidden";
}
- deleteSubtree(node, value = null) {
- this.modsService.delete(this.activeSession, node, value);
+ deleteSubtree(node) {
+ let rmlist = [];
+ if (node['info']['type'] == 8) {
+ rmlist = this.treeService.nodesToShow(this.activeSession, node);
+ } else {
+ rmlist.push(node);
+ }
+ for (let item of rmlist) {
+ this.modsService.delete(this.activeSession, item);
+ }
this.sessionsService.storeData();
}
- /* 0 - not deleted, 1 - deleted value, 2 - deleted all values */
- isDeleted(): number {
- if ('deleted' in this.node) {
- if (typeof this.node['deleted'] === 'boolean') {
- if (this.node['deleted']) {
- return 2;
- } else {
- return 0;
- }
- } else if (this.value) {
- if (this.node['deleted'].indexOf(this.value) != -1) {
- return 1;
- }
- }
- }
- return 0;
+ deleteInstance(node) {
+ this.modsService.delete(this.activeSession, node);
+ this.sessionsService.storeData();
}
openCreatingDialog(element, node, parent = false) {
if (parent) {
- node = this.modsService.nodeParent(this.activeSession, node);
+ node = this.treeService.nodeParent(this.activeSession, node);
}
if (!('creatingChild' in node)) {
this.sessionsService.childrenSchemas(this.activeSession.key, node['info']['path'], node).then(result => {
- this.modsService.createOpen(result, node);
+ this.modsService.createOpen(this.activeSession, result, node);
});
} else if (element){
/* scroll to the existing element */
@@ -157,7 +155,7 @@
}
closeCreatingDialog(node, reason='abort') {
- this.modsService.createClose(node, reason);
+ this.modsService.createClose(this.activeSession, node, reason);
}
cancelModification(node, value = null) {
@@ -172,15 +170,15 @@
}
@Component({
- selector: 'tree-view',
- templateUrl: './tree.component.html',
+ selector: 'tree-node',
+ templateUrl: './tree-node.html',
styleUrls: ['./tree.component.scss']
})
-export class TreeView implements OnInit {
+export class TreeNode {
@Input() node;
@Input() indentation;
- activeSession: Session;
+ @Input() activeSession: Session;
constructor(private modsService: ModificationsService,
private sessionsService: SessionsService,
@@ -189,10 +187,6 @@
private changeDetector: ChangeDetectorRef,
private router: Router) {}
- ngOnInit(): void {
- this.activeSession = this.sessionsService.getActiveSession();
- }
-
inheritIndentation(node) {
let newIndent;
if (node['last']) {
@@ -269,7 +263,7 @@
changeValue(node, target) {
let input;
- if (target.classList.contains('value')) {
+ if (target.classList.contains('value_inline')) {
if (target.classList.contains('invalid')) {
return;
}
@@ -279,7 +273,7 @@
}
if (node['info']['type'] == 8) {
- this.modsService.change(this.activeSession, node, [input.value]);
+ this.modsService.change(this.activeSession, node, input.value);
} else {
this.modsService.change(this.activeSession, node, input.value);
}
@@ -331,3 +325,23 @@
}
}
}
+
+@Component({
+ selector: 'tree-view',
+ templateUrl: './tree.component.html',
+ styleUrls: ['./tree.component.scss']
+})
+
+export class TreeView implements OnInit {
+ @Input() node;
+ @Input() indentation;
+ activeSession: Session;
+
+ constructor(private sessionsService: SessionsService,
+ private treeService: TreeService) {}
+
+ ngOnInit(): void {
+ this.activeSession = this.sessionsService.getActiveSession();
+ }
+}
+
diff --git a/frontend/config/tree.service.ts b/frontend/config/tree.service.ts
new file mode 100644
index 0000000..11ffbee
--- /dev/null
+++ b/frontend/config/tree.service.ts
@@ -0,0 +1,230 @@
+import {Injectable} from '@angular/core';
+
+import {Session} from './session';
+
+@Injectable()
+export class TreeService {
+
+ constructor() {}
+
+ expandable(node): boolean {
+ if (node['info']['type'] == 1 || /* container */
+ node['info']['type'] == 16) { /* list */
+ return true;
+ }
+ return false;
+ }
+
+ nodeParent(activeSession: Session, node) {
+ if (node['path'] =='/') {
+ return null;
+ }
+
+ let match = false;
+ let parent = null;
+ let children = activeSession.data['children'];
+ let newChildren = activeSession.data['newChildren'];
+
+ while (children || newChildren) {
+ match = false;
+
+ if (children) {
+ for (let iter of children) {
+ if (node['path'] == iter['path']) {
+ match = true;
+ children = null;
+ newChildren = null;
+ break;
+ } else if (node['path'].startsWith(iter['path'] + '/')) {
+ match = true;
+ parent = iter;
+ children = iter['children'];
+ if (('new' in node) && ('newChildren' in iter)) {
+ newChildren = iter['newChildren'];
+ } else {
+ newChildren = null;
+ }
+ break;
+ }
+ }
+ if (!match) {
+ children = null;
+ }
+ }
+ if (match) {
+ continue;
+ }
+ if (newChildren) {
+ for (let iter of newChildren) {
+ if (node['path'] == iter['path']) {
+ match = true;
+ children = null;
+ newChildren = null;
+ break;
+ } else if (node['path'].startsWith(iter['path'] + '/')) {
+ match = true;
+ parent = iter;
+ children = iter['children'];
+ if (('new' in node) && ('newChildren' in iter)) {
+ newChildren = iter['newChildren'];
+ } else {
+ newChildren = null;
+ }
+ break;
+ }
+ }
+ if (!match) {
+ children = null;
+ }
+ }
+ }
+
+ if (!parent) {
+ parent = activeSession.data;
+ }
+ return parent;
+ }
+
+ inheritIndentation(indentation, node) {
+ let newIndent;
+ if (node['last'] || ('lastLeafList' in node)) {
+ newIndent = [true];
+ } else {
+ newIndent = [false];
+ }
+
+ if (!indentation) {
+ return newIndent;
+ } else {
+ return indentation.concat(newIndent);
+ }
+ }
+
+ childrenToShow(node) {
+ let result = [];
+ let nc_dup = [];
+ if ('newChildren' in node) {
+ nc_dup = node['newChildren'].slice();
+ }
+ if ('children' in node) {
+ let lastList = null;
+ for (let child of node['children']) {
+ if (lastList) {
+ if (lastList['name'] == child['info']['name'] && lastList['module'] == child['info']['module']) {
+ continue;
+ } else {
+ lastList = null;
+ }
+ }
+ if (child['info']['type'] == 16 || child['info']['type'] == 8) {
+ lastList = child['info'];
+ for (let i = nc_dup.length - 1; i >= 0; i--) {
+ if (lastList['name'] == nc_dup[i]['info']['name'] && lastList['module'] == nc_dup[i]['info']['module']) {
+ nc_dup.splice(Number(i), 1);
+ }
+ }
+ }
+ result.push(child);
+ }
+ }
+ if (nc_dup.length) {
+ result = result.concat(nc_dup);
+ }
+ return result;
+ }
+
+ getInstances(activeSession, node, result = []) {
+ let parent = this.nodeParent(activeSession, node);
+ if ('children' in parent) {
+ for (let child of parent['children']) {
+ if (node['info']['name'] == child['info']['name'] && node['info']['module'] == child['info']['module']) {
+ result.push(child);
+ }
+ }
+ }
+ if ('newChildren' in parent) {
+ for (let child of parent['newChildren']) {
+ if (node['info']['name'] == child['info']['name'] && node['info']['module'] == child['info']['module']) {
+ result.push(child);
+ }
+ }
+ }
+ return result;
+ }
+
+ nodesToShow(activeSession, node) {
+ let result = [];
+ if (node['info']['type'] == 16) {
+ this.getInstances(activeSession, node, result);
+ } else if (node['info']['type'] == 8) {
+ if (node['first']) {
+ this.getInstances(activeSession, node, result);
+ }
+ } else {
+ result.push(node);
+ }
+ return result;
+ }
+
+ setDirty(activeSession, node) {
+ if (!activeSession.modifications) {
+ return;
+ }
+
+ if (node['path'] in activeSession.modifications) {
+ node['dirty'] = true;
+ if (activeSession.modifications[node['path']]['type'] == 'change') {
+ activeSession.modifications[node['path']]['original'] = node['value'];
+ }
+ node['value'] = activeSession.modifications[node['path']]['value'];
+ }
+ /* recursion */
+ if ('children' in node) {
+ for (let child of node['children']) {
+ this.setDirty(activeSession, child);
+ }
+ }
+ }
+
+ hasHiddenChild(node, clean=false): boolean {
+ if (!clean && 'hasHiddenChild' in node) {
+ return node['hasHiddenChild'];
+ }
+ node['hasHiddenChild'] = false;
+ if (!this.expandable(node)) {
+ /* terminal node (leaf or leaf-list) */
+ return node['hasHiddenChild'];
+ } else if (!('children' in node)) {
+ /* internal node without children */
+ node['hasHiddenChild'] = true;
+ } else {
+ /* go recursively */
+ for (let child of node['children']) {
+ if (this.hasHiddenChild(child, clean)) {
+ node['hasHiddenChild'] = true;
+ break;
+ }
+ }
+ }
+ return node['hasHiddenChild'];
+ }
+
+ updateHiddenFlags(activeSession) {
+ let mixed = false;
+ let rootsonly = true;
+ for (let root of activeSession.data['children']) {
+ if (this.hasHiddenChild(root, true)) {
+ mixed = true;
+ } else {
+ rootsonly = false;
+ }
+ }
+ if (mixed) {
+ if (rootsonly) {
+ activeSession.dataVisibility = 'root';
+ } else {
+ activeSession.dataVisibility = 'mixed';
+ }
+ }
+ }
+}
diff --git a/frontend/netopeer.module.ts b/frontend/netopeer.module.ts
index 72f3097..c5daeb7 100644
--- a/frontend/netopeer.module.ts
+++ b/frontend/netopeer.module.ts
@@ -20,13 +20,14 @@
import { InventorySchemasComponent } from './inventory/schemas.component';
import { InventoryDevicesComponent } from './inventory/devices.component';
import { ConfigComponent } from './config/config.component';
-import { TreeView, TreeIndent, TreeCreate, TreeScrollTo, CheckLeafValue } from './config/tree.component';
+import { TreeView, TreeNode, TreeIndent, TreeCreate, TreeScrollTo, CheckLeafValue } from './config/tree.component';
import { YANGComponent } from './yang/yang.component';
import { MonitoringComponent } from './monitoring/monitoring.component';
import { PluginsComponent } from './plugins/plugins.component';
-import { SessionsService } from './config/sessions.service'
-import { SchemasService } from './yang/schemas.service'
+import { SessionsService } from './config/sessions.service';
+import { SchemasService } from './yang/schemas.service';
+import { TreeService } from './config/tree.service';
const routes: Routes = [
{ path : 'netopeer', component : NetopeerComponent, canActivate : [AuthGuard],
@@ -103,6 +104,7 @@
TreeScrollTo,
TreeIndent,
TreeCreate,
+ TreeNode,
TreeView,
YANGComponent,
MonitoringComponent,
@@ -110,7 +112,8 @@
],
providers: [
SessionsService,
- SchemasService
+ SchemasService,
+ TreeService
],
entryComponents : [
NetopeerComponent