frontend CHANGE refactor data modification
move data manipulation operations into a separate service to allow
better splitting to components without code/templates duplication
and communication between components via events
diff --git a/frontend/config/modifications.service.ts b/frontend/config/modifications.service.ts
new file mode 100644
index 0000000..a0de997
--- /dev/null
+++ b/frontend/config/modifications.service.ts
@@ -0,0 +1,471 @@
+import { Injectable } from '@angular/core';
+
+import { Session} from './session';
+import { SessionsService } from './sessions.service';
+
+@Injectable()
+export class ModificationsService{
+
+ constructor(private sessionsService: SessionsService) {}
+
+ createModificationsRecord(activeSession, path) {
+ if (!activeSession.modifications) {
+ activeSession.modifications = {};
+ }
+
+ if (!(path in activeSession.modifications)) {
+ activeSession.modifications[path] = {};
+ }
+ return activeSession.modifications[path];
+ }
+
+ getModificationsRecord(activeSession, path) {
+ if (!activeSession.modifications) {
+ return null;
+ }
+
+ if (!(path in activeSession.modifications)) {
+ return null;
+ }
+ return activeSession.modifications[path];
+ }
+
+ removeModificationsRecord(activeSession, path = null) {
+ if (!activeSession.modifications) {
+ return;
+ }
+
+ if (path && (path in activeSession.modifications)) {
+ delete activeSession.modifications[path];
+ }
+
+ if (!Object.keys(activeSession.modifications).length) {
+ delete activeSession.modifications;
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ setEdit(node, set = true) {
+ if (set && node['info']['datatypebase'] == 'empty') {
+ node['value'] = '';
+ return;
+ }
+ node['edit'] = set;
+ }
+
+ setLast(list) {
+ let last;
+ for (let iter of list) {
+ delete iter['last'];
+ last = iter;
+ }
+ last['last'] = true;
+ }
+
+ nodeParent(activeSession, node) {
+ if (node['path'] =='/') {
+ return null;
+ }
+
+ let match = false;
+ let parent = null;
+ let children = activeSession.data['children'];
+ let 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'];
+ } else {
+ return child['name'];
+ }
+ }
+
+ delete(activeSession, node) {
+ if ('new' in node) {
+ /* removing newly created subtree */
+ let parent = this.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;
+ }
+ }
+ } 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'];
+ }
+ }
+ } else {
+ let record = this.createModificationsRecord(activeSession, node['path']);
+
+ if (!('type' in record)) {
+ /* new record */
+ record['type'] = 'delete';
+ record['original'] = node;
+ node['deleted'] = true;
+ node['dirty'] = true;
+ } else if (record['type'] == 'change') {
+ record['type'] = 'delete';
+ node['value'] = record['original'];
+ delete record['original'];
+ delete record['value'];
+ node['deleted'] = true;
+ }
+ }
+ }
+
+ change(activeSession, node, leafValue) {
+ if (!('new' in node)) {
+ let 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 */
+ this.setEdit(node, false);
+ this.removeModificationsRecord(activeSession);
+ return;
+ }
+ record['type'] = 'change';
+ record['original'] = node['value'];
+ 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;
+ }
+ }
+
+ node['value'] = leafValue;
+ this.setEdit(node, false);
+ }
+
+ createOpen(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'];
+ }
+ }
+ }
+
+ createClose(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;
+ }
+ }
+ delete node['creatingChild'];
+ delete node['schemaChildren'];
+ }
+
+ create(activeSession, node, index) {
+ //console.trace();
+ let newNode = {};
+ newNode['new'] = true;
+ newNode['info'] = node['schemaChildren'][index];
+ if (node['path'] == '/') {
+ newNode['path'] = '/' + this.schemaName(node['info'], newNode['info']);
+ } else {
+ newNode['path'] = node['path'] + '/' + this.schemaName(node['info'], newNode['info']);
+ }
+ newNode['dirty'] = true;
+
+ if ('new' in node) {
+ if (!('children' in node)) {
+ node['children'] = []
+ }
+ node['children'].push(newNode)
+ } else {
+ if (!('newChildren' in node)) {
+ node['newChildren'] = [];
+ }
+ node['newChildren'].push(newNode);
+ }
+
+ switch(newNode['info']['type']) {
+ case 1: { /* container */
+ node['schemaChildren'].splice(index, 1);
+
+ 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);
+ });
+ break;
+ }
+ case 4: { /* leaf */
+ node['schemaChildren'].splice(index, 1);
+
+ newNode['value'] = newNode['info']['default'];
+ this.setEdit(newNode, true)
+ break;
+ }
+ case 16: { /* list */
+ let search;
+ if ('new' in node) {
+ search = node['children'];
+ } else {
+ search = node['newChildren'];
+ }
+ let pos = 1;
+ if (search.length) {
+ for (let sibling of search) {
+ if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
+ let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
+ if (n >= pos) {
+ pos = n + 1;
+ }
+ }
+ }
+ }
+ newNode['path'] = newNode['path'] + '[' + pos + ']';
+
+ newNode['children'] = [];
+ /* open creation dialog for nodes inside the created list */
+ this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
+ this.createOpen(result, newNode);
+
+ if (newNode['schemaChildren'].length) {
+ for (let i in newNode['schemaChildren']) {
+ if (!newNode['schemaChildren'][i]['key']) {
+ continue;
+ }
+ let newKey = {};
+ newKey['new'] = true;
+ newKey['info'] = newNode['schemaChildren'][i];
+ newKey['path'] = newNode['path'] + '/' + this.schemaName(newNode['info'], newKey['info']);
+ newKey['dirty'] = true;
+ this.setEdit(newKey, true)
+ newNode['children'].push(newKey)
+ newNode['schemaChildren'].splice(i, 1);
+ }
+ }
+ });
+
+ break;
+ }
+ }
+
+ if (!node['schemaChildren'].length) {
+ newNode['last'] = true;
+ this.createClose(node, 'success');
+ }
+
+ if (!('new' in node)) {
+ /* store the record about the newly created data */
+ let record = this.createModificationsRecord(activeSession, newNode['path']);
+ if (('type' in record) && record['type'] == 'delete') {
+ record['type'] = 'change';
+ record['value'] = newNode;
+ delete record['original']['deleted'];
+ for (let i in node['children']) {
+ if (node['children'][i] == record['original']) {
+ node['children'].splice(i, 1);
+ break;
+ }
+ }
+ } else {
+ record['type'] = 'create';
+ record['data'] = newNode;
+ }
+ }
+ console.log(node)
+ }
+
+ cancelModification(activeSession, node = activeSession.data, recursion = true) {
+ if ('creatingChild' in node) {
+ delete node['creatingChild'];
+ }
+ if ('deleted' in node) {
+ node['dirty'] = false;
+ node['deleted'] = false;
+ }
+
+ if ('new' in node) {
+ /* removing newly created subtree */
+ let parent = this.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) {
+ if (Number(i) > 0 && parent['children'][i]['last']) {
+ parent['children'][Number(i) - 1]['last'] = true;
+ }
+ parent['children'].splice(i, 1);
+ break;
+ }
+ }
+ } else {
+ this.removeModificationsRecord(activeSession, node['path']);
+ for (let i in parent['newChildren']) {
+ if (parent['newChildren'][i]['path'] == node['path']) {
+ if (Number(i) > 0 && parent['newChildren'][i]['last']) {
+ parent['newChildren'][Number(i) - 1]['last'] = true;
+ }
+ parent['newChildren'].splice(i, 1);
+ break;
+ }
+ }
+ if (!parent['newChildren'].length) {
+ delete parent['newChildren'];
+ }
+ }
+
+ if (node['info']['type'] == 1 || node['info']['type'] == 4) {
+ /* fix the list of nodes to create in parent */
+ let schemas;
+ if (!('schemaChildren' in parent)) {
+ schemas = [];
+ } else {
+ schemas = parent['schemaChildren'];
+ }
+ schemas.push(node['info']);
+ this.createOpen(schemas, parent)
+ }
+ } else if (activeSession.modifications) {
+ let record = this.getModificationsRecord(activeSession, node['path']);
+ if (record) {
+ node['dirty'] = false;
+ if (record['type'] == 'change') {
+ node['value'] = record['original'];
+ }
+ this.removeModificationsRecord(activeSession, node['path']);
+ if (!activeSession.modifications) {
+ return;
+ }
+ }
+ }
+
+ /* recursion */
+ if (recursion && 'children' in node) {
+ if ('newChildren' in node) {
+ for (let child of node['newChildren']) {
+ let record = this.getModificationsRecord(activeSession, child['path']);
+ if (record['type'] == 'change') {
+ if (node['children'].length) {
+ node['children'][node['children'].length - 1]['last'] = false;
+ }
+ record['original']['last'] = true;
+ node['children'].push(record['original'])
+ }
+ this.removeModificationsRecord(activeSession, child['path']);
+ }
+ delete node['newChildren'];
+ if (('children' in node) && node['children'].length) {
+ node['children'][node['children'].length - 1]['last'] = true;
+ }
+ }
+ let last;
+ for (let child of node['children']) {
+ this.cancelModification(activeSession, child);
+ delete child['last'];
+ last = child;
+ }
+ last['last'] = true;
+ }
+ }
+}