concepto-user-controls 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/concepto-tree/concepto-tree.component.mjs +19 -0
- package/esm2022/lib/entity-comparison/components/entity-comparison.component.mjs +218 -0
- package/esm2022/lib/entity-comparison/core/services/entity-comparison.service.mjs +111 -0
- package/esm2022/public-api.mjs +4 -1
- package/fesm2022/concepto-user-controls.mjs +339 -1
- package/fesm2022/concepto-user-controls.mjs.map +1 -1
- package/lib/concepto-tree/concepto-tree.component.d.ts +5 -0
- package/lib/entity-comparison/components/entity-comparison.component.d.ts +49 -0
- package/lib/entity-comparison/core/services/entity-comparison.service.d.ts +10 -0
- package/package.json +1 -1
- package/public-api.d.ts +3 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export class ConceptoTreeComponent {
|
|
4
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConceptoTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ConceptoTreeComponent, isStandalone: true, selector: "lib-concepto-tree", ngImport: i0, template: `
|
|
6
|
+
<p>
|
|
7
|
+
concepto-tree works!
|
|
8
|
+
</p>
|
|
9
|
+
`, isInline: true, styles: [""] });
|
|
10
|
+
}
|
|
11
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConceptoTreeComponent, decorators: [{
|
|
12
|
+
type: Component,
|
|
13
|
+
args: [{ selector: 'lib-concepto-tree', standalone: true, imports: [], template: `
|
|
14
|
+
<p>
|
|
15
|
+
concepto-tree works!
|
|
16
|
+
</p>
|
|
17
|
+
` }]
|
|
18
|
+
}] });
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uY2VwdG8tdHJlZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9jb25jZXB0by11c2VyLWNvbnRyb2xzL3NyYy9saWIvY29uY2VwdG8tdHJlZS9jb25jZXB0by10cmVlLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQWExQyxNQUFNLE9BQU8scUJBQXFCO3dHQUFyQixxQkFBcUI7NEZBQXJCLHFCQUFxQiw2RUFQdEI7Ozs7R0FJVDs7NEZBR1UscUJBQXFCO2tCQVhqQyxTQUFTOytCQUNFLG1CQUFtQixjQUNqQixJQUFJLFdBQ1AsRUFBRSxZQUNEOzs7O0dBSVQiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnbGliLWNvbmNlcHRvLXRyZWUnLFxyXG4gIHN0YW5kYWxvbmU6IHRydWUsXHJcbiAgaW1wb3J0czogW10sXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxwPlxyXG4gICAgICBjb25jZXB0by10cmVlIHdvcmtzIVxyXG4gICAgPC9wPlxyXG4gIGAsXHJcbiAgc3R5bGVzOiBgYFxyXG59KVxyXG5leHBvcnQgY2xhc3MgQ29uY2VwdG9UcmVlQ29tcG9uZW50IHtcclxuXHJcbn1cclxuIl19
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { EntityComparisonService } from '../core/services/entity-comparison.service';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "../core/services/entity-comparison.service";
|
|
6
|
+
import * as i2 from "@angular/common";
|
|
7
|
+
export class EntityComparisonComponent {
|
|
8
|
+
comparisonService;
|
|
9
|
+
leftFile = '';
|
|
10
|
+
rightFile = '';
|
|
11
|
+
leftEntities = [];
|
|
12
|
+
rightEntities = [];
|
|
13
|
+
selectedLeftEntity = null;
|
|
14
|
+
selectedRightEntity = null;
|
|
15
|
+
comparisonResult = null;
|
|
16
|
+
expandedNodes = new Set();
|
|
17
|
+
isFullScreen = false;
|
|
18
|
+
currentDifferenceIndex = 0;
|
|
19
|
+
differences = [];
|
|
20
|
+
constructor(comparisonService) {
|
|
21
|
+
this.comparisonService = comparisonService;
|
|
22
|
+
}
|
|
23
|
+
onLeftFileSelected(event) {
|
|
24
|
+
const file = event.target.files[0];
|
|
25
|
+
if (!file)
|
|
26
|
+
return;
|
|
27
|
+
this.leftFile = file.name;
|
|
28
|
+
const reader = new FileReader();
|
|
29
|
+
reader.onload = (e) => {
|
|
30
|
+
const xmlContent = e.target.result;
|
|
31
|
+
this.leftEntities = this.comparisonService.parseEntities(xmlContent);
|
|
32
|
+
this.selectedLeftEntity = null;
|
|
33
|
+
this.comparisonResult = null;
|
|
34
|
+
};
|
|
35
|
+
reader.readAsText(file);
|
|
36
|
+
}
|
|
37
|
+
onRightFileSelected(event) {
|
|
38
|
+
const file = event.target.files[0];
|
|
39
|
+
if (!file)
|
|
40
|
+
return;
|
|
41
|
+
this.rightFile = file.name;
|
|
42
|
+
const reader = new FileReader();
|
|
43
|
+
reader.onload = (e) => {
|
|
44
|
+
const xmlContent = e.target.result;
|
|
45
|
+
this.rightEntities = this.comparisonService.parseEntities(xmlContent);
|
|
46
|
+
this.selectedRightEntity = null;
|
|
47
|
+
this.comparisonResult = null;
|
|
48
|
+
};
|
|
49
|
+
reader.readAsText(file);
|
|
50
|
+
}
|
|
51
|
+
selectLeftEntity(entity) {
|
|
52
|
+
this.selectedLeftEntity = entity;
|
|
53
|
+
this.compareIfBothSelected();
|
|
54
|
+
}
|
|
55
|
+
selectRightEntity(entity) {
|
|
56
|
+
this.selectedRightEntity = entity;
|
|
57
|
+
this.compareIfBothSelected();
|
|
58
|
+
}
|
|
59
|
+
compareIfBothSelected() {
|
|
60
|
+
if (this.selectedLeftEntity && this.selectedRightEntity) {
|
|
61
|
+
this.comparisonResult = this.comparisonService.compareStructures(this.selectedLeftEntity.structure, this.selectedRightEntity.structure);
|
|
62
|
+
this.buildDifferencesList();
|
|
63
|
+
this.currentDifferenceIndex = 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
buildDifferencesList() {
|
|
67
|
+
if (!this.comparisonResult) {
|
|
68
|
+
this.differences = [];
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.differences = [
|
|
72
|
+
...this.comparisonResult.leftOnly.map(node => ({ node, type: 'removed' })),
|
|
73
|
+
...this.comparisonResult.rightOnly.map(node => ({ node, type: 'added' })),
|
|
74
|
+
...this.comparisonResult.modified.map(node => ({ node, type: 'modified' }))
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
navigateToNextDifference() {
|
|
78
|
+
if (this.differences.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
this.currentDifferenceIndex = (this.currentDifferenceIndex + 1) % this.differences.length;
|
|
81
|
+
this.scrollToDifference();
|
|
82
|
+
}
|
|
83
|
+
navigateToPreviousDifference() {
|
|
84
|
+
if (this.differences.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
this.currentDifferenceIndex = this.currentDifferenceIndex === 0
|
|
87
|
+
? this.differences.length - 1
|
|
88
|
+
: this.currentDifferenceIndex - 1;
|
|
89
|
+
this.scrollToDifference();
|
|
90
|
+
}
|
|
91
|
+
scrollToDifference() {
|
|
92
|
+
if (this.differences.length === 0)
|
|
93
|
+
return;
|
|
94
|
+
const currentDiff = this.differences[this.currentDifferenceIndex];
|
|
95
|
+
const nodeId = `${currentDiff.node.Id}-${currentDiff.node.name}`;
|
|
96
|
+
// Expand parent nodes FIRST to make the difference visible
|
|
97
|
+
this.expandPathToNode(currentDiff.node);
|
|
98
|
+
// Wait for DOM to update after expansion, then scroll
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
const leftElement = document.querySelector(`[data-node-id="left-${nodeId}"]`);
|
|
101
|
+
const rightElement = document.querySelector(`[data-node-id="right-${nodeId}"]`);
|
|
102
|
+
if (leftElement) {
|
|
103
|
+
leftElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
104
|
+
}
|
|
105
|
+
if (rightElement) {
|
|
106
|
+
rightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
107
|
+
}
|
|
108
|
+
}, 100);
|
|
109
|
+
}
|
|
110
|
+
expandPathToNode(node) {
|
|
111
|
+
// Find and expand all parent nodes in both structures
|
|
112
|
+
this.expandParentsInStructure(this.selectedLeftEntity.structure, node, 'left');
|
|
113
|
+
this.expandParentsInStructure(this.selectedRightEntity.structure, node, 'right');
|
|
114
|
+
}
|
|
115
|
+
expandParentsInStructure(structure, targetNode, side) {
|
|
116
|
+
for (const node of structure) {
|
|
117
|
+
const nodeId = `${side}-${node.Id}-${node.name}`;
|
|
118
|
+
const targetId = `${targetNode.Id}-${targetNode.name}`;
|
|
119
|
+
const currentId = `${node.Id}-${node.name}`;
|
|
120
|
+
// If this is the target node, we found it
|
|
121
|
+
if (currentId === targetId) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
// Check children if they exist
|
|
125
|
+
if (this.hasChildren(node)) {
|
|
126
|
+
const children = this.getChildren(node);
|
|
127
|
+
if (this.expandParentsInStructure(children, targetNode, side)) {
|
|
128
|
+
// If the target is in this branch, expand this node
|
|
129
|
+
this.expandedNodes.add(nodeId);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
get hasDifferences() {
|
|
137
|
+
return this.differences.length > 0;
|
|
138
|
+
}
|
|
139
|
+
get currentDifferenceNumber() {
|
|
140
|
+
return this.currentDifferenceIndex + 1;
|
|
141
|
+
}
|
|
142
|
+
get totalDifferences() {
|
|
143
|
+
return this.differences.length;
|
|
144
|
+
}
|
|
145
|
+
toggleNode(nodeId) {
|
|
146
|
+
if (this.expandedNodes.has(nodeId)) {
|
|
147
|
+
this.expandedNodes.delete(nodeId);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
this.expandedNodes.add(nodeId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
isExpanded(nodeId) {
|
|
154
|
+
return this.expandedNodes.has(nodeId);
|
|
155
|
+
}
|
|
156
|
+
getNodeId(node, prefix) {
|
|
157
|
+
return `${prefix}-${node.Id}-${node.name}`;
|
|
158
|
+
}
|
|
159
|
+
hasChildren(node) {
|
|
160
|
+
return node.Children && JSON.parse(node.Children).length > 0;
|
|
161
|
+
}
|
|
162
|
+
getChildren(node) {
|
|
163
|
+
if (!node.Children)
|
|
164
|
+
return [];
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(node.Children);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
getProperties(node) {
|
|
173
|
+
if (!node.Properties)
|
|
174
|
+
return {};
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(node.Properties);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
getPropertyKeys(node) {
|
|
183
|
+
const props = this.getProperties(node);
|
|
184
|
+
return Object.keys(props);
|
|
185
|
+
}
|
|
186
|
+
getChangeType(node) {
|
|
187
|
+
if (!this.comparisonResult)
|
|
188
|
+
return '';
|
|
189
|
+
const nodeId = `${node.Id}-${node.name}`;
|
|
190
|
+
if (this.comparisonResult.leftOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
191
|
+
return 'removed';
|
|
192
|
+
}
|
|
193
|
+
if (this.comparisonResult.rightOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
194
|
+
return 'added';
|
|
195
|
+
}
|
|
196
|
+
if (this.comparisonResult.modified.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
197
|
+
return 'modified';
|
|
198
|
+
}
|
|
199
|
+
return 'unchanged';
|
|
200
|
+
}
|
|
201
|
+
getModifiedProperties(node) {
|
|
202
|
+
if (!this.comparisonResult)
|
|
203
|
+
return [];
|
|
204
|
+
const nodeId = `${node.Id}-${node.name}`;
|
|
205
|
+
const modified = this.comparisonResult.modified.find(n => `${n.Id}-${n.name}` === nodeId);
|
|
206
|
+
return modified?.modifiedProperties || [];
|
|
207
|
+
}
|
|
208
|
+
toggleFullScreen() {
|
|
209
|
+
this.isFullScreen = !this.isFullScreen;
|
|
210
|
+
}
|
|
211
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonComponent, deps: [{ token: i1.EntityComparisonService }], target: i0.ɵɵFactoryTarget.Component });
|
|
212
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: EntityComparisonComponent, isStandalone: true, selector: "app-entity-comparison", providers: [EntityComparisonService], ngImport: i0, template: "<div class=\"comparison-container\" [class.fullscreen]=\"isFullScreen\">\r\n <header class=\"header\">\r\n <h1>Entity Structure Comparison</h1>\r\n <p class=\"subtitle\">Compare EntityStructureJson between two entity exports</p>\r\n </header>\r\n\r\n <div class=\"file-selection\">\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label left\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onLeftFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Left Entity\r\n </label>\r\n <span *ngIf=\"leftFile\" class=\"file-name\">{{ leftFile }}</span>\r\n </div>\r\n\r\n <div class=\"vs-separator\">VS</div>\r\n\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label right\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onRightFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Right Entity\r\n </label>\r\n <span *ngIf=\"rightFile\" class=\"file-name\">{{ rightFile }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-selection\" *ngIf=\"leftEntities.length > 0 || rightEntities.length > 0\">\r\n <div class=\"entity-list\">\r\n <h3>Left Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of leftEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedLeftEntity === entity\" (click)=\"selectLeftEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-list\">\r\n <h3>Right Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of rightEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedRightEntity === entity\" (click)=\"selectRightEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary-section\" *ngIf=\"comparisonResult\">\r\n <div class=\"summary\">\r\n <div class=\"summary-item added\">\r\n <span class=\"count\">{{ comparisonResult.rightOnly.length }}</span>\r\n <span class=\"label\">Added</span>\r\n </div>\r\n <div class=\"summary-item removed\">\r\n <span class=\"count\">{{ comparisonResult.leftOnly.length }}</span>\r\n <span class=\"label\">Removed</span>\r\n </div>\r\n <div class=\"summary-item modified\">\r\n <span class=\"count\">{{ comparisonResult.modified.length }}</span>\r\n <span class=\"label\">Modified</span>\r\n </div>\r\n <div class=\"summary-item unchanged\">\r\n <span class=\"count\">{{ comparisonResult.unchanged.length }}</span>\r\n <span class=\"label\">Unchanged</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-results\" *ngIf=\"comparisonResult\">\r\n <div class=\"results-header\">\r\n <div class=\"header-title\">\r\n <h2>Comparison Results</h2>\r\n <div class=\"header-actions\">\r\n <div class=\"diff-navigation\" *ngIf=\"hasDifferences\">\r\n <button class=\"nav-btn\" (click)=\"navigateToPreviousDifference()\" title=\"Previous Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"18 15 12 9 6 15\" />\r\n </svg>\r\n </button>\r\n <span class=\"diff-counter\">{{ currentDifferenceNumber }} / {{ totalDifferences }}</span>\r\n <button class=\"nav-btn\" (click)=\"navigateToNextDifference()\" title=\"Next Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n </div>\r\n <button class=\"fullscreen-btn\" (click)=\"toggleFullScreen()\" [title]=\"isFullScreen ? 'Exit Full Screen' : 'Enter Full Screen'\">\r\n <svg *ngIf=\"!isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\r\n </svg>\r\n <svg *ngIf=\"isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"structure-comparison\">\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedLeftEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedLeftEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'left' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedRightEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedRightEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'right' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<ng-template #nodeTemplate let-node let-side=\"side\">\r\n <div class=\"tree-node\" [class]=\"getChangeType(node)\" [attr.data-node-id]=\"getNodeId(node, side)\">\r\n <div class=\"node-header\">\r\n <button *ngIf=\"hasChildren(node)\" class=\"expand-btn\" (click)=\"toggleNode(getNodeId(node, side))\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline *ngIf=\"!isExpanded(getNodeId(node, side))\" points=\"9 18 15 12 9 6\" />\r\n <polyline *ngIf=\"isExpanded(getNodeId(node, side))\" points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n <div *ngIf=\"!hasChildren(node)\" class=\"spacer\"></div>\r\n\r\n <div class=\"node-icon\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path *ngIf=\"node.nodeType === 'Group'\"\r\n d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n <circle *ngIf=\"node.nodeType === 'Attribute'\" cx=\"12\" cy=\"12\" r=\"10\" />\r\n </svg>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <span class=\"node-name\">{{ node.name }}</span>\r\n <span class=\"node-type\">{{ node.nodeType }}</span>\r\n <span class=\"change-badge\" *ngIf=\"getChangeType(node) !== 'unchanged'\">\r\n {{ getChangeType(node) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-properties\" *ngIf=\"getPropertyKeys(node).length > 0\">\r\n <div *ngFor=\"let key of getPropertyKeys(node)\" class=\"property\"\r\n [class.modified]=\"getModifiedProperties(node).includes(key)\">\r\n <span class=\"property-key\">{{ key }}:</span>\r\n <span class=\"property-value\">{{ getProperties(node)[key] }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-children\" *ngIf=\"hasChildren(node) && isExpanded(getNodeId(node, side))\">\r\n <ng-container *ngFor=\"let child of getChildren(node)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: child, side: side }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".comparison-container{display:flex;flex-direction:column;height:100%;width:100%;background:#f9fafb}.header{background:#fff;padding:1.5rem;border-bottom:1px solid #e5e7eb;box-shadow:0 1px 3px #0000001a}.header h1{margin:0;font-size:1.5rem;font-weight:700;color:#1f2937}.subtitle{margin:.5rem 0 0;font-size:.875rem;color:#6b7280}.file-selection{display:flex;align-items:center;gap:2rem;padding:1.5rem;background:#fff;border-bottom:1px solid #e5e7eb}.file-input-group{flex:1;display:flex;flex-direction:column;gap:.5rem}.file-label{display:inline-flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;border:2px solid #2563eb;border-radius:.5rem;cursor:pointer;font-weight:500;transition:all .2s;justify-content:center}.file-label.left{background:#fff;color:#2563eb}.file-label.left:hover{background:#eff6ff}.file-label.right{background:#fff;color:#7c3aed;border-color:#7c3aed}.file-label.right:hover{background:#f5f3ff}.file-name{font-size:.875rem;color:#6b7280;text-align:center}.vs-separator{padding:.75rem 1.5rem;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;font-weight:700;border-radius:2rem;font-size:1.25rem;box-shadow:0 4px 6px #0000001a}.entity-selection{display:flex;gap:1rem;padding:1rem;background:#fff;border-bottom:1px solid #e5e7eb}.entity-list{flex:1}.entity-list h3{margin:0 0 .75rem;font-size:.875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.entities{display:flex;flex-direction:column;gap:.5rem}.entity-card{display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1px solid #e5e7eb;border-radius:.5rem;cursor:pointer;transition:all .2s}.entity-card:hover{background:#f9fafb;border-color:#d1d5db}.entity-card.selected{background:#eff6ff;border-color:#2563eb}.entity-icon{color:#6b7280}.entity-card.selected .entity-icon{color:#2563eb}.entity-info{flex:1}.entity-name{font-size:.875rem;font-weight:500;color:#1f2937}.entity-id{font-size:.75rem;color:#6b7280}.summary-section{padding:1.5rem 1.5rem 0;background:#f9fafb}.comparison-results{flex:1;overflow:auto;padding:1.5rem}.results-header{margin-bottom:1.5rem;position:sticky;top:0;background:#f9fafb;z-index:100;padding-top:1rem;padding-bottom:1rem;margin-top:-1rem}.header-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}.results-header h2{margin:0;font-size:1.25rem;font-weight:700;color:#1f2937}.header-actions{display:flex;align-items:center;gap:1rem}.diff-navigation{display:flex;align-items:center;gap:.5rem;background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.25rem}.nav-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.375rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.nav-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.diff-counter{font-size:.875rem;font-weight:600;color:#1f2937;padding:0 .5rem;white-space:nowrap;min-width:60px;text-align:center}.fullscreen-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.fullscreen-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.summary{display:flex;gap:1rem}.summary-item{flex:1;display:flex;flex-direction:column;align-items:center;padding:1rem;border-radius:.5rem;background:#fff;border:2px solid}.summary-item.added{border-color:#10b981;background:#ecfdf5}.summary-item.removed{border-color:#ef4444;background:#fef2f2}.summary-item.modified{border-color:#f59e0b;background:#fffbeb}.summary-item.unchanged{border-color:#6b7280;background:#f9fafb}.summary-item .count{font-size:1.5rem;font-weight:700}.summary-item.added .count{color:#10b981}.summary-item.removed .count{color:#ef4444}.summary-item.modified .count{color:#f59e0b}.summary-item.unchanged .count{color:#6b7280}.summary-item .label{font-size:.75rem;font-weight:600;text-transform:uppercase;margin-top:.25rem}.structure-comparison{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;height:100%}.comparison-section{background:#fff;border-radius:.5rem;padding:1rem;border:1px solid #e5e7eb;min-width:0;width:100%;overflow:auto}.comparison-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1f2937;padding-bottom:.75rem;border-bottom:1px solid #e5e7eb}.structure-tree{display:flex;flex-direction:column;gap:.5rem}.tree-node{border-left:3px solid transparent;padding-left:.5rem;transition:all .2s;max-width:100%;overflow:hidden}.tree-node.added{background:#ecfdf5;border-left-color:#10b981}.tree-node.removed{background:#fef2f2;border-left-color:#ef4444}.tree-node.modified{background:#fffbeb;border-left-color:#f59e0b}.node-header{display:flex;align-items:center;gap:.5rem;padding:.5rem;border-radius:.375rem}.expand-btn{background:none;border:none;cursor:pointer;padding:.25rem;display:flex;align-items:center;justify-content:center;border-radius:.25rem;transition:background .2s}.expand-btn:hover{background:#f3f4f6}.spacer{width:24px}.node-icon{color:#6b7280}.node-content{display:flex;align-items:center;gap:.5rem;flex:1}.node-name{font-size:.875rem;font-weight:500;color:#1f2937}.node-type{font-size:.75rem;color:#6b7280;background:#f3f4f6;padding:.125rem .5rem;border-radius:.25rem}.change-badge{font-size:.625rem;font-weight:600;text-transform:uppercase;padding:.125rem .5rem;border-radius:.25rem;margin-left:auto}.tree-node.added .change-badge{background:#10b981;color:#fff}.tree-node.removed .change-badge{background:#ef4444;color:#fff}.tree-node.modified .change-badge{background:#f59e0b;color:#fff}.node-properties{margin-left:2.5rem;margin-top:.5rem;padding:.5rem;background:#f9fafb;border-radius:.375rem;font-size:.75rem;max-width:100%;overflow:hidden}.property{display:flex;flex-wrap:wrap;gap:.5rem;padding:.25rem 0;max-width:100%;overflow:hidden}.property.modified{background:#fef3c7;padding:.25rem .5rem;border-radius:.25rem;margin:.125rem 0}.property-key{font-weight:600;color:#374151;flex-shrink:0}.property-value{color:#6b7280;word-break:break-word;overflow:auto;overflow-wrap:break-word;flex:1;min-width:0}.node-children{margin-left:1.5rem;margin-top:.5rem;display:flex;flex-direction:column;gap:.5rem}.comparison-container.fullscreen{position:fixed;inset:0;z-index:9999;background:#f9fafb}.comparison-container.fullscreen .header,.comparison-container.fullscreen .file-selection,.comparison-container.fullscreen .entity-selection,.comparison-container.fullscreen .summary-section{display:none}.comparison-container.fullscreen .comparison-results{height:100vh;padding:2rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
213
|
+
}
|
|
214
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonComponent, decorators: [{
|
|
215
|
+
type: Component,
|
|
216
|
+
args: [{ selector: 'app-entity-comparison', standalone: true, imports: [CommonModule], providers: [EntityComparisonService], template: "<div class=\"comparison-container\" [class.fullscreen]=\"isFullScreen\">\r\n <header class=\"header\">\r\n <h1>Entity Structure Comparison</h1>\r\n <p class=\"subtitle\">Compare EntityStructureJson between two entity exports</p>\r\n </header>\r\n\r\n <div class=\"file-selection\">\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label left\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onLeftFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Left Entity\r\n </label>\r\n <span *ngIf=\"leftFile\" class=\"file-name\">{{ leftFile }}</span>\r\n </div>\r\n\r\n <div class=\"vs-separator\">VS</div>\r\n\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label right\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onRightFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Right Entity\r\n </label>\r\n <span *ngIf=\"rightFile\" class=\"file-name\">{{ rightFile }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-selection\" *ngIf=\"leftEntities.length > 0 || rightEntities.length > 0\">\r\n <div class=\"entity-list\">\r\n <h3>Left Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of leftEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedLeftEntity === entity\" (click)=\"selectLeftEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-list\">\r\n <h3>Right Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of rightEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedRightEntity === entity\" (click)=\"selectRightEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary-section\" *ngIf=\"comparisonResult\">\r\n <div class=\"summary\">\r\n <div class=\"summary-item added\">\r\n <span class=\"count\">{{ comparisonResult.rightOnly.length }}</span>\r\n <span class=\"label\">Added</span>\r\n </div>\r\n <div class=\"summary-item removed\">\r\n <span class=\"count\">{{ comparisonResult.leftOnly.length }}</span>\r\n <span class=\"label\">Removed</span>\r\n </div>\r\n <div class=\"summary-item modified\">\r\n <span class=\"count\">{{ comparisonResult.modified.length }}</span>\r\n <span class=\"label\">Modified</span>\r\n </div>\r\n <div class=\"summary-item unchanged\">\r\n <span class=\"count\">{{ comparisonResult.unchanged.length }}</span>\r\n <span class=\"label\">Unchanged</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-results\" *ngIf=\"comparisonResult\">\r\n <div class=\"results-header\">\r\n <div class=\"header-title\">\r\n <h2>Comparison Results</h2>\r\n <div class=\"header-actions\">\r\n <div class=\"diff-navigation\" *ngIf=\"hasDifferences\">\r\n <button class=\"nav-btn\" (click)=\"navigateToPreviousDifference()\" title=\"Previous Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"18 15 12 9 6 15\" />\r\n </svg>\r\n </button>\r\n <span class=\"diff-counter\">{{ currentDifferenceNumber }} / {{ totalDifferences }}</span>\r\n <button class=\"nav-btn\" (click)=\"navigateToNextDifference()\" title=\"Next Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n </div>\r\n <button class=\"fullscreen-btn\" (click)=\"toggleFullScreen()\" [title]=\"isFullScreen ? 'Exit Full Screen' : 'Enter Full Screen'\">\r\n <svg *ngIf=\"!isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\r\n </svg>\r\n <svg *ngIf=\"isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"structure-comparison\">\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedLeftEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedLeftEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'left' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedRightEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedRightEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'right' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<ng-template #nodeTemplate let-node let-side=\"side\">\r\n <div class=\"tree-node\" [class]=\"getChangeType(node)\" [attr.data-node-id]=\"getNodeId(node, side)\">\r\n <div class=\"node-header\">\r\n <button *ngIf=\"hasChildren(node)\" class=\"expand-btn\" (click)=\"toggleNode(getNodeId(node, side))\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline *ngIf=\"!isExpanded(getNodeId(node, side))\" points=\"9 18 15 12 9 6\" />\r\n <polyline *ngIf=\"isExpanded(getNodeId(node, side))\" points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n <div *ngIf=\"!hasChildren(node)\" class=\"spacer\"></div>\r\n\r\n <div class=\"node-icon\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path *ngIf=\"node.nodeType === 'Group'\"\r\n d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n <circle *ngIf=\"node.nodeType === 'Attribute'\" cx=\"12\" cy=\"12\" r=\"10\" />\r\n </svg>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <span class=\"node-name\">{{ node.name }}</span>\r\n <span class=\"node-type\">{{ node.nodeType }}</span>\r\n <span class=\"change-badge\" *ngIf=\"getChangeType(node) !== 'unchanged'\">\r\n {{ getChangeType(node) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-properties\" *ngIf=\"getPropertyKeys(node).length > 0\">\r\n <div *ngFor=\"let key of getPropertyKeys(node)\" class=\"property\"\r\n [class.modified]=\"getModifiedProperties(node).includes(key)\">\r\n <span class=\"property-key\">{{ key }}:</span>\r\n <span class=\"property-value\">{{ getProperties(node)[key] }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-children\" *ngIf=\"hasChildren(node) && isExpanded(getNodeId(node, side))\">\r\n <ng-container *ngFor=\"let child of getChildren(node)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: child, side: side }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".comparison-container{display:flex;flex-direction:column;height:100%;width:100%;background:#f9fafb}.header{background:#fff;padding:1.5rem;border-bottom:1px solid #e5e7eb;box-shadow:0 1px 3px #0000001a}.header h1{margin:0;font-size:1.5rem;font-weight:700;color:#1f2937}.subtitle{margin:.5rem 0 0;font-size:.875rem;color:#6b7280}.file-selection{display:flex;align-items:center;gap:2rem;padding:1.5rem;background:#fff;border-bottom:1px solid #e5e7eb}.file-input-group{flex:1;display:flex;flex-direction:column;gap:.5rem}.file-label{display:inline-flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;border:2px solid #2563eb;border-radius:.5rem;cursor:pointer;font-weight:500;transition:all .2s;justify-content:center}.file-label.left{background:#fff;color:#2563eb}.file-label.left:hover{background:#eff6ff}.file-label.right{background:#fff;color:#7c3aed;border-color:#7c3aed}.file-label.right:hover{background:#f5f3ff}.file-name{font-size:.875rem;color:#6b7280;text-align:center}.vs-separator{padding:.75rem 1.5rem;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;font-weight:700;border-radius:2rem;font-size:1.25rem;box-shadow:0 4px 6px #0000001a}.entity-selection{display:flex;gap:1rem;padding:1rem;background:#fff;border-bottom:1px solid #e5e7eb}.entity-list{flex:1}.entity-list h3{margin:0 0 .75rem;font-size:.875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.entities{display:flex;flex-direction:column;gap:.5rem}.entity-card{display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1px solid #e5e7eb;border-radius:.5rem;cursor:pointer;transition:all .2s}.entity-card:hover{background:#f9fafb;border-color:#d1d5db}.entity-card.selected{background:#eff6ff;border-color:#2563eb}.entity-icon{color:#6b7280}.entity-card.selected .entity-icon{color:#2563eb}.entity-info{flex:1}.entity-name{font-size:.875rem;font-weight:500;color:#1f2937}.entity-id{font-size:.75rem;color:#6b7280}.summary-section{padding:1.5rem 1.5rem 0;background:#f9fafb}.comparison-results{flex:1;overflow:auto;padding:1.5rem}.results-header{margin-bottom:1.5rem;position:sticky;top:0;background:#f9fafb;z-index:100;padding-top:1rem;padding-bottom:1rem;margin-top:-1rem}.header-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}.results-header h2{margin:0;font-size:1.25rem;font-weight:700;color:#1f2937}.header-actions{display:flex;align-items:center;gap:1rem}.diff-navigation{display:flex;align-items:center;gap:.5rem;background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.25rem}.nav-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.375rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.nav-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.diff-counter{font-size:.875rem;font-weight:600;color:#1f2937;padding:0 .5rem;white-space:nowrap;min-width:60px;text-align:center}.fullscreen-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.fullscreen-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.summary{display:flex;gap:1rem}.summary-item{flex:1;display:flex;flex-direction:column;align-items:center;padding:1rem;border-radius:.5rem;background:#fff;border:2px solid}.summary-item.added{border-color:#10b981;background:#ecfdf5}.summary-item.removed{border-color:#ef4444;background:#fef2f2}.summary-item.modified{border-color:#f59e0b;background:#fffbeb}.summary-item.unchanged{border-color:#6b7280;background:#f9fafb}.summary-item .count{font-size:1.5rem;font-weight:700}.summary-item.added .count{color:#10b981}.summary-item.removed .count{color:#ef4444}.summary-item.modified .count{color:#f59e0b}.summary-item.unchanged .count{color:#6b7280}.summary-item .label{font-size:.75rem;font-weight:600;text-transform:uppercase;margin-top:.25rem}.structure-comparison{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;height:100%}.comparison-section{background:#fff;border-radius:.5rem;padding:1rem;border:1px solid #e5e7eb;min-width:0;width:100%;overflow:auto}.comparison-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1f2937;padding-bottom:.75rem;border-bottom:1px solid #e5e7eb}.structure-tree{display:flex;flex-direction:column;gap:.5rem}.tree-node{border-left:3px solid transparent;padding-left:.5rem;transition:all .2s;max-width:100%;overflow:hidden}.tree-node.added{background:#ecfdf5;border-left-color:#10b981}.tree-node.removed{background:#fef2f2;border-left-color:#ef4444}.tree-node.modified{background:#fffbeb;border-left-color:#f59e0b}.node-header{display:flex;align-items:center;gap:.5rem;padding:.5rem;border-radius:.375rem}.expand-btn{background:none;border:none;cursor:pointer;padding:.25rem;display:flex;align-items:center;justify-content:center;border-radius:.25rem;transition:background .2s}.expand-btn:hover{background:#f3f4f6}.spacer{width:24px}.node-icon{color:#6b7280}.node-content{display:flex;align-items:center;gap:.5rem;flex:1}.node-name{font-size:.875rem;font-weight:500;color:#1f2937}.node-type{font-size:.75rem;color:#6b7280;background:#f3f4f6;padding:.125rem .5rem;border-radius:.25rem}.change-badge{font-size:.625rem;font-weight:600;text-transform:uppercase;padding:.125rem .5rem;border-radius:.25rem;margin-left:auto}.tree-node.added .change-badge{background:#10b981;color:#fff}.tree-node.removed .change-badge{background:#ef4444;color:#fff}.tree-node.modified .change-badge{background:#f59e0b;color:#fff}.node-properties{margin-left:2.5rem;margin-top:.5rem;padding:.5rem;background:#f9fafb;border-radius:.375rem;font-size:.75rem;max-width:100%;overflow:hidden}.property{display:flex;flex-wrap:wrap;gap:.5rem;padding:.25rem 0;max-width:100%;overflow:hidden}.property.modified{background:#fef3c7;padding:.25rem .5rem;border-radius:.25rem;margin:.125rem 0}.property-key{font-weight:600;color:#374151;flex-shrink:0}.property-value{color:#6b7280;word-break:break-word;overflow:auto;overflow-wrap:break-word;flex:1;min-width:0}.node-children{margin-left:1.5rem;margin-top:.5rem;display:flex;flex-direction:column;gap:.5rem}.comparison-container.fullscreen{position:fixed;inset:0;z-index:9999;background:#f9fafb}.comparison-container.fullscreen .header,.comparison-container.fullscreen .file-selection,.comparison-container.fullscreen .entity-selection,.comparison-container.fullscreen .summary-section{display:none}.comparison-container.fullscreen .comparison-results{height:100vh;padding:2rem}\n"] }]
|
|
217
|
+
}], ctorParameters: () => [{ type: i1.EntityComparisonService }] });
|
|
218
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWNvbXBhcmlzb24uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvY29uY2VwdG8tdXNlci1jb250cm9scy9zcmMvbGliL2VudGl0eS1jb21wYXJpc29uL2NvbXBvbmVudHMvZW50aXR5LWNvbXBhcmlzb24uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvY29uY2VwdG8tdXNlci1jb250cm9scy9zcmMvbGliL2VudGl0eS1jb21wYXJpc29uL2NvbXBvbmVudHMvZW50aXR5LWNvbXBhcmlzb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMxQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sNENBQTRDLENBQUM7Ozs7QUFpQnJGLE1BQU0sT0FBTyx5QkFBeUI7SUFhaEI7SUFacEIsUUFBUSxHQUFXLEVBQUUsQ0FBQztJQUN0QixTQUFTLEdBQVcsRUFBRSxDQUFDO0lBQ3ZCLFlBQVksR0FBVSxFQUFFLENBQUM7SUFDekIsYUFBYSxHQUFVLEVBQUUsQ0FBQztJQUMxQixrQkFBa0IsR0FBUSxJQUFJLENBQUM7SUFDL0IsbUJBQW1CLEdBQVEsSUFBSSxDQUFDO0lBQ2hDLGdCQUFnQixHQUE0QixJQUFJLENBQUM7SUFDakQsYUFBYSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDbEMsWUFBWSxHQUFHLEtBQUssQ0FBQztJQUNyQixzQkFBc0IsR0FBRyxDQUFDLENBQUM7SUFDM0IsV0FBVyxHQUFVLEVBQUUsQ0FBQztJQUV4QixZQUFvQixpQkFBMEM7UUFBMUMsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUF5QjtJQUFHLENBQUM7SUFFbEUsa0JBQWtCLENBQUMsS0FBVTtRQUMz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QztRQUV0QyxNQUFNLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3pDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxNQUFNLENBQUMsQ0FBQztRQUUxRixPQUFPLFFBQVEsRUFBRSxrQkFBa0IsSUFBSSxFQUFFLENBQUM7SUFDNUMsQ0FBQztJQUVELGdCQUFnQjtRQUNkLElBQUksQ0FBQyxZQUFZLEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDO0lBQ3pDLENBQUM7d0dBNU9VLHlCQUF5Qjs0RkFBekIseUJBQXlCLG9FQUp6QixDQUFDLHVCQUF1QixDQUFDLDBCQ2Z0QyxxNFdBOE1jLGkvTURoTUYsWUFBWTs7NEZBS1gseUJBQXlCO2tCQVJyQyxTQUFTOytCQUNFLHVCQUF1QixjQUNyQixJQUFJLFdBQ1AsQ0FBQyxZQUFZLENBQUMsYUFDWixDQUFDLHVCQUF1QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IEVudGl0eUNvbXBhcmlzb25TZXJ2aWNlIH0gZnJvbSAnLi4vY29yZS9zZXJ2aWNlcy9lbnRpdHktY29tcGFyaXNvbi5zZXJ2aWNlJztcclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgQ29tcGFyaXNvblJlc3VsdCB7XHJcbiAgbGVmdE9ubHk6IGFueVtdO1xyXG4gIHJpZ2h0T25seTogYW55W107XHJcbiAgbW9kaWZpZWQ6IGFueVtdO1xyXG4gIHVuY2hhbmdlZDogYW55W107XHJcbn1cclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnYXBwLWVudGl0eS1jb21wYXJpc29uJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdLFxyXG4gIHByb3ZpZGVyczogW0VudGl0eUNvbXBhcmlzb25TZXJ2aWNlXSxcclxuICB0ZW1wbGF0ZVVybDogJy4vZW50aXR5LWNvbXBhcmlzb24uY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2VudGl0eS1jb21wYXJpc29uLmNvbXBvbmVudC5jc3MnXVxyXG59KVxyXG5leHBvcnQgY2xhc3MgRW50aXR5Q29tcGFyaXNvbkNvbXBvbmVudCB7XHJcbiAgbGVmdEZpbGU6IHN0cmluZyA9ICcnO1xyXG4gIHJpZ2h0RmlsZTogc3RyaW5nID0gJyc7XHJcbiAgbGVmdEVudGl0aWVzOiBhbnlbXSA9IFtdO1xyXG4gIHJpZ2h0RW50aXRpZXM6IGFueVtdID0gW107XHJcbiAgc2VsZWN0ZWRMZWZ0RW50aXR5OiBhbnkgPSBudWxsO1xyXG4gIHNlbGVjdGVkUmlnaHRFbnRpdHk6IGFueSA9IG51bGw7XHJcbiAgY29tcGFyaXNvblJlc3VsdDogQ29tcGFyaXNvblJlc3VsdCB8IG51bGwgPSBudWxsO1xyXG4gIGV4cGFuZGVkTm9kZXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcclxuICBpc0Z1bGxTY3JlZW4gPSBmYWxzZTtcclxuICBjdXJyZW50RGlmZmVyZW5jZUluZGV4ID0gMDtcclxuICBkaWZmZXJlbmNlczogYW55W10gPSBbXTtcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjb21wYXJpc29uU2VydmljZTogRW50aXR5Q29tcGFyaXNvblNlcnZpY2UpIHt9XHJcblxyXG4gIG9uTGVmdEZpbGVTZWxlY3RlZChldmVudDogYW55KTogdm9pZCB7XHJcbiAgICBjb25zdCBmaWxlOiBGaWxlID0gZXZlbnQudGFyZ2V0LmZpbGVzWzBdO1xyXG4gICAgaWYgKCFmaWxlKSByZXR1cm47XHJcblxyXG4gICAgdGhpcy5sZWZ0RmlsZSA9IGZpbGUubmFtZTtcclxuICAgIGNvbnN0IHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7XHJcbiAgICBcclxuICAgIHJlYWRlci5vbmxvYWQgPSAoZTogYW55KSA9PiB7XHJcbiAgICAgIGNvbnN0IHhtbENvbnRlbnQgPSBlLnRhcmdldC5yZXN1bHQ7XHJcbiAgICAgIHRoaXMubGVmdEVudGl0aWVzID0gdGhpcy5jb21wYXJpc29uU2VydmljZS5wYXJzZUVudGl0aWVzKHhtbENvbnRlbnQpO1xyXG4gICAgICB0aGlzLnNlbGVjdGVkTGVmdEVudGl0eSA9IG51bGw7XHJcbiAgICAgIHRoaXMuY29tcGFyaXNvblJlc3VsdCA9IG51bGw7XHJcbiAgICB9O1xyXG4gICAgXHJcbiAgICByZWFkZXIucmVhZEFzVGV4dChmaWxlKTtcclxuICB9XHJcblxyXG4gIG9uUmlnaHRGaWxlU2VsZWN0ZWQoZXZlbnQ6IGFueSk6IHZvaWQge1xyXG4gICAgY29uc3QgZmlsZTogRmlsZSA9IGV2ZW50LnRhcmdldC5maWxlc1swXTtcclxuICAgIGlmICghZmlsZSkgcmV0dXJuO1xyXG5cclxuICAgIHRoaXMucmlnaHRGaWxlID0gZmlsZS5uYW1lO1xyXG4gICAgY29uc3QgcmVhZGVyID0gbmV3IEZpbGVSZWFkZXIoKTtcclxuICAgIFxyXG4gICAgcmVhZGVyLm9ubG9hZCA9IChlOiBhbnkpID0+IHtcclxuICAgICAgY29uc3QgeG1sQ29udGVudCA9IGUudGFyZ2V0LnJlc3VsdDtcclxuICAgICAgdGhpcy5yaWdodEVudGl0aWVzID0gdGhpcy5jb21wYXJpc29uU2VydmljZS5wYXJzZUVudGl0aWVzKHhtbENvbnRlbnQpO1xyXG4gICAgICB0aGlzLnNlbGVjdGVkUmlnaHRFbnRpdHkgPSBudWxsO1xyXG4gICAgICB0aGlzLmNvbXBhcmlzb25SZXN1bHQgPSBudWxsO1xyXG4gICAgfTtcclxuICAgIFxyXG4gICAgcmVhZGVyLnJlYWRBc1RleHQoZmlsZSk7XHJcbiAgfVxyXG5cclxuICBzZWxlY3RMZWZ0RW50aXR5KGVudGl0eTogYW55KTogdm9pZCB7XHJcbiAgICB0aGlzLnNlbGVjdGVkTGVmdEVudGl0eSA9IGVudGl0eTtcclxuICAgIHRoaXMuY29tcGFyZUlmQm90aFNlbGVjdGVkKCk7XHJcbiAgfVxyXG5cclxuICBzZWxlY3RSaWdodEVudGl0eShlbnRpdHk6IGFueSk6IHZvaWQge1xyXG4gICAgdGhpcy5zZWxlY3RlZFJpZ2h0RW50aXR5ID0gZW50aXR5O1xyXG4gICAgdGhpcy5jb21wYXJlSWZCb3RoU2VsZWN0ZWQoKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgY29tcGFyZUlmQm90aFNlbGVjdGVkKCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRMZWZ0RW50aXR5ICYmIHRoaXMuc2VsZWN0ZWRSaWdodEVudGl0eSkge1xyXG4gICAgICB0aGlzLmNvbXBhcmlzb25SZXN1bHQgPSB0aGlzLmNvbXBhcmlzb25TZXJ2aWNlLmNvbXBhcmVTdHJ1Y3R1cmVzKFxyXG4gICAgICAgIHRoaXMuc2VsZWN0ZWRMZWZ0RW50aXR5LnN0cnVjdHVyZSxcclxuICAgICAgICB0aGlzLnNlbGVjdGVkUmlnaHRFbnRpdHkuc3RydWN0dXJlXHJcbiAgICAgICk7XHJcbiAgICAgIHRoaXMuYnVpbGREaWZmZXJlbmNlc0xpc3QoKTtcclxuICAgICAgdGhpcy5jdXJyZW50RGlmZmVyZW5jZUluZGV4ID0gMDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgYnVpbGREaWZmZXJlbmNlc0xpc3QoKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMuY29tcGFyaXNvblJlc3VsdCkge1xyXG4gICAgICB0aGlzLmRpZmZlcmVuY2VzID0gW107XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLmRpZmZlcmVuY2VzID0gW1xyXG4gICAgICAuLi50aGlzLmNvbXBhcmlzb25SZXN1bHQubGVmdE9ubHkubWFwKG5vZGUgPT4gKHsgbm9kZSwgdHlwZTogJ3JlbW92ZWQnIH0pKSxcclxuICAgICAgLi4udGhpcy5jb21wYXJpc29uUmVzdWx0LnJpZ2h0T25seS5tYXAobm9kZSA9PiAoeyBub2RlLCB0eXBlOiAnYWRkZWQnIH0pKSxcclxuICAgICAgLi4udGhpcy5jb21wYXJpc29uUmVzdWx0Lm1vZGlmaWVkLm1hcChub2RlID0+ICh7IG5vZGUsIHR5cGU6ICdtb2RpZmllZCcgfSkpXHJcbiAgICBdO1xyXG4gIH1cclxuXHJcbiAgbmF2aWdhdGVUb05leHREaWZmZXJlbmNlKCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuZGlmZmVyZW5jZXMubGVuZ3RoID09PSAwKSByZXR1cm47XHJcblxyXG4gICAgdGhpcy5jdXJyZW50RGlmZmVyZW5jZUluZGV4ID0gKHRoaXMuY3VycmVudERpZmZlcmVuY2VJbmRleCArIDEpICUgdGhpcy5kaWZmZXJlbmNlcy5sZW5ndGg7XHJcbiAgICB0aGlzLnNjcm9sbFRvRGlmZmVyZW5jZSgpO1xyXG4gIH1cclxuXHJcbiAgbmF2aWdhdGVUb1ByZXZpb3VzRGlmZmVyZW5jZSgpOiB2b2lkIHtcclxuICAgIGlmICh0aGlzLmRpZmZlcmVuY2VzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xyXG5cclxuICAgIHRoaXMuY3VycmVudERpZmZlcmVuY2VJbmRleCA9IHRoaXMuY3VycmVudERpZmZlcmVuY2VJbmRleCA9PT0gMFxyXG4gICAgICA/IHRoaXMuZGlmZmVyZW5jZXMubGVuZ3RoIC0gMVxyXG4gICAgICA6IHRoaXMuY3VycmVudERpZmZlcmVuY2VJbmRleCAtIDE7XHJcbiAgICB0aGlzLnNjcm9sbFRvRGlmZmVyZW5jZSgpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBzY3JvbGxUb0RpZmZlcmVuY2UoKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5kaWZmZXJlbmNlcy5sZW5ndGggPT09IDApIHJldHVybjtcclxuXHJcbiAgICBjb25zdCBjdXJyZW50RGlmZiA9IHRoaXMuZGlmZmVyZW5jZXNbdGhpcy5jdXJyZW50RGlmZmVyZW5jZUluZGV4XTtcclxuICAgIGNvbnN0IG5vZGVJZCA9IGAke2N1cnJlbnREaWZmLm5vZGUuSWR9LSR7Y3VycmVudERpZmYubm9kZS5uYW1lfWA7XHJcblxyXG4gICAgLy8gRXhwYW5kIHBhcmVudCBub2RlcyBGSVJTVCB0byBtYWtlIHRoZSBkaWZmZXJlbmNlIHZpc2libGVcclxuICAgIHRoaXMuZXhwYW5kUGF0aFRvTm9kZShjdXJyZW50RGlmZi5ub2RlKTtcclxuXHJcbiAgICAvLyBXYWl0IGZvciBET00gdG8gdXBkYXRlIGFmdGVyIGV4cGFuc2lvbiwgdGhlbiBzY3JvbGxcclxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICBjb25zdCBsZWZ0RWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYFtkYXRhLW5vZGUtaWQ9XCJsZWZ0LSR7bm9kZUlkfVwiXWApO1xyXG4gICAgICBjb25zdCByaWdodEVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBbZGF0YS1ub2RlLWlkPVwicmlnaHQtJHtub2RlSWR9XCJdYCk7XHJcblxyXG4gICAgICBpZiAobGVmdEVsZW1lbnQpIHtcclxuICAgICAgICBsZWZ0RWxlbWVudC5zY3JvbGxJbnRvVmlldyh7IGJlaGF2aW9yOiAnc21vb3RoJywgYmxvY2s6ICdjZW50ZXInIH0pO1xyXG4gICAgICB9XHJcbiAgICAgIGlmIChyaWdodEVsZW1lbnQpIHtcclxuICAgICAgICByaWdodEVsZW1lbnQuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvcjogJ3Ntb290aCcsIGJsb2NrOiAnY2VudGVyJyB9KTtcclxuICAgICAgfVxyXG4gICAgfSwgMTAwKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgZXhwYW5kUGF0aFRvTm9kZShub2RlOiBhbnkpOiB2b2lkIHtcclxuICAgIC8vIEZpbmQgYW5kIGV4cGFuZCBhbGwgcGFyZW50IG5vZGVzIGluIGJvdGggc3RydWN0dXJlc1xyXG4gICAgdGhpcy5leHBhbmRQYXJlbnRzSW5TdHJ1Y3R1cmUodGhpcy5zZWxlY3RlZExlZnRFbnRpdHkuc3RydWN0dXJlLCBub2RlLCAnbGVmdCcpO1xyXG4gICAgdGhpcy5leHBhbmRQYXJlbnRzSW5TdHJ1Y3R1cmUodGhpcy5zZWxlY3RlZFJpZ2h0RW50aXR5LnN0cnVjdHVyZSwgbm9kZSwgJ3JpZ2h0Jyk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGV4cGFuZFBhcmVudHNJblN0cnVjdHVyZShzdHJ1Y3R1cmU6IGFueVtdLCB0YXJnZXROb2RlOiBhbnksIHNpZGU6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgZm9yIChjb25zdCBub2RlIG9mIHN0cnVjdHVyZSkge1xyXG4gICAgICBjb25zdCBub2RlSWQgPSBgJHtzaWRlfS0ke25vZGUuSWR9LSR7bm9kZS5uYW1lfWA7XHJcbiAgICAgIGNvbnN0IHRhcmdldElkID0gYCR7dGFyZ2V0Tm9kZS5JZH0tJHt0YXJnZXROb2RlLm5hbWV9YDtcclxuICAgICAgY29uc3QgY3VycmVudElkID0gYCR7bm9kZS5JZH0tJHtub2RlLm5hbWV9YDtcclxuXHJcbiAgICAgIC8vIElmIHRoaXMgaXMgdGhlIHRhcmdldCBub2RlLCB3ZSBmb3VuZCBpdFxyXG4gICAgICBpZiAoY3VycmVudElkID09PSB0YXJnZXRJZCkge1xyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBjaGlsZHJlbiBpZiB0aGV5IGV4aXN0XHJcbiAgICAgIGlmICh0aGlzLmhhc0NoaWxkcmVuKG5vZGUpKSB7XHJcbiAgICAgICAgY29uc3QgY2hpbGRyZW4gPSB0aGlzLmdldENoaWxkcmVuKG5vZGUpO1xyXG4gICAgICAgIGlmICh0aGlzLmV4cGFuZFBhcmVudHNJblN0cnVjdHVyZShjaGlsZHJlbiwgdGFyZ2V0Tm9kZSwgc2lkZSkpIHtcclxuICAgICAgICAgIC8vIElmIHRoZSB0YXJnZXQgaXMgaW4gdGhpcyBicmFuY2gsIGV4cGFuZCB0aGlzIG5vZGVcclxuICAgICAgICAgIHRoaXMuZXhwYW5kZWROb2Rlcy5hZGQobm9kZUlkKTtcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBmYWxzZTtcclxuICB9XHJcblxyXG4gIGdldCBoYXNEaWZmZXJlbmNlcygpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLmRpZmZlcmVuY2VzLmxlbmd0aCA+IDA7XHJcbiAgfVxyXG5cclxuICBnZXQgY3VycmVudERpZmZlcmVuY2VOdW1iZXIoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLmN1cnJlbnREaWZmZXJlbmNlSW5kZXggKyAxO1xyXG4gIH1cclxuXHJcbiAgZ2V0IHRvdGFsRGlmZmVyZW5jZXMoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLmRpZmZlcmVuY2VzLmxlbmd0aDtcclxuICB9XHJcblxyXG4gIHRvZ2dsZU5vZGUobm9kZUlkOiBzdHJpbmcpOiB2b2lkIHtcclxuICAgIGlmICh0aGlzLmV4cGFuZGVkTm9kZXMuaGFzKG5vZGVJZCkpIHtcclxuICAgICAgdGhpcy5leHBhbmRlZE5vZGVzLmRlbGV0ZShub2RlSWQpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5leHBhbmRlZE5vZGVzLmFkZChub2RlSWQpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgaXNFeHBhbmRlZChub2RlSWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHRoaXMuZXhwYW5kZWROb2Rlcy5oYXMobm9kZUlkKTtcclxuICB9XHJcblxyXG4gIGdldE5vZGVJZChub2RlOiBhbnksIHByZWZpeDogc3RyaW5nKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBgJHtwcmVmaXh9LSR7bm9kZS5JZH0tJHtub2RlLm5hbWV9YDtcclxuICB9XHJcblxyXG4gIGhhc0NoaWxkcmVuKG5vZGU6IGFueSk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIG5vZGUuQ2hpbGRyZW4gJiYgSlNPTi5wYXJzZShub2RlLkNoaWxkcmVuKS5sZW5ndGggPiAwO1xyXG4gIH1cclxuXHJcbiAgZ2V0Q2hpbGRyZW4obm9kZTogYW55KTogYW55W10ge1xyXG4gICAgaWYgKCFub2RlLkNoaWxkcmVuKSByZXR1cm4gW107XHJcbiAgICB0cnkge1xyXG4gICAgICByZXR1cm4gSlNPTi5wYXJzZShub2RlLkNoaWxkcmVuKTtcclxuICAgIH0gY2F0Y2gge1xyXG4gICAgICByZXR1cm4gW107XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBnZXRQcm9wZXJ0aWVzKG5vZGU6IGFueSk6IGFueSB7XHJcbiAgICBpZiAoIW5vZGUuUHJvcGVydGllcykgcmV0dXJuIHt9O1xyXG4gICAgdHJ5IHtcclxuICAgICAgcmV0dXJuIEpTT04ucGFyc2Uobm9kZS5Qcm9wZXJ0aWVzKTtcclxuICAgIH0gY2F0Y2gge1xyXG4gICAgICByZXR1cm4ge307XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBnZXRQcm9wZXJ0eUtleXMobm9kZTogYW55KTogc3RyaW5nW10ge1xyXG4gICAgY29uc3QgcHJvcHMgPSB0aGlzLmdldFByb3BlcnRpZXMobm9kZSk7XHJcbiAgICByZXR1cm4gT2JqZWN0LmtleXMocHJvcHMpO1xyXG4gIH1cclxuXHJcbiAgZ2V0Q2hhbmdlVHlwZShub2RlOiBhbnkpOiBzdHJpbmcge1xyXG4gICAgaWYgKCF0aGlzLmNvbXBhcmlzb25SZXN1bHQpIHJldHVybiAnJztcclxuICAgIFxyXG4gICAgY29uc3Qgbm9kZUlkID0gYCR7bm9kZS5JZH0tJHtub2RlLm5hbWV9YDtcclxuICAgIFxyXG4gICAgaWYgKHRoaXMuY29tcGFyaXNvblJlc3VsdC5sZWZ0T25seS5zb21lKG4gPT4gYCR7bi5JZH0tJHtuLm5hbWV9YCA9PT0gbm9kZUlkKSkge1xyXG4gICAgICByZXR1cm4gJ3JlbW92ZWQnO1xyXG4gICAgfVxyXG4gICAgaWYgKHRoaXMuY29tcGFyaXNvblJlc3VsdC5yaWdodE9ubHkuc29tZShuID0+IGAke24uSWR9LSR7bi5uYW1lfWAgPT09IG5vZGVJZCkpIHtcclxuICAgICAgcmV0dXJuICdhZGRlZCc7XHJcbiAgICB9XHJcbiAgICBpZiAodGhpcy5jb21wYXJpc29uUmVzdWx0Lm1vZGlmaWVkLnNvbWUobiA9PiBgJHtuLklkfS0ke24ubmFtZX1gID09PSBub2RlSWQpKSB7XHJcbiAgICAgIHJldHVybiAnbW9kaWZpZWQnO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuICd1bmNoYW5nZWQnO1xyXG4gIH1cclxuXHJcbiAgZ2V0TW9kaWZpZWRQcm9wZXJ0aWVzKG5vZGU6IGFueSk6IHN0cmluZ1tdIHtcclxuICAgIGlmICghdGhpcy5jb21wYXJpc29uUmVzdWx0KSByZXR1cm4gW107XHJcblxyXG4gICAgY29uc3Qgbm9kZUlkID0gYCR7bm9kZS5JZH0tJHtub2RlLm5hbWV9YDtcclxuICAgIGNvbnN0IG1vZGlmaWVkID0gdGhpcy5jb21wYXJpc29uUmVzdWx0Lm1vZGlmaWVkLmZpbmQobiA9PiBgJHtuLklkfS0ke24ubmFtZX1gID09PSBub2RlSWQpO1xyXG5cclxuICAgIHJldHVybiBtb2RpZmllZD8ubW9kaWZpZWRQcm9wZXJ0aWVzIHx8IFtdO1xyXG4gIH1cclxuXHJcbiAgdG9nZ2xlRnVsbFNjcmVlbigpOiB2b2lkIHtcclxuICAgIHRoaXMuaXNGdWxsU2NyZWVuID0gIXRoaXMuaXNGdWxsU2NyZWVuO1xyXG4gIH1cclxufSIsIjxkaXYgY2xhc3M9XCJjb21wYXJpc29uLWNvbnRhaW5lclwiIFtjbGFzcy5mdWxsc2NyZWVuXT1cImlzRnVsbFNjcmVlblwiPlxyXG4gICAgPGhlYWRlciBjbGFzcz1cImhlYWRlclwiPlxyXG4gICAgICAgIDxoMT5FbnRpdHkgU3RydWN0dXJlIENvbXBhcmlzb248L2gxPlxyXG4gICAgICAgIDxwIGNsYXNzPVwic3VidGl0bGVcIj5Db21wYXJlIEVudGl0eVN0cnVjdHVyZUpzb24gYmV0d2VlbiB0d28gZW50aXR5IGV4cG9ydHM8L3A+XHJcbiAgICA8L2hlYWRlcj5cclxuXHJcbiAgICA8ZGl2IGNsYXNzPVwiZmlsZS1zZWxlY3Rpb25cIj5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwiZmlsZS1pbnB1dC1ncm91cFwiPlxyXG4gICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJmaWxlLWxhYmVsIGxlZnRcIj5cclxuICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiZmlsZVwiIGFjY2VwdD1cIi54bWxcIiAoY2hhbmdlKT1cIm9uTGVmdEZpbGVTZWxlY3RlZCgkZXZlbnQpXCIgaGlkZGVuPlxyXG4gICAgICAgICAgICAgICAgPHN2ZyB3aWR0aD1cIjE2XCIgaGVpZ2h0PVwiMTZcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD1cIk0yMSAxNXY0YTIgMiAwIDAgMS0yIDJINWEyIDIgMCAwIDEtMi0ydi00XCIgLz5cclxuICAgICAgICAgICAgICAgICAgICA8cG9seWxpbmUgcG9pbnRzPVwiMTcgOCAxMiAzIDcgOFwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgPGxpbmUgeDE9XCIxMlwiIHkxPVwiM1wiIHgyPVwiMTJcIiB5Mj1cIjE1XCIgLz5cclxuICAgICAgICAgICAgICAgIDwvc3ZnPlxyXG4gICAgICAgICAgICAgICAgTG9hZCBMZWZ0IEVudGl0eVxyXG4gICAgICAgICAgICA8L2xhYmVsPlxyXG4gICAgICAgICAgICA8c3BhbiAqbmdJZj1cImxlZnRGaWxlXCIgY2xhc3M9XCJmaWxlLW5hbWVcIj57eyBsZWZ0RmlsZSB9fTwvc3Bhbj5cclxuICAgICAgICA8L2Rpdj5cclxuXHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInZzLXNlcGFyYXRvclwiPlZTPC9kaXY+XHJcblxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJmaWxlLWlucHV0LWdyb3VwXCI+XHJcbiAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cImZpbGUtbGFiZWwgcmlnaHRcIj5cclxuICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwiZmlsZVwiIGFjY2VwdD1cIi54bWxcIiAoY2hhbmdlKT1cIm9uUmlnaHRGaWxlU2VsZWN0ZWQoJGV2ZW50KVwiIGhpZGRlbj5cclxuICAgICAgICAgICAgICAgIDxzdmcgd2lkdGg9XCIxNlwiIGhlaWdodD1cIjE2XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMlwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNMjEgMTV2NGEyIDIgMCAwIDEtMiAySDVhMiAyIDAgMCAxLTItMnYtNFwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBvbHlsaW5lIHBvaW50cz1cIjE3IDggMTIgMyA3IDhcIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgIDxsaW5lIHgxPVwiMTJcIiB5MT1cIjNcIiB4Mj1cIjEyXCIgeTI9XCIxNVwiIC8+XHJcbiAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgICAgIExvYWQgUmlnaHQgRW50aXR5XHJcbiAgICAgICAgICAgIDwvbGFiZWw+XHJcbiAgICAgICAgICAgIDxzcGFuICpuZ0lmPVwicmlnaHRGaWxlXCIgY2xhc3M9XCJmaWxlLW5hbWVcIj57eyByaWdodEZpbGUgfX08L3NwYW4+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuXHJcbiAgICA8ZGl2IGNsYXNzPVwiZW50aXR5LXNlbGVjdGlvblwiICpuZ0lmPVwibGVmdEVudGl0aWVzLmxlbmd0aCA+IDAgfHwgcmlnaHRFbnRpdGllcy5sZW5ndGggPiAwXCI+XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cImVudGl0eS1saXN0XCI+XHJcbiAgICAgICAgICAgIDxoMz5MZWZ0IEVudGl0aWVzPC9oMz5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImVudGl0aWVzXCI+XHJcbiAgICAgICAgICAgICAgICA8ZGl2ICpuZ0Zvcj1cImxldCBlbnRpdHkgb2YgbGVmdEVudGl0aWVzXCIgY2xhc3M9XCJlbnRpdHktY2FyZFwiXHJcbiAgICAgICAgICAgICAgICAgICAgW2NsYXNzLnNlbGVjdGVkXT1cInNlbGVjdGVkTGVmdEVudGl0eSA9PT0gZW50aXR5XCIgKGNsaWNrKT1cInNlbGVjdExlZnRFbnRpdHkoZW50aXR5KVwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdHktaWNvblwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8c3ZnIHdpZHRoPVwiMjBcIiBoZWlnaHQ9XCIyMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJva2Utd2lkdGg9XCIyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPVwiTTIyIDE5YTIgMiAwIDAgMS0yIDJINGEyIDIgMCAwIDEtMi0yVjVhMiAyIDAgMCAxIDItMmg1bDIgM2g5YTIgMiAwIDAgMSAyIDJ6XCIgLz5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPC9zdmc+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImVudGl0eS1pbmZvXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdHktbmFtZVwiPnt7IGVudGl0eS5uYW1lIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdHktaWRcIj57eyBlbnRpdHkuaWQgfX08L2Rpdj5cclxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICA8L2Rpdj5cclxuXHJcbiAgICAgICAgPGRpdiBjbGFzcz1cImVudGl0eS1saXN0XCI+XHJcbiAgICAgICAgICAgIDxoMz5SaWdodCBFbnRpdGllczwvaDM+XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdGllc1wiPlxyXG4gICAgICAgICAgICAgICAgPGRpdiAqbmdGb3I9XCJsZXQgZW50aXR5IG9mIHJpZ2h0RW50aXRpZXNcIiBjbGFzcz1cImVudGl0eS1jYXJkXCJcclxuICAgICAgICAgICAgICAgICAgICBbY2xhc3Muc2VsZWN0ZWRdPVwic2VsZWN0ZWRSaWdodEVudGl0eSA9PT0gZW50aXR5XCIgKGNsaWNrKT1cInNlbGVjdFJpZ2h0RW50aXR5KGVudGl0eSlcIj5cclxuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW50aXR5LWljb25cIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHN2ZyB3aWR0aD1cIjIwXCIgaGVpZ2h0PVwiMjBcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlLXdpZHRoPVwiMlwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD1cIk0yMiAxOWEyIDIgMCAwIDEtMiAySDRhMiAyIDAgMCAxLTItMlY1YTIgMiAwIDAgMSAyLTJoNWwyIDNoOWEyIDIgMCAwIDEgMiAyelwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvc3ZnPlxyXG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJlbnRpdHktaW5mb1wiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW50aXR5LW5hbWVcIj57eyBlbnRpdHkubmFtZSB9fTwvZGl2PlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZW50aXR5LWlkXCI+e3sgZW50aXR5LmlkIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuXHJcbiAgICA8ZGl2IGNsYXNzPVwic3VtbWFyeS1zZWN0aW9uXCIgKm5nSWY9XCJjb21wYXJpc29uUmVzdWx0XCI+XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInN1bW1hcnlcIj5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInN1bW1hcnktaXRlbSBhZGRlZFwiPlxyXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJjb3VudFwiPnt7IGNvbXBhcmlzb25SZXN1bHQucmlnaHRPbmx5Lmxlbmd0aCB9fTwvc3Bhbj5cclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5BZGRlZDwvc3Bhbj5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdW1tYXJ5LWl0ZW0gcmVtb3ZlZFwiPlxyXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJjb3VudFwiPnt7IGNvbXBhcmlzb25SZXN1bHQubGVmdE9ubHkubGVuZ3RoIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsYWJlbFwiPlJlbW92ZWQ8L3NwYW4+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3VtbWFyeS1pdGVtIG1vZGlmaWVkXCI+XHJcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImNvdW50XCI+e3sgY29tcGFyaXNvblJlc3VsdC5tb2RpZmllZC5sZW5ndGggfX08L3NwYW4+XHJcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxhYmVsXCI+TW9kaWZpZWQ8L3NwYW4+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3VtbWFyeS1pdGVtIHVuY2hhbmdlZFwiPlxyXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJjb3VudFwiPnt7IGNvbXBhcmlzb25SZXN1bHQudW5jaGFuZ2VkLmxlbmd0aCB9fTwvc3Bhbj5cclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGFiZWxcIj5VbmNoYW5nZWQ8L3NwYW4+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgPC9kaXY+XHJcblxyXG4gICAgPGRpdiBjbGFzcz1cImNvbXBhcmlzb24tcmVzdWx0c1wiICpuZ0lmPVwiY29tcGFyaXNvblJlc3VsdFwiPlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJyZXN1bHRzLWhlYWRlclwiPlxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiaGVhZGVyLXRpdGxlXCI+XHJcbiAgICAgICAgICAgICAgICA8aDI+Q29tcGFyaXNvbiBSZXN1bHRzPC9oMj5cclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJoZWFkZXItYWN0aW9uc1wiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkaWZmLW5hdmlnYXRpb25cIiAqbmdJZj1cImhhc0RpZmZlcmVuY2VzXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJuYXYtYnRuXCIgKGNsaWNrKT1cIm5hdmlnYXRlVG9QcmV2aW91c0RpZmZlcmVuY2UoKVwiIHRpdGxlPVwiUHJldmlvdXMgRGlmZmVyZW5jZVwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN2ZyB3aWR0aD1cIjIwXCIgaGVpZ2h0PVwiMjBcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBvbHlsaW5lIHBvaW50cz1cIjE4IDE1IDEyIDkgNiAxNVwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGlmZi1jb3VudGVyXCI+e3sgY3VycmVudERpZmZlcmVuY2VOdW1iZXIgfX0gLyB7eyB0b3RhbERpZmZlcmVuY2VzIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwibmF2LWJ0blwiIChjbGljayk9XCJuYXZpZ2F0ZVRvTmV4dERpZmZlcmVuY2UoKVwiIHRpdGxlPVwiTmV4dCBEaWZmZXJlbmNlXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c3ZnIHdpZHRoPVwiMjBcIiBoZWlnaHQ9XCIyMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cG9seWxpbmUgcG9pbnRzPVwiNiA5IDEyIDE1IDE4IDlcIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zdmc+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJmdWxsc2NyZWVuLWJ0blwiIChjbGljayk9XCJ0b2dnbGVGdWxsU2NyZWVuKClcIiBbdGl0bGVdPVwiaXNGdWxsU2NyZWVuID8gJ0V4aXQgRnVsbCBTY3JlZW4nIDogJ0VudGVyIEZ1bGwgU2NyZWVuJ1wiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8c3ZnICpuZ0lmPVwiIWlzRnVsbFNjcmVlblwiIHdpZHRoPVwiMjBcIiBoZWlnaHQ9XCIyMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNOCAzSDVhMiAyIDAgMCAwLTIgMnYzbTE4IDBWNWEyIDIgMCAwIDAtMi0yaC0zbTAgMThoM2EyIDIgMCAwIDAgMi0ydi0zTTMgMTZ2M2EyIDIgMCAwIDAgMiAyaDNcIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHN2ZyAqbmdJZj1cImlzRnVsbFNjcmVlblwiIHdpZHRoPVwiMjBcIiBoZWlnaHQ9XCIyMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNOCAzdjNhMiAyIDAgMCAxLTIgMkgzbTE4IDBoLTNhMiAyIDAgMCAxLTItMlYzbTAgMTh2LTNhMiAyIDAgMCAxIDItMmgzTTMgMTZoM2EyIDIgMCAwIDEgMiAydjNcIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICA8L2Rpdj5cclxuXHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInN0cnVjdHVyZS1jb21wYXJpc29uXCI+XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb21wYXJpc29uLXNlY3Rpb25cIj5cclxuICAgICAgICAgICAgICAgIDxoMz5cclxuICAgICAgICAgICAgICAgICAgICA8c3ZnIHdpZHRoPVwiMTZcIiBoZWlnaHQ9XCIxNlwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD1cIk05IDExbDMgM0wyMiA0XCIgLz5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD1cIk0yMSAxMnY3YTIgMiAwIDAgMS0yIDJINWEyIDIgMCAwIDEtMi0yVjVhMiAyIDAgMCAxIDItMmgxMVwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9zdmc+XHJcbiAgICAgICAgICAgICAgICAgICAge3sgc2VsZWN0ZWRMZWZ0RW50aXR5Lm5hbWUgfX1cclxuICAgICAgICAgICAgICAgIDwvaDM+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RydWN0dXJlLXRyZWVcIj5cclxuICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBub2RlIG9mIHNlbGVjdGVkTGVmdEVudGl0eS5zdHJ1Y3R1cmVcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJub2RlVGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBub2RlLCBzaWRlOiAnbGVmdCcgfVwiPjwvbmctY29udGFpbmVyPlxyXG4gICAgICAgICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImNvbXBhcmlzb24tc2VjdGlvblwiPlxyXG4gICAgICAgICAgICAgICAgPGgzPlxyXG4gICAgICAgICAgICAgICAgICAgIDxzdmcgd2lkdGg9XCIxNlwiIGhlaWdodD1cIjE2XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMlwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPVwiTTkgMTFsMyAzTDIyIDRcIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPVwiTTIxIDEydjdhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJWNWEyIDIgMCAwIDEgMi0yaDExXCIgLz5cclxuICAgICAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgICAgICAgICB7eyBzZWxlY3RlZFJpZ2h0RW50aXR5Lm5hbWUgfX1cclxuICAgICAgICAgICAgICAgIDwvaDM+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwic3RydWN0dXJlLXRyZWVcIj5cclxuICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBub2RlIG9mIHNlbGVjdGVkUmlnaHRFbnRpdHkuc3RydWN0dXJlXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwibm9kZVRlbXBsYXRlOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogbm9kZSwgc2lkZTogJ3JpZ2h0JyB9XCI+PC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuPC9kaXY+XHJcblxyXG48bmctdGVtcGxhdGUgI25vZGVUZW1wbGF0ZSBsZXQtbm9kZSBsZXQtc2lkZT1cInNpZGVcIj5cclxuICAgIDxkaXYgY2xhc3M9XCJ0cmVlLW5vZGVcIiBbY2xhc3NdPVwiZ2V0Q2hhbmdlVHlwZShub2RlKVwiIFthdHRyLmRhdGEtbm9kZS1pZF09XCJnZXROb2RlSWQobm9kZSwgc2lkZSlcIj5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwibm9kZS1oZWFkZXJcIj5cclxuICAgICAgICAgICAgPGJ1dHRvbiAqbmdJZj1cImhhc0NoaWxkcmVuKG5vZGUpXCIgY2xhc3M9XCJleHBhbmQtYnRuXCIgKGNsaWNrKT1cInRvZ2dsZU5vZGUoZ2V0Tm9kZUlkKG5vZGUsIHNpZGUpKVwiPlxyXG4gICAgICAgICAgICAgICAgPHN2ZyB3aWR0aD1cIjEyXCIgaGVpZ2h0PVwiMTJcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBvbHlsaW5lICpuZ0lmPVwiIWlzRXhwYW5kZWQoZ2V0Tm9kZUlkKG5vZGUsIHNpZGUpKVwiIHBvaW50cz1cIjkgMTggMTUgMTIgOSA2XCIgLz5cclxuICAgICAgICAgICAgICAgICAgICA8cG9seWxpbmUgKm5nSWY9XCJpc0V4cGFuZGVkKGdldE5vZGVJZChub2RlLCBzaWRlKSlcIiBwb2ludHM9XCI2IDkgMTIgMTUgMTggOVwiIC8+XHJcbiAgICAgICAgICAgICAgICA8L3N2Zz5cclxuICAgICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICAgIDxkaXYgKm5nSWY9XCIhaGFzQ2hpbGRyZW4obm9kZSlcIiBjbGFzcz1cInNwYWNlclwiPjwvZGl2PlxyXG5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm5vZGUtaWNvblwiPlxyXG4gICAgICAgICAgICAgICAgPHN2ZyB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPHBhdGggKm5nSWY9XCJub2RlLm5vZGVUeXBlID09PSAnR3JvdXAnXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgZD1cIk0yMiAxOWEyIDIgMCAwIDEtMiAySDRhMiAyIDAgMCAxLTItMlY1YTIgMiAwIDAgMSAyLTJoNWwyIDNoOWEyIDIgMCAwIDEgMiAyelwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSAqbmdJZj1cIm5vZGUubm9kZVR5cGUgPT09ICdBdHRyaWJ1dGUnXCIgY3g9XCIxMlwiIGN5PVwiMTJcIiByPVwiMTBcIiAvPlxyXG4gICAgICAgICAgICAgICAgPC9zdmc+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm5vZGUtY29udGVudFwiPlxyXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJub2RlLW5hbWVcIj57eyBub2RlLm5hbWUgfX08L3NwYW4+XHJcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5vZGUtdHlwZVwiPnt7IG5vZGUubm9kZVR5cGUgfX08L3NwYW4+XHJcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImNoYW5nZS1iYWRnZVwiICpuZ0lmPVwiZ2V0Q2hhbmdlVHlwZShub2RlKSAhPT0gJ3VuY2hhbmdlZCdcIj5cclxuICAgICAgICAgICAgICAgICAgICB7eyBnZXRDaGFuZ2VUeXBlKG5vZGUpIH19XHJcbiAgICAgICAgICAgICAgICA8L3NwYW4+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwibm9kZS1wcm9wZXJ0aWVzXCIgKm5nSWY9XCJnZXRQcm9wZXJ0eUtleXMobm9kZSkubGVuZ3RoID4gMFwiPlxyXG4gICAgICAgICAgICA8ZGl2ICpuZ0Zvcj1cImxldCBrZXkgb2YgZ2V0UHJvcGVydHlLZXlzKG5vZGUpXCIgY2xhc3M9XCJwcm9wZXJ0eVwiXHJcbiAgICAgICAgICAgICAgICBbY2xhc3MubW9kaWZpZWRdPVwiZ2V0TW9kaWZpZWRQcm9wZXJ0aWVzKG5vZGUpLmluY2x1ZGVzKGtleSlcIj5cclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwicHJvcGVydHkta2V5XCI+e3sga2V5IH19Ojwvc3Bhbj5cclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwicHJvcGVydHktdmFsdWVcIj57eyBnZXRQcm9wZXJ0aWVzKG5vZGUpW2tleV0gfX08L3NwYW4+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwibm9kZS1jaGlsZHJlblwiICpuZ0lmPVwiaGFzQ2hpbGRyZW4obm9kZSkgJiYgaXNFeHBhbmRlZChnZXROb2RlSWQobm9kZSwgc2lkZSkpXCI+XHJcbiAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IGNoaWxkIG9mIGdldENoaWxkcmVuKG5vZGUpXCI+XHJcbiAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyXHJcbiAgICAgICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJub2RlVGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBjaGlsZCwgc2lkZTogc2lkZSB9XCI+PC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgPC9kaXY+XHJcbjwvbmctdGVtcGxhdGU+Il19
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export class EntityComparisonService {
|
|
4
|
+
parseEntities(xmlString) {
|
|
5
|
+
const parser = new DOMParser();
|
|
6
|
+
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
|
|
7
|
+
const exportItems = xmlDoc.querySelectorAll('ExportItem[Type="Entity"]');
|
|
8
|
+
const entities = [];
|
|
9
|
+
exportItems.forEach((item) => {
|
|
10
|
+
const entity = item.querySelector('Entity_WithoutRedundancies');
|
|
11
|
+
if (!entity)
|
|
12
|
+
return;
|
|
13
|
+
const structureJson = entity.querySelector('EntityStructureJson')?.textContent;
|
|
14
|
+
if (!structureJson)
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
const structure = JSON.parse(structureJson);
|
|
18
|
+
entities.push({
|
|
19
|
+
id: entity.querySelector('EntityId')?.textContent || '',
|
|
20
|
+
name: entity.querySelector('EntityName')?.textContent || '',
|
|
21
|
+
structure: structure
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
console.error('Error parsing EntityStructureJson:', e);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return entities;
|
|
29
|
+
}
|
|
30
|
+
compareStructures(leftStructure, rightStructure) {
|
|
31
|
+
const leftOnly = [];
|
|
32
|
+
const rightOnly = [];
|
|
33
|
+
const modified = [];
|
|
34
|
+
const unchanged = [];
|
|
35
|
+
// Create maps for quick lookup
|
|
36
|
+
const leftMap = this.createNodeMap(leftStructure);
|
|
37
|
+
const rightMap = this.createNodeMap(rightStructure);
|
|
38
|
+
// Find nodes only in left or modified
|
|
39
|
+
for (const [key, leftNode] of leftMap.entries()) {
|
|
40
|
+
if (!rightMap.has(key)) {
|
|
41
|
+
leftOnly.push(leftNode);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const rightNode = rightMap.get(key);
|
|
45
|
+
const diff = this.compareNodes(leftNode, rightNode);
|
|
46
|
+
if (diff.length > 0) {
|
|
47
|
+
modified.push({ ...leftNode, modifiedProperties: diff });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
unchanged.push(leftNode);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Find nodes only in right
|
|
55
|
+
for (const [key, rightNode] of rightMap.entries()) {
|
|
56
|
+
if (!leftMap.has(key)) {
|
|
57
|
+
rightOnly.push(rightNode);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { leftOnly, rightOnly, modified, unchanged };
|
|
61
|
+
}
|
|
62
|
+
createNodeMap(structure, parentPath = '') {
|
|
63
|
+
const map = new Map();
|
|
64
|
+
for (const node of structure) {
|
|
65
|
+
const nodePath = parentPath ? `${parentPath}/${node.Id}-${node.name}` : `${node.Id}-${node.name}`;
|
|
66
|
+
map.set(nodePath, node);
|
|
67
|
+
if (node.Children) {
|
|
68
|
+
try {
|
|
69
|
+
const children = JSON.parse(node.Children);
|
|
70
|
+
const childMap = this.createNodeMap(children, nodePath);
|
|
71
|
+
for (const [key, value] of childMap.entries()) {
|
|
72
|
+
map.set(key, value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
// Ignore parsing errors
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return map;
|
|
81
|
+
}
|
|
82
|
+
compareNodes(left, right) {
|
|
83
|
+
const modifiedProps = [];
|
|
84
|
+
try {
|
|
85
|
+
const leftProps = JSON.parse(left.Properties || '{}');
|
|
86
|
+
const rightProps = JSON.parse(right.Properties || '{}');
|
|
87
|
+
const allKeys = new Set([
|
|
88
|
+
...Object.keys(leftProps),
|
|
89
|
+
...Object.keys(rightProps)
|
|
90
|
+
]);
|
|
91
|
+
for (const key of allKeys) {
|
|
92
|
+
if (leftProps[key] !== rightProps[key]) {
|
|
93
|
+
modifiedProps.push(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
// Ignore parsing errors
|
|
99
|
+
}
|
|
100
|
+
return modifiedProps;
|
|
101
|
+
}
|
|
102
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
103
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, providedIn: 'root' });
|
|
104
|
+
}
|
|
105
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, decorators: [{
|
|
106
|
+
type: Injectable,
|
|
107
|
+
args: [{
|
|
108
|
+
providedIn: 'root'
|
|
109
|
+
}]
|
|
110
|
+
}] });
|
|
111
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWNvbXBhcmlzb24uc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2NvbmNlcHRvLXVzZXItY29udHJvbHMvc3JjL2xpYi9lbnRpdHktY29tcGFyaXNvbi9jb3JlL3NlcnZpY2VzL2VudGl0eS1jb21wYXJpc29uLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFNM0MsTUFBTSxPQUFPLHVCQUF1QjtJQUVsQyxhQUFhLENBQUMsU0FBaUI7UUFDN0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUMvQixNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLFNBQVMsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUU3RCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsMkJBQTJCLENBQUMsQ0FBQztRQUN6RSxNQUFNLFFBQVEsR0FBVSxFQUFFLENBQUM7UUFFM0IsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1lBQzNCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsNEJBQTRCLENBQUMsQ0FBQztZQUNoRSxJQUFJLENBQUMsTUFBTTtnQkFBRSxPQUFPO1lBRXBCLE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMscUJBQXFCLENBQUMsRUFBRSxXQUFXLENBQUM7WUFDL0UsSUFBSSxDQUFDLGFBQWE7Z0JBQUUsT0FBTztZQUUzQixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztnQkFDNUMsUUFBUSxDQUFDLElBQUksQ0FBQztvQkFDWixFQUFFLEVBQUUsTUFBTSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsRUFBRSxXQUFX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QyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ1gsd0JBQXdCO1FBQzFCLENBQUM7UUFFRCxPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO3dHQS9HVSx1QkFBdUI7NEdBQXZCLHVCQUF1QixjQUZ0QixNQUFNOzs0RkFFUCx1QkFBdUI7a0JBSG5DLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBDb21wYXJpc29uUmVzdWx0IH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9lbnRpdHktY29tcGFyaXNvbi5jb21wb25lbnQnO1xyXG5cclxuQEluamVjdGFibGUoe1xyXG4gIHByb3ZpZGVkSW46ICdyb290J1xyXG59KVxyXG5leHBvcnQgY2xhc3MgRW50aXR5Q29tcGFyaXNvblNlcnZpY2Uge1xyXG5cclxuICBwYXJzZUVudGl0aWVzKHhtbFN0cmluZzogc3RyaW5nKTogYW55W10ge1xyXG4gICAgY29uc3QgcGFyc2VyID0gbmV3IERPTVBhcnNlcigpO1xyXG4gICAgY29uc3QgeG1sRG9jID0gcGFyc2VyLnBhcnNlRnJvbVN0cmluZyh4bWxTdHJpbmcsICd0ZXh0L3htbCcpO1xyXG4gICAgXHJcbiAgICBjb25zdCBleHBvcnRJdGVtcyA9IHhtbERvYy5xdWVyeVNlbGVjdG9yQWxsKCdFeHBvcnRJdGVtW1R5cGU9XCJFbnRpdHlcIl0nKTtcclxuICAgIGNvbnN0IGVudGl0aWVzOiBhbnlbXSA9IFtdO1xyXG4gICAgXHJcbiAgICBleHBvcnRJdGVtcy5mb3JFYWNoKChpdGVtKSA9PiB7XHJcbiAgICAgIGNvbnN0IGVudGl0eSA9IGl0ZW0ucXVlcnlTZWxlY3RvcignRW50aXR5X1dpdGhvdXRSZWR1bmRhbmNpZXMnKTtcclxuICAgICAgaWYgKCFlbnRpdHkpIHJldHVybjtcclxuICAgICAgXHJcbiAgICAgIGNvbnN0IHN0cnVjdHVyZUpzb24gPSBlbnRpdHkucXVlcnlTZWxlY3RvcignRW50aXR5U3RydWN0dXJlSnNvbicpPy50ZXh0Q29udGVudDtcclxuICAgICAgaWYgKCFzdHJ1Y3R1cmVKc29uKSByZXR1cm47XHJcbiAgICAgIFxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHN0cnVjdHVyZSA9IEpTT04ucGFyc2Uoc3RydWN0dXJlSnNvbik7XHJcbiAgICAgICAgZW50aXRpZXMucHVzaCh7XHJcbiAgICAgICAgICBpZDogZW50aXR5LnF1ZXJ5U2VsZWN0b3IoJ0VudGl0eUlkJyk/LnRleHRDb250ZW50IHx8ICcnLFxyXG4gICAgICAgICAgbmFtZTogZW50aXR5LnF1ZXJ5U2VsZWN0b3IoJ0VudGl0eU5hbWUnKT8udGV4dENvbnRlbnQgfHwgJycsXHJcbiAgICAgICAgICBzdHJ1Y3R1cmU6IHN0cnVjdHVyZVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgcGFyc2luZyBFbnRpdHlTdHJ1Y3R1cmVKc29uOicsIGUpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICAgIFxyXG4gICAgcmV0dXJuIGVudGl0aWVzO1xyXG4gIH1cclxuXHJcbiAgY29tcGFyZVN0cnVjdHVyZXMobGVmdFN0cnVjdHVyZTogYW55W10sIHJpZ2h0U3RydWN0dXJlOiBhbnlbXSk6IENvbXBhcmlzb25SZXN1bHQge1xyXG4gICAgY29uc3QgbGVmdE9ubHk6IGFueVtdID0gW107XHJcbiAgICBjb25zdCByaWdodE9ubHk6IGFueVtdID0gW107XHJcbiAgICBjb25zdCBtb2RpZmllZDogYW55W10gPSBbXTtcclxuICAgIGNvbnN0IHVuY2hhbmdlZDogYW55W10gPSBbXTtcclxuICAgIFxyXG4gICAgLy8gQ3JlYXRlIG1hcHMgZm9yIHF1aWNrIGxvb2t1cFxyXG4gICAgY29uc3QgbGVmdE1hcCA9IHRoaXMuY3JlYXRlTm9kZU1hcChsZWZ0U3RydWN0dXJlKTtcclxuICAgIGNvbnN0IHJpZ2h0TWFwID0gdGhpcy5jcmVhdGVOb2RlTWFwKHJpZ2h0U3RydWN0dXJlKTtcclxuICAgIFxyXG4gICAgLy8gRmluZCBub2RlcyBvbmx5IGluIGxlZnQgb3IgbW9kaWZpZWRcclxuICAgIGZvciAoY29uc3QgW2tleSwgbGVmdE5vZGVdIG9mIGxlZnRNYXAuZW50cmllcygpKSB7XHJcbiAgICAgIGlmICghcmlnaHRNYXAuaGFzKGtleSkpIHtcclxuICAgICAgICBsZWZ0T25seS5wdXNoKGxlZnROb2RlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zdCByaWdodE5vZGUgPSByaWdodE1hcC5nZXQoa2V5KTtcclxuICAgICAgICBjb25zdCBkaWZmID0gdGhpcy5jb21wYXJlTm9kZXMobGVmdE5vZGUsIHJpZ2h0Tm9kZSEpO1xyXG4gICAgICAgIGlmIChkaWZmLmxlbmd0aCA+IDApIHtcclxuICAgICAgICAgIG1vZGlmaWVkLnB1c2goeyAuLi5sZWZ0Tm9kZSwgbW9kaWZpZWRQcm9wZXJ0aWVzOiBkaWZmIH0pO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB1bmNoYW5nZWQucHVzaChsZWZ0Tm9kZSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIC8vIEZpbmQgbm9kZXMgb25seSBpbiByaWdodFxyXG4gICAgZm9yIChjb25zdCBba2V5LCByaWdodE5vZGVdIG9mIHJpZ2h0TWFwLmVudHJpZXMoKSkge1xyXG4gICAgICBpZiAoIWxlZnRNYXAuaGFzKGtleSkpIHtcclxuICAgICAgICByaWdodE9ubHkucHVzaChyaWdodE5vZGUpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIHJldHVybiB7IGxlZnRPbmx5LCByaWdodE9ubHksIG1vZGlmaWVkLCB1bmNoYW5nZWQgfTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgY3JlYXRlTm9kZU1hcChzdHJ1Y3R1cmU6IGFueVtdLCBwYXJlbnRQYXRoOiBzdHJpbmcgPSAnJyk6IE1hcDxzdHJpbmcsIGFueT4ge1xyXG4gICAgY29uc3QgbWFwID0gbmV3IE1hcDxzdHJpbmcsIGFueT4oKTtcclxuICAgIFxyXG4gICAgZm9yIChjb25zdCBub2RlIG9mIHN0cnVjdHVyZSkge1xyXG4gICAgICBjb25zdCBub2RlUGF0aCA9IHBhcmVudFBhdGggPyBgJHtwYXJlbnRQYXRofS8ke25vZGUuSWR9LSR7bm9kZS5uYW1lfWAgOiBgJHtub2RlLklkfS0ke25vZGUubmFtZX1gO1xyXG4gICAgICBtYXAuc2V0KG5vZGVQYXRoLCBub2RlKTtcclxuICAgICAgXHJcbiAgICAgIGlmIChub2RlLkNoaWxkcmVuKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGNvbnN0IGNoaWxkcmVuID0gSlNPTi5wYXJzZShub2RlLkNoaWxkcmVuKTtcclxuICAgICAgICAgIGNvbnN0IGNoaWxkTWFwID0gdGhpcy5jcmVhdGVOb2RlTWFwKGNoaWxkcmVuLCBub2RlUGF0aCk7XHJcbiAgICAgICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBjaGlsZE1hcC5lbnRyaWVzKCkpIHtcclxuICAgICAgICAgICAgbWFwLnNldChrZXksIHZhbHVlKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAvLyBJZ25vcmUgcGFyc2luZyBlcnJvcnNcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIG1hcDtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgY29tcGFyZU5vZGVzKGxlZnQ6IGFueSwgcmlnaHQ6IGFueSk6IHN0cmluZ1tdIHtcclxuICAgIGNvbnN0IG1vZGlmaWVkUHJvcHM6IHN0cmluZ1tdID0gW107XHJcbiAgICBcclxuICAgIHRyeSB7XHJcbiAgICAgIGNvbnN0IGxlZnRQcm9wcyA9IEpTT04ucGFyc2UobGVmdC5Qcm9wZXJ0aWVzIHx8ICd7fScpO1xyXG4gICAgICBjb25zdCByaWdodFByb3BzID0gSlNPTi5wYXJzZShyaWdodC5Qcm9wZXJ0aWVzIHx8ICd7fScpO1xyXG4gICAgICBcclxuICAgICAgY29uc3QgYWxsS2V5cyA9IG5ldyBTZXQoW1xyXG4gICAgICAgIC4uLk9iamVjdC5rZXlzKGxlZnRQcm9wcyksXHJcbiAgICAgICAgLi4uT2JqZWN0LmtleXMocmlnaHRQcm9wcylcclxuICAgICAgXSk7XHJcbiAgICAgIFxyXG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBhbGxLZXlzKSB7XHJcbiAgICAgICAgaWYgKGxlZnRQcm9wc1trZXldICE9PSByaWdodFByb3BzW2tleV0pIHtcclxuICAgICAgICAgIG1vZGlmaWVkUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAvLyBJZ25vcmUgcGFyc2luZyBlcnJvcnNcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIG1vZGlmaWVkUHJvcHM7XHJcbiAgfVxyXG59Il19
|
package/esm2022/public-api.mjs
CHANGED
|
@@ -5,4 +5,7 @@ export * from './lib/concepto-user-controls.service';
|
|
|
5
5
|
export * from './lib/concepto-user-controls.component';
|
|
6
6
|
export * from './lib/concepto-message/concepto-message.component';
|
|
7
7
|
export * from './lib/concepto-context-menu/concepto-context-menu.component';
|
|
8
|
-
|
|
8
|
+
export * from './lib/concepto-tree/concepto-tree.component';
|
|
9
|
+
export * from './lib/entity-comparison/components/entity-comparison.component';
|
|
10
|
+
export * from './lib/entity-comparison/core/services/entity-comparison.service';
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3Byb2plY3RzL2NvbmNlcHRvLXVzZXItY29udHJvbHMvc3JjL3B1YmxpYy1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLHNDQUFzQyxDQUFDO0FBQ3JELGNBQWMsd0NBQXdDLENBQUM7QUFDdkQsY0FBYyxtREFBbUQsQ0FBQztBQUNsRSxjQUFjLDZEQUE2RCxDQUFDO0FBQzVFLGNBQWMsNkNBQTZDLENBQUM7QUFDNUQsY0FBYyxnRUFBZ0UsQ0FBQztBQUMvRSxjQUFjLGlFQUFpRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLypcclxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIGNvbmNlcHRvLW1lc3NhZ2VcclxuICovXHJcblxyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb25jZXB0by11c2VyLWNvbnRyb2xzLnNlcnZpY2UnO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb25jZXB0by11c2VyLWNvbnRyb2xzLmNvbXBvbmVudCc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbmNlcHRvLW1lc3NhZ2UvY29uY2VwdG8tbWVzc2FnZS5jb21wb25lbnQnO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9jb25jZXB0by1jb250ZXh0LW1lbnUvY29uY2VwdG8tY29udGV4dC1tZW51LmNvbXBvbmVudCc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbmNlcHRvLXRyZWUvY29uY2VwdG8tdHJlZS5jb21wb25lbnQnO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9lbnRpdHktY29tcGFyaXNvbi9jb21wb25lbnRzL2VudGl0eS1jb21wYXJpc29uLmNvbXBvbmVudCc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL2VudGl0eS1jb21wYXJpc29uL2NvcmUvc2VydmljZXMvZW50aXR5LWNvbXBhcmlzb24uc2VydmljZSc7Il19
|
|
@@ -130,6 +130,344 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
130
130
|
args: ['document:click', ['$event']]
|
|
131
131
|
}] } });
|
|
132
132
|
|
|
133
|
+
class ConceptoTreeComponent {
|
|
134
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConceptoTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
135
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ConceptoTreeComponent, isStandalone: true, selector: "lib-concepto-tree", ngImport: i0, template: `
|
|
136
|
+
<p>
|
|
137
|
+
concepto-tree works!
|
|
138
|
+
</p>
|
|
139
|
+
`, isInline: true, styles: [""] });
|
|
140
|
+
}
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ConceptoTreeComponent, decorators: [{
|
|
142
|
+
type: Component,
|
|
143
|
+
args: [{ selector: 'lib-concepto-tree', standalone: true, imports: [], template: `
|
|
144
|
+
<p>
|
|
145
|
+
concepto-tree works!
|
|
146
|
+
</p>
|
|
147
|
+
` }]
|
|
148
|
+
}] });
|
|
149
|
+
|
|
150
|
+
class EntityComparisonService {
|
|
151
|
+
parseEntities(xmlString) {
|
|
152
|
+
const parser = new DOMParser();
|
|
153
|
+
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
|
|
154
|
+
const exportItems = xmlDoc.querySelectorAll('ExportItem[Type="Entity"]');
|
|
155
|
+
const entities = [];
|
|
156
|
+
exportItems.forEach((item) => {
|
|
157
|
+
const entity = item.querySelector('Entity_WithoutRedundancies');
|
|
158
|
+
if (!entity)
|
|
159
|
+
return;
|
|
160
|
+
const structureJson = entity.querySelector('EntityStructureJson')?.textContent;
|
|
161
|
+
if (!structureJson)
|
|
162
|
+
return;
|
|
163
|
+
try {
|
|
164
|
+
const structure = JSON.parse(structureJson);
|
|
165
|
+
entities.push({
|
|
166
|
+
id: entity.querySelector('EntityId')?.textContent || '',
|
|
167
|
+
name: entity.querySelector('EntityName')?.textContent || '',
|
|
168
|
+
structure: structure
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
console.error('Error parsing EntityStructureJson:', e);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return entities;
|
|
176
|
+
}
|
|
177
|
+
compareStructures(leftStructure, rightStructure) {
|
|
178
|
+
const leftOnly = [];
|
|
179
|
+
const rightOnly = [];
|
|
180
|
+
const modified = [];
|
|
181
|
+
const unchanged = [];
|
|
182
|
+
// Create maps for quick lookup
|
|
183
|
+
const leftMap = this.createNodeMap(leftStructure);
|
|
184
|
+
const rightMap = this.createNodeMap(rightStructure);
|
|
185
|
+
// Find nodes only in left or modified
|
|
186
|
+
for (const [key, leftNode] of leftMap.entries()) {
|
|
187
|
+
if (!rightMap.has(key)) {
|
|
188
|
+
leftOnly.push(leftNode);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const rightNode = rightMap.get(key);
|
|
192
|
+
const diff = this.compareNodes(leftNode, rightNode);
|
|
193
|
+
if (diff.length > 0) {
|
|
194
|
+
modified.push({ ...leftNode, modifiedProperties: diff });
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
unchanged.push(leftNode);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Find nodes only in right
|
|
202
|
+
for (const [key, rightNode] of rightMap.entries()) {
|
|
203
|
+
if (!leftMap.has(key)) {
|
|
204
|
+
rightOnly.push(rightNode);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { leftOnly, rightOnly, modified, unchanged };
|
|
208
|
+
}
|
|
209
|
+
createNodeMap(structure, parentPath = '') {
|
|
210
|
+
const map = new Map();
|
|
211
|
+
for (const node of structure) {
|
|
212
|
+
const nodePath = parentPath ? `${parentPath}/${node.Id}-${node.name}` : `${node.Id}-${node.name}`;
|
|
213
|
+
map.set(nodePath, node);
|
|
214
|
+
if (node.Children) {
|
|
215
|
+
try {
|
|
216
|
+
const children = JSON.parse(node.Children);
|
|
217
|
+
const childMap = this.createNodeMap(children, nodePath);
|
|
218
|
+
for (const [key, value] of childMap.entries()) {
|
|
219
|
+
map.set(key, value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
// Ignore parsing errors
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return map;
|
|
228
|
+
}
|
|
229
|
+
compareNodes(left, right) {
|
|
230
|
+
const modifiedProps = [];
|
|
231
|
+
try {
|
|
232
|
+
const leftProps = JSON.parse(left.Properties || '{}');
|
|
233
|
+
const rightProps = JSON.parse(right.Properties || '{}');
|
|
234
|
+
const allKeys = new Set([
|
|
235
|
+
...Object.keys(leftProps),
|
|
236
|
+
...Object.keys(rightProps)
|
|
237
|
+
]);
|
|
238
|
+
for (const key of allKeys) {
|
|
239
|
+
if (leftProps[key] !== rightProps[key]) {
|
|
240
|
+
modifiedProps.push(key);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (e) {
|
|
245
|
+
// Ignore parsing errors
|
|
246
|
+
}
|
|
247
|
+
return modifiedProps;
|
|
248
|
+
}
|
|
249
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
250
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, providedIn: 'root' });
|
|
251
|
+
}
|
|
252
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonService, decorators: [{
|
|
253
|
+
type: Injectable,
|
|
254
|
+
args: [{
|
|
255
|
+
providedIn: 'root'
|
|
256
|
+
}]
|
|
257
|
+
}] });
|
|
258
|
+
|
|
259
|
+
class EntityComparisonComponent {
|
|
260
|
+
comparisonService;
|
|
261
|
+
leftFile = '';
|
|
262
|
+
rightFile = '';
|
|
263
|
+
leftEntities = [];
|
|
264
|
+
rightEntities = [];
|
|
265
|
+
selectedLeftEntity = null;
|
|
266
|
+
selectedRightEntity = null;
|
|
267
|
+
comparisonResult = null;
|
|
268
|
+
expandedNodes = new Set();
|
|
269
|
+
isFullScreen = false;
|
|
270
|
+
currentDifferenceIndex = 0;
|
|
271
|
+
differences = [];
|
|
272
|
+
constructor(comparisonService) {
|
|
273
|
+
this.comparisonService = comparisonService;
|
|
274
|
+
}
|
|
275
|
+
onLeftFileSelected(event) {
|
|
276
|
+
const file = event.target.files[0];
|
|
277
|
+
if (!file)
|
|
278
|
+
return;
|
|
279
|
+
this.leftFile = file.name;
|
|
280
|
+
const reader = new FileReader();
|
|
281
|
+
reader.onload = (e) => {
|
|
282
|
+
const xmlContent = e.target.result;
|
|
283
|
+
this.leftEntities = this.comparisonService.parseEntities(xmlContent);
|
|
284
|
+
this.selectedLeftEntity = null;
|
|
285
|
+
this.comparisonResult = null;
|
|
286
|
+
};
|
|
287
|
+
reader.readAsText(file);
|
|
288
|
+
}
|
|
289
|
+
onRightFileSelected(event) {
|
|
290
|
+
const file = event.target.files[0];
|
|
291
|
+
if (!file)
|
|
292
|
+
return;
|
|
293
|
+
this.rightFile = file.name;
|
|
294
|
+
const reader = new FileReader();
|
|
295
|
+
reader.onload = (e) => {
|
|
296
|
+
const xmlContent = e.target.result;
|
|
297
|
+
this.rightEntities = this.comparisonService.parseEntities(xmlContent);
|
|
298
|
+
this.selectedRightEntity = null;
|
|
299
|
+
this.comparisonResult = null;
|
|
300
|
+
};
|
|
301
|
+
reader.readAsText(file);
|
|
302
|
+
}
|
|
303
|
+
selectLeftEntity(entity) {
|
|
304
|
+
this.selectedLeftEntity = entity;
|
|
305
|
+
this.compareIfBothSelected();
|
|
306
|
+
}
|
|
307
|
+
selectRightEntity(entity) {
|
|
308
|
+
this.selectedRightEntity = entity;
|
|
309
|
+
this.compareIfBothSelected();
|
|
310
|
+
}
|
|
311
|
+
compareIfBothSelected() {
|
|
312
|
+
if (this.selectedLeftEntity && this.selectedRightEntity) {
|
|
313
|
+
this.comparisonResult = this.comparisonService.compareStructures(this.selectedLeftEntity.structure, this.selectedRightEntity.structure);
|
|
314
|
+
this.buildDifferencesList();
|
|
315
|
+
this.currentDifferenceIndex = 0;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
buildDifferencesList() {
|
|
319
|
+
if (!this.comparisonResult) {
|
|
320
|
+
this.differences = [];
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
this.differences = [
|
|
324
|
+
...this.comparisonResult.leftOnly.map(node => ({ node, type: 'removed' })),
|
|
325
|
+
...this.comparisonResult.rightOnly.map(node => ({ node, type: 'added' })),
|
|
326
|
+
...this.comparisonResult.modified.map(node => ({ node, type: 'modified' }))
|
|
327
|
+
];
|
|
328
|
+
}
|
|
329
|
+
navigateToNextDifference() {
|
|
330
|
+
if (this.differences.length === 0)
|
|
331
|
+
return;
|
|
332
|
+
this.currentDifferenceIndex = (this.currentDifferenceIndex + 1) % this.differences.length;
|
|
333
|
+
this.scrollToDifference();
|
|
334
|
+
}
|
|
335
|
+
navigateToPreviousDifference() {
|
|
336
|
+
if (this.differences.length === 0)
|
|
337
|
+
return;
|
|
338
|
+
this.currentDifferenceIndex = this.currentDifferenceIndex === 0
|
|
339
|
+
? this.differences.length - 1
|
|
340
|
+
: this.currentDifferenceIndex - 1;
|
|
341
|
+
this.scrollToDifference();
|
|
342
|
+
}
|
|
343
|
+
scrollToDifference() {
|
|
344
|
+
if (this.differences.length === 0)
|
|
345
|
+
return;
|
|
346
|
+
const currentDiff = this.differences[this.currentDifferenceIndex];
|
|
347
|
+
const nodeId = `${currentDiff.node.Id}-${currentDiff.node.name}`;
|
|
348
|
+
// Expand parent nodes FIRST to make the difference visible
|
|
349
|
+
this.expandPathToNode(currentDiff.node);
|
|
350
|
+
// Wait for DOM to update after expansion, then scroll
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
const leftElement = document.querySelector(`[data-node-id="left-${nodeId}"]`);
|
|
353
|
+
const rightElement = document.querySelector(`[data-node-id="right-${nodeId}"]`);
|
|
354
|
+
if (leftElement) {
|
|
355
|
+
leftElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
356
|
+
}
|
|
357
|
+
if (rightElement) {
|
|
358
|
+
rightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
359
|
+
}
|
|
360
|
+
}, 100);
|
|
361
|
+
}
|
|
362
|
+
expandPathToNode(node) {
|
|
363
|
+
// Find and expand all parent nodes in both structures
|
|
364
|
+
this.expandParentsInStructure(this.selectedLeftEntity.structure, node, 'left');
|
|
365
|
+
this.expandParentsInStructure(this.selectedRightEntity.structure, node, 'right');
|
|
366
|
+
}
|
|
367
|
+
expandParentsInStructure(structure, targetNode, side) {
|
|
368
|
+
for (const node of structure) {
|
|
369
|
+
const nodeId = `${side}-${node.Id}-${node.name}`;
|
|
370
|
+
const targetId = `${targetNode.Id}-${targetNode.name}`;
|
|
371
|
+
const currentId = `${node.Id}-${node.name}`;
|
|
372
|
+
// If this is the target node, we found it
|
|
373
|
+
if (currentId === targetId) {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
// Check children if they exist
|
|
377
|
+
if (this.hasChildren(node)) {
|
|
378
|
+
const children = this.getChildren(node);
|
|
379
|
+
if (this.expandParentsInStructure(children, targetNode, side)) {
|
|
380
|
+
// If the target is in this branch, expand this node
|
|
381
|
+
this.expandedNodes.add(nodeId);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
get hasDifferences() {
|
|
389
|
+
return this.differences.length > 0;
|
|
390
|
+
}
|
|
391
|
+
get currentDifferenceNumber() {
|
|
392
|
+
return this.currentDifferenceIndex + 1;
|
|
393
|
+
}
|
|
394
|
+
get totalDifferences() {
|
|
395
|
+
return this.differences.length;
|
|
396
|
+
}
|
|
397
|
+
toggleNode(nodeId) {
|
|
398
|
+
if (this.expandedNodes.has(nodeId)) {
|
|
399
|
+
this.expandedNodes.delete(nodeId);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
this.expandedNodes.add(nodeId);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
isExpanded(nodeId) {
|
|
406
|
+
return this.expandedNodes.has(nodeId);
|
|
407
|
+
}
|
|
408
|
+
getNodeId(node, prefix) {
|
|
409
|
+
return `${prefix}-${node.Id}-${node.name}`;
|
|
410
|
+
}
|
|
411
|
+
hasChildren(node) {
|
|
412
|
+
return node.Children && JSON.parse(node.Children).length > 0;
|
|
413
|
+
}
|
|
414
|
+
getChildren(node) {
|
|
415
|
+
if (!node.Children)
|
|
416
|
+
return [];
|
|
417
|
+
try {
|
|
418
|
+
return JSON.parse(node.Children);
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
getProperties(node) {
|
|
425
|
+
if (!node.Properties)
|
|
426
|
+
return {};
|
|
427
|
+
try {
|
|
428
|
+
return JSON.parse(node.Properties);
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return {};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
getPropertyKeys(node) {
|
|
435
|
+
const props = this.getProperties(node);
|
|
436
|
+
return Object.keys(props);
|
|
437
|
+
}
|
|
438
|
+
getChangeType(node) {
|
|
439
|
+
if (!this.comparisonResult)
|
|
440
|
+
return '';
|
|
441
|
+
const nodeId = `${node.Id}-${node.name}`;
|
|
442
|
+
if (this.comparisonResult.leftOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
443
|
+
return 'removed';
|
|
444
|
+
}
|
|
445
|
+
if (this.comparisonResult.rightOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
446
|
+
return 'added';
|
|
447
|
+
}
|
|
448
|
+
if (this.comparisonResult.modified.some(n => `${n.Id}-${n.name}` === nodeId)) {
|
|
449
|
+
return 'modified';
|
|
450
|
+
}
|
|
451
|
+
return 'unchanged';
|
|
452
|
+
}
|
|
453
|
+
getModifiedProperties(node) {
|
|
454
|
+
if (!this.comparisonResult)
|
|
455
|
+
return [];
|
|
456
|
+
const nodeId = `${node.Id}-${node.name}`;
|
|
457
|
+
const modified = this.comparisonResult.modified.find(n => `${n.Id}-${n.name}` === nodeId);
|
|
458
|
+
return modified?.modifiedProperties || [];
|
|
459
|
+
}
|
|
460
|
+
toggleFullScreen() {
|
|
461
|
+
this.isFullScreen = !this.isFullScreen;
|
|
462
|
+
}
|
|
463
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonComponent, deps: [{ token: EntityComparisonService }], target: i0.ɵɵFactoryTarget.Component });
|
|
464
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: EntityComparisonComponent, isStandalone: true, selector: "app-entity-comparison", providers: [EntityComparisonService], ngImport: i0, template: "<div class=\"comparison-container\" [class.fullscreen]=\"isFullScreen\">\r\n <header class=\"header\">\r\n <h1>Entity Structure Comparison</h1>\r\n <p class=\"subtitle\">Compare EntityStructureJson between two entity exports</p>\r\n </header>\r\n\r\n <div class=\"file-selection\">\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label left\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onLeftFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Left Entity\r\n </label>\r\n <span *ngIf=\"leftFile\" class=\"file-name\">{{ leftFile }}</span>\r\n </div>\r\n\r\n <div class=\"vs-separator\">VS</div>\r\n\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label right\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onRightFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Right Entity\r\n </label>\r\n <span *ngIf=\"rightFile\" class=\"file-name\">{{ rightFile }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-selection\" *ngIf=\"leftEntities.length > 0 || rightEntities.length > 0\">\r\n <div class=\"entity-list\">\r\n <h3>Left Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of leftEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedLeftEntity === entity\" (click)=\"selectLeftEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-list\">\r\n <h3>Right Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of rightEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedRightEntity === entity\" (click)=\"selectRightEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary-section\" *ngIf=\"comparisonResult\">\r\n <div class=\"summary\">\r\n <div class=\"summary-item added\">\r\n <span class=\"count\">{{ comparisonResult.rightOnly.length }}</span>\r\n <span class=\"label\">Added</span>\r\n </div>\r\n <div class=\"summary-item removed\">\r\n <span class=\"count\">{{ comparisonResult.leftOnly.length }}</span>\r\n <span class=\"label\">Removed</span>\r\n </div>\r\n <div class=\"summary-item modified\">\r\n <span class=\"count\">{{ comparisonResult.modified.length }}</span>\r\n <span class=\"label\">Modified</span>\r\n </div>\r\n <div class=\"summary-item unchanged\">\r\n <span class=\"count\">{{ comparisonResult.unchanged.length }}</span>\r\n <span class=\"label\">Unchanged</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-results\" *ngIf=\"comparisonResult\">\r\n <div class=\"results-header\">\r\n <div class=\"header-title\">\r\n <h2>Comparison Results</h2>\r\n <div class=\"header-actions\">\r\n <div class=\"diff-navigation\" *ngIf=\"hasDifferences\">\r\n <button class=\"nav-btn\" (click)=\"navigateToPreviousDifference()\" title=\"Previous Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"18 15 12 9 6 15\" />\r\n </svg>\r\n </button>\r\n <span class=\"diff-counter\">{{ currentDifferenceNumber }} / {{ totalDifferences }}</span>\r\n <button class=\"nav-btn\" (click)=\"navigateToNextDifference()\" title=\"Next Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n </div>\r\n <button class=\"fullscreen-btn\" (click)=\"toggleFullScreen()\" [title]=\"isFullScreen ? 'Exit Full Screen' : 'Enter Full Screen'\">\r\n <svg *ngIf=\"!isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\r\n </svg>\r\n <svg *ngIf=\"isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"structure-comparison\">\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedLeftEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedLeftEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'left' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedRightEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedRightEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'right' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<ng-template #nodeTemplate let-node let-side=\"side\">\r\n <div class=\"tree-node\" [class]=\"getChangeType(node)\" [attr.data-node-id]=\"getNodeId(node, side)\">\r\n <div class=\"node-header\">\r\n <button *ngIf=\"hasChildren(node)\" class=\"expand-btn\" (click)=\"toggleNode(getNodeId(node, side))\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline *ngIf=\"!isExpanded(getNodeId(node, side))\" points=\"9 18 15 12 9 6\" />\r\n <polyline *ngIf=\"isExpanded(getNodeId(node, side))\" points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n <div *ngIf=\"!hasChildren(node)\" class=\"spacer\"></div>\r\n\r\n <div class=\"node-icon\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path *ngIf=\"node.nodeType === 'Group'\"\r\n d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n <circle *ngIf=\"node.nodeType === 'Attribute'\" cx=\"12\" cy=\"12\" r=\"10\" />\r\n </svg>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <span class=\"node-name\">{{ node.name }}</span>\r\n <span class=\"node-type\">{{ node.nodeType }}</span>\r\n <span class=\"change-badge\" *ngIf=\"getChangeType(node) !== 'unchanged'\">\r\n {{ getChangeType(node) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-properties\" *ngIf=\"getPropertyKeys(node).length > 0\">\r\n <div *ngFor=\"let key of getPropertyKeys(node)\" class=\"property\"\r\n [class.modified]=\"getModifiedProperties(node).includes(key)\">\r\n <span class=\"property-key\">{{ key }}:</span>\r\n <span class=\"property-value\">{{ getProperties(node)[key] }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-children\" *ngIf=\"hasChildren(node) && isExpanded(getNodeId(node, side))\">\r\n <ng-container *ngFor=\"let child of getChildren(node)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: child, side: side }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".comparison-container{display:flex;flex-direction:column;height:100%;width:100%;background:#f9fafb}.header{background:#fff;padding:1.5rem;border-bottom:1px solid #e5e7eb;box-shadow:0 1px 3px #0000001a}.header h1{margin:0;font-size:1.5rem;font-weight:700;color:#1f2937}.subtitle{margin:.5rem 0 0;font-size:.875rem;color:#6b7280}.file-selection{display:flex;align-items:center;gap:2rem;padding:1.5rem;background:#fff;border-bottom:1px solid #e5e7eb}.file-input-group{flex:1;display:flex;flex-direction:column;gap:.5rem}.file-label{display:inline-flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;border:2px solid #2563eb;border-radius:.5rem;cursor:pointer;font-weight:500;transition:all .2s;justify-content:center}.file-label.left{background:#fff;color:#2563eb}.file-label.left:hover{background:#eff6ff}.file-label.right{background:#fff;color:#7c3aed;border-color:#7c3aed}.file-label.right:hover{background:#f5f3ff}.file-name{font-size:.875rem;color:#6b7280;text-align:center}.vs-separator{padding:.75rem 1.5rem;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;font-weight:700;border-radius:2rem;font-size:1.25rem;box-shadow:0 4px 6px #0000001a}.entity-selection{display:flex;gap:1rem;padding:1rem;background:#fff;border-bottom:1px solid #e5e7eb}.entity-list{flex:1}.entity-list h3{margin:0 0 .75rem;font-size:.875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.entities{display:flex;flex-direction:column;gap:.5rem}.entity-card{display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1px solid #e5e7eb;border-radius:.5rem;cursor:pointer;transition:all .2s}.entity-card:hover{background:#f9fafb;border-color:#d1d5db}.entity-card.selected{background:#eff6ff;border-color:#2563eb}.entity-icon{color:#6b7280}.entity-card.selected .entity-icon{color:#2563eb}.entity-info{flex:1}.entity-name{font-size:.875rem;font-weight:500;color:#1f2937}.entity-id{font-size:.75rem;color:#6b7280}.summary-section{padding:1.5rem 1.5rem 0;background:#f9fafb}.comparison-results{flex:1;overflow:auto;padding:1.5rem}.results-header{margin-bottom:1.5rem;position:sticky;top:0;background:#f9fafb;z-index:100;padding-top:1rem;padding-bottom:1rem;margin-top:-1rem}.header-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}.results-header h2{margin:0;font-size:1.25rem;font-weight:700;color:#1f2937}.header-actions{display:flex;align-items:center;gap:1rem}.diff-navigation{display:flex;align-items:center;gap:.5rem;background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.25rem}.nav-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.375rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.nav-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.diff-counter{font-size:.875rem;font-weight:600;color:#1f2937;padding:0 .5rem;white-space:nowrap;min-width:60px;text-align:center}.fullscreen-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.fullscreen-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.summary{display:flex;gap:1rem}.summary-item{flex:1;display:flex;flex-direction:column;align-items:center;padding:1rem;border-radius:.5rem;background:#fff;border:2px solid}.summary-item.added{border-color:#10b981;background:#ecfdf5}.summary-item.removed{border-color:#ef4444;background:#fef2f2}.summary-item.modified{border-color:#f59e0b;background:#fffbeb}.summary-item.unchanged{border-color:#6b7280;background:#f9fafb}.summary-item .count{font-size:1.5rem;font-weight:700}.summary-item.added .count{color:#10b981}.summary-item.removed .count{color:#ef4444}.summary-item.modified .count{color:#f59e0b}.summary-item.unchanged .count{color:#6b7280}.summary-item .label{font-size:.75rem;font-weight:600;text-transform:uppercase;margin-top:.25rem}.structure-comparison{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;height:100%}.comparison-section{background:#fff;border-radius:.5rem;padding:1rem;border:1px solid #e5e7eb;min-width:0;width:100%;overflow:auto}.comparison-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1f2937;padding-bottom:.75rem;border-bottom:1px solid #e5e7eb}.structure-tree{display:flex;flex-direction:column;gap:.5rem}.tree-node{border-left:3px solid transparent;padding-left:.5rem;transition:all .2s;max-width:100%;overflow:hidden}.tree-node.added{background:#ecfdf5;border-left-color:#10b981}.tree-node.removed{background:#fef2f2;border-left-color:#ef4444}.tree-node.modified{background:#fffbeb;border-left-color:#f59e0b}.node-header{display:flex;align-items:center;gap:.5rem;padding:.5rem;border-radius:.375rem}.expand-btn{background:none;border:none;cursor:pointer;padding:.25rem;display:flex;align-items:center;justify-content:center;border-radius:.25rem;transition:background .2s}.expand-btn:hover{background:#f3f4f6}.spacer{width:24px}.node-icon{color:#6b7280}.node-content{display:flex;align-items:center;gap:.5rem;flex:1}.node-name{font-size:.875rem;font-weight:500;color:#1f2937}.node-type{font-size:.75rem;color:#6b7280;background:#f3f4f6;padding:.125rem .5rem;border-radius:.25rem}.change-badge{font-size:.625rem;font-weight:600;text-transform:uppercase;padding:.125rem .5rem;border-radius:.25rem;margin-left:auto}.tree-node.added .change-badge{background:#10b981;color:#fff}.tree-node.removed .change-badge{background:#ef4444;color:#fff}.tree-node.modified .change-badge{background:#f59e0b;color:#fff}.node-properties{margin-left:2.5rem;margin-top:.5rem;padding:.5rem;background:#f9fafb;border-radius:.375rem;font-size:.75rem;max-width:100%;overflow:hidden}.property{display:flex;flex-wrap:wrap;gap:.5rem;padding:.25rem 0;max-width:100%;overflow:hidden}.property.modified{background:#fef3c7;padding:.25rem .5rem;border-radius:.25rem;margin:.125rem 0}.property-key{font-weight:600;color:#374151;flex-shrink:0}.property-value{color:#6b7280;word-break:break-word;overflow:auto;overflow-wrap:break-word;flex:1;min-width:0}.node-children{margin-left:1.5rem;margin-top:.5rem;display:flex;flex-direction:column;gap:.5rem}.comparison-container.fullscreen{position:fixed;inset:0;z-index:9999;background:#f9fafb}.comparison-container.fullscreen .header,.comparison-container.fullscreen .file-selection,.comparison-container.fullscreen .entity-selection,.comparison-container.fullscreen .summary-section{display:none}.comparison-container.fullscreen .comparison-results{height:100vh;padding:2rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
465
|
+
}
|
|
466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EntityComparisonComponent, decorators: [{
|
|
467
|
+
type: Component,
|
|
468
|
+
args: [{ selector: 'app-entity-comparison', standalone: true, imports: [CommonModule], providers: [EntityComparisonService], template: "<div class=\"comparison-container\" [class.fullscreen]=\"isFullScreen\">\r\n <header class=\"header\">\r\n <h1>Entity Structure Comparison</h1>\r\n <p class=\"subtitle\">Compare EntityStructureJson between two entity exports</p>\r\n </header>\r\n\r\n <div class=\"file-selection\">\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label left\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onLeftFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Left Entity\r\n </label>\r\n <span *ngIf=\"leftFile\" class=\"file-name\">{{ leftFile }}</span>\r\n </div>\r\n\r\n <div class=\"vs-separator\">VS</div>\r\n\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label right\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onRightFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Right Entity\r\n </label>\r\n <span *ngIf=\"rightFile\" class=\"file-name\">{{ rightFile }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-selection\" *ngIf=\"leftEntities.length > 0 || rightEntities.length > 0\">\r\n <div class=\"entity-list\">\r\n <h3>Left Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of leftEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedLeftEntity === entity\" (click)=\"selectLeftEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-list\">\r\n <h3>Right Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of rightEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedRightEntity === entity\" (click)=\"selectRightEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary-section\" *ngIf=\"comparisonResult\">\r\n <div class=\"summary\">\r\n <div class=\"summary-item added\">\r\n <span class=\"count\">{{ comparisonResult.rightOnly.length }}</span>\r\n <span class=\"label\">Added</span>\r\n </div>\r\n <div class=\"summary-item removed\">\r\n <span class=\"count\">{{ comparisonResult.leftOnly.length }}</span>\r\n <span class=\"label\">Removed</span>\r\n </div>\r\n <div class=\"summary-item modified\">\r\n <span class=\"count\">{{ comparisonResult.modified.length }}</span>\r\n <span class=\"label\">Modified</span>\r\n </div>\r\n <div class=\"summary-item unchanged\">\r\n <span class=\"count\">{{ comparisonResult.unchanged.length }}</span>\r\n <span class=\"label\">Unchanged</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-results\" *ngIf=\"comparisonResult\">\r\n <div class=\"results-header\">\r\n <div class=\"header-title\">\r\n <h2>Comparison Results</h2>\r\n <div class=\"header-actions\">\r\n <div class=\"diff-navigation\" *ngIf=\"hasDifferences\">\r\n <button class=\"nav-btn\" (click)=\"navigateToPreviousDifference()\" title=\"Previous Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"18 15 12 9 6 15\" />\r\n </svg>\r\n </button>\r\n <span class=\"diff-counter\">{{ currentDifferenceNumber }} / {{ totalDifferences }}</span>\r\n <button class=\"nav-btn\" (click)=\"navigateToNextDifference()\" title=\"Next Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n </div>\r\n <button class=\"fullscreen-btn\" (click)=\"toggleFullScreen()\" [title]=\"isFullScreen ? 'Exit Full Screen' : 'Enter Full Screen'\">\r\n <svg *ngIf=\"!isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\r\n </svg>\r\n <svg *ngIf=\"isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"structure-comparison\">\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedLeftEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedLeftEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'left' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedRightEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedRightEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'right' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<ng-template #nodeTemplate let-node let-side=\"side\">\r\n <div class=\"tree-node\" [class]=\"getChangeType(node)\" [attr.data-node-id]=\"getNodeId(node, side)\">\r\n <div class=\"node-header\">\r\n <button *ngIf=\"hasChildren(node)\" class=\"expand-btn\" (click)=\"toggleNode(getNodeId(node, side))\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline *ngIf=\"!isExpanded(getNodeId(node, side))\" points=\"9 18 15 12 9 6\" />\r\n <polyline *ngIf=\"isExpanded(getNodeId(node, side))\" points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n <div *ngIf=\"!hasChildren(node)\" class=\"spacer\"></div>\r\n\r\n <div class=\"node-icon\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path *ngIf=\"node.nodeType === 'Group'\"\r\n d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n <circle *ngIf=\"node.nodeType === 'Attribute'\" cx=\"12\" cy=\"12\" r=\"10\" />\r\n </svg>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <span class=\"node-name\">{{ node.name }}</span>\r\n <span class=\"node-type\">{{ node.nodeType }}</span>\r\n <span class=\"change-badge\" *ngIf=\"getChangeType(node) !== 'unchanged'\">\r\n {{ getChangeType(node) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-properties\" *ngIf=\"getPropertyKeys(node).length > 0\">\r\n <div *ngFor=\"let key of getPropertyKeys(node)\" class=\"property\"\r\n [class.modified]=\"getModifiedProperties(node).includes(key)\">\r\n <span class=\"property-key\">{{ key }}:</span>\r\n <span class=\"property-value\">{{ getProperties(node)[key] }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-children\" *ngIf=\"hasChildren(node) && isExpanded(getNodeId(node, side))\">\r\n <ng-container *ngFor=\"let child of getChildren(node)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: child, side: side }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>", styles: [".comparison-container{display:flex;flex-direction:column;height:100%;width:100%;background:#f9fafb}.header{background:#fff;padding:1.5rem;border-bottom:1px solid #e5e7eb;box-shadow:0 1px 3px #0000001a}.header h1{margin:0;font-size:1.5rem;font-weight:700;color:#1f2937}.subtitle{margin:.5rem 0 0;font-size:.875rem;color:#6b7280}.file-selection{display:flex;align-items:center;gap:2rem;padding:1.5rem;background:#fff;border-bottom:1px solid #e5e7eb}.file-input-group{flex:1;display:flex;flex-direction:column;gap:.5rem}.file-label{display:inline-flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;border:2px solid #2563eb;border-radius:.5rem;cursor:pointer;font-weight:500;transition:all .2s;justify-content:center}.file-label.left{background:#fff;color:#2563eb}.file-label.left:hover{background:#eff6ff}.file-label.right{background:#fff;color:#7c3aed;border-color:#7c3aed}.file-label.right:hover{background:#f5f3ff}.file-name{font-size:.875rem;color:#6b7280;text-align:center}.vs-separator{padding:.75rem 1.5rem;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;font-weight:700;border-radius:2rem;font-size:1.25rem;box-shadow:0 4px 6px #0000001a}.entity-selection{display:flex;gap:1rem;padding:1rem;background:#fff;border-bottom:1px solid #e5e7eb}.entity-list{flex:1}.entity-list h3{margin:0 0 .75rem;font-size:.875rem;font-weight:600;color:#6b7280;text-transform:uppercase}.entities{display:flex;flex-direction:column;gap:.5rem}.entity-card{display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1px solid #e5e7eb;border-radius:.5rem;cursor:pointer;transition:all .2s}.entity-card:hover{background:#f9fafb;border-color:#d1d5db}.entity-card.selected{background:#eff6ff;border-color:#2563eb}.entity-icon{color:#6b7280}.entity-card.selected .entity-icon{color:#2563eb}.entity-info{flex:1}.entity-name{font-size:.875rem;font-weight:500;color:#1f2937}.entity-id{font-size:.75rem;color:#6b7280}.summary-section{padding:1.5rem 1.5rem 0;background:#f9fafb}.comparison-results{flex:1;overflow:auto;padding:1.5rem}.results-header{margin-bottom:1.5rem;position:sticky;top:0;background:#f9fafb;z-index:100;padding-top:1rem;padding-bottom:1rem;margin-top:-1rem}.header-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}.results-header h2{margin:0;font-size:1.25rem;font-weight:700;color:#1f2937}.header-actions{display:flex;align-items:center;gap:1rem}.diff-navigation{display:flex;align-items:center;gap:.5rem;background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.25rem}.nav-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.375rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.nav-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.diff-counter{font-size:.875rem;font-weight:600;color:#1f2937;padding:0 .5rem;white-space:nowrap;min-width:60px;text-align:center}.fullscreen-btn{background:#fff;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;color:#6b7280}.fullscreen-btn:hover{background:#f9fafb;border-color:#2563eb;color:#2563eb}.summary{display:flex;gap:1rem}.summary-item{flex:1;display:flex;flex-direction:column;align-items:center;padding:1rem;border-radius:.5rem;background:#fff;border:2px solid}.summary-item.added{border-color:#10b981;background:#ecfdf5}.summary-item.removed{border-color:#ef4444;background:#fef2f2}.summary-item.modified{border-color:#f59e0b;background:#fffbeb}.summary-item.unchanged{border-color:#6b7280;background:#f9fafb}.summary-item .count{font-size:1.5rem;font-weight:700}.summary-item.added .count{color:#10b981}.summary-item.removed .count{color:#ef4444}.summary-item.modified .count{color:#f59e0b}.summary-item.unchanged .count{color:#6b7280}.summary-item .label{font-size:.75rem;font-weight:600;text-transform:uppercase;margin-top:.25rem}.structure-comparison{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;height:100%}.comparison-section{background:#fff;border-radius:.5rem;padding:1rem;border:1px solid #e5e7eb;min-width:0;width:100%;overflow:auto}.comparison-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1f2937;padding-bottom:.75rem;border-bottom:1px solid #e5e7eb}.structure-tree{display:flex;flex-direction:column;gap:.5rem}.tree-node{border-left:3px solid transparent;padding-left:.5rem;transition:all .2s;max-width:100%;overflow:hidden}.tree-node.added{background:#ecfdf5;border-left-color:#10b981}.tree-node.removed{background:#fef2f2;border-left-color:#ef4444}.tree-node.modified{background:#fffbeb;border-left-color:#f59e0b}.node-header{display:flex;align-items:center;gap:.5rem;padding:.5rem;border-radius:.375rem}.expand-btn{background:none;border:none;cursor:pointer;padding:.25rem;display:flex;align-items:center;justify-content:center;border-radius:.25rem;transition:background .2s}.expand-btn:hover{background:#f3f4f6}.spacer{width:24px}.node-icon{color:#6b7280}.node-content{display:flex;align-items:center;gap:.5rem;flex:1}.node-name{font-size:.875rem;font-weight:500;color:#1f2937}.node-type{font-size:.75rem;color:#6b7280;background:#f3f4f6;padding:.125rem .5rem;border-radius:.25rem}.change-badge{font-size:.625rem;font-weight:600;text-transform:uppercase;padding:.125rem .5rem;border-radius:.25rem;margin-left:auto}.tree-node.added .change-badge{background:#10b981;color:#fff}.tree-node.removed .change-badge{background:#ef4444;color:#fff}.tree-node.modified .change-badge{background:#f59e0b;color:#fff}.node-properties{margin-left:2.5rem;margin-top:.5rem;padding:.5rem;background:#f9fafb;border-radius:.375rem;font-size:.75rem;max-width:100%;overflow:hidden}.property{display:flex;flex-wrap:wrap;gap:.5rem;padding:.25rem 0;max-width:100%;overflow:hidden}.property.modified{background:#fef3c7;padding:.25rem .5rem;border-radius:.25rem;margin:.125rem 0}.property-key{font-weight:600;color:#374151;flex-shrink:0}.property-value{color:#6b7280;word-break:break-word;overflow:auto;overflow-wrap:break-word;flex:1;min-width:0}.node-children{margin-left:1.5rem;margin-top:.5rem;display:flex;flex-direction:column;gap:.5rem}.comparison-container.fullscreen{position:fixed;inset:0;z-index:9999;background:#f9fafb}.comparison-container.fullscreen .header,.comparison-container.fullscreen .file-selection,.comparison-container.fullscreen .entity-selection,.comparison-container.fullscreen .summary-section{display:none}.comparison-container.fullscreen .comparison-results{height:100vh;padding:2rem}\n"] }]
|
|
469
|
+
}], ctorParameters: () => [{ type: EntityComparisonService }] });
|
|
470
|
+
|
|
133
471
|
/*
|
|
134
472
|
* Public API Surface of concepto-message
|
|
135
473
|
*/
|
|
@@ -138,5 +476,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
138
476
|
* Generated bundle index. Do not edit.
|
|
139
477
|
*/
|
|
140
478
|
|
|
141
|
-
export { ConceptoContextMenuComponent, ConceptoMessageComponent, ConceptoUserControls, ConceptoUserControlsService };
|
|
479
|
+
export { ConceptoContextMenuComponent, ConceptoMessageComponent, ConceptoTreeComponent, ConceptoUserControls, ConceptoUserControlsService, EntityComparisonComponent, EntityComparisonService };
|
|
142
480
|
//# sourceMappingURL=concepto-user-controls.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concepto-user-controls.mjs","sources":["../../../projects/concepto-user-controls/src/lib/concepto-user-controls.service.ts","../../../projects/concepto-user-controls/src/lib/concepto-user-controls.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-message/concepto-message.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-message/concepto-message.component.html","../../../projects/concepto-user-controls/src/lib/concepto-context-menu/concepto-context-menu.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-context-menu/concepto-context-menu.component.html","../../../projects/concepto-user-controls/src/public-api.ts","../../../projects/concepto-user-controls/src/concepto-user-controls.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class ConceptoUserControlsService {\r\n\r\n constructor() { }\r\n}\r\n","import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'lib-concepto-message',\r\n standalone: true,\r\n imports: [],\r\n template: `\r\n <p>\r\n concepto-message works!\r\n </p>\r\n `,\r\n styles: ``\r\n})\r\nexport class ConceptoUserControls {\r\n\r\n}\r\n","import { NgIf } from '@angular/common';\r\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\r\n\r\n\r\n@Component({\r\n selector: 'concepto-message',\r\n standalone: true,\r\n imports: [NgIf],\r\n templateUrl: './concepto-message.component.html',\r\n styleUrl: './concepto-message.component.css'\r\n})\r\nexport class ConceptoMessageComponent {\r\n @Input() title: string = 'Mensaje';\r\n @Input() message: string = 'Este es un mensaje';\r\n @Input() visible: boolean = false;\r\n\r\n @Output() closed = new EventEmitter<void>();\r\n @Output() accepted = new EventEmitter<void>();\r\n\r\n close() {\r\n this.closed.emit();\r\n }\r\n\r\n accept() {\r\n this.accepted.emit();\r\n }\r\n\r\n show() {\r\n this.visible = true;\r\n }\r\n}\r\n","<div class=\"modal-backdrop\" *ngIf=\"visible\">\r\n <div class=\"modal\">\r\n <div class=\"modal-header\">\r\n <h3>{{ title }}</h3>\r\n <button class=\"close-button\" (click)=\"close()\">×</button>\r\n </div>\r\n <div class=\"modal-body\">\r\n <p>{{ message }}</p>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button (click)=\"accept()\">Aceptar</button>\r\n </div>\r\n </div>\r\n</div>","import { Component, ElementRef, HostListener, Input } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\n\r\n\r\nexport interface NodeOption {\r\n NodesId: string;\r\n NodesText: string;\r\n NodesParentId: string;\r\n NodesImg: string;\r\n children?: NodeOption[];\r\n}\r\n\r\n@Component({\r\n selector: 'concepto-context-menu',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './concepto-context-menu.component.html',\r\n styleUrl: './concepto-context-menu.component.css'\r\n})\r\nexport class ConceptoContextMenuComponent {\r\n\r\n @Input() nodes: NodeOption[] = [];\r\n menuTree: NodeOption[] = [];\r\n visible = false;\r\n pos = { x: 0, y: 0 };\r\n\r\n constructor(private elRef: ElementRef) {}\r\n\r\n ngOnChanges(): void {\r\n const map = new Map<string, NodeOption>();\r\n this.nodes.forEach(n => map.set(n.NodesId, { ...n, children: [] }));\r\n\r\n const roots: NodeOption[] = [];\r\n map.forEach(n => {\r\n if (n.NodesParentId && map.has(n.NodesParentId)) {\r\n map.get(n.NodesParentId)?.children?.push(n);\r\n } else {\r\n roots.push(n);\r\n }\r\n });\r\n this.menuTree = roots;\r\n }\r\n\r\n showAtEvent(event: MouseEvent): void {\r\n event.preventDefault();\r\n const x = event.clientX;\r\n const y = event.clientY;\r\n this.visible = true;\r\n setTimeout(() => {\r\n const menu = this.elRef.nativeElement.querySelector('.context-menu');\r\n const rect = menu.getBoundingClientRect();\r\n this.pos.x = x + rect.width > window.innerWidth ? window.innerWidth - rect.width - 10 : x;\r\n this.pos.y = y + rect.height > window.innerHeight ? window.innerHeight - rect.height - 10 : y;\r\n });\r\n }\r\n\r\n hide(): void {\r\n this.visible = false;\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n if (!this.elRef.nativeElement.contains(event.target)) {\r\n this.hide();\r\n }\r\n }\r\n\r\n onOptionClick(option: NodeOption): void {\r\n if (!option.children || option.children.length === 0) {\r\n console.log('Selected:', option);\r\n this.hide();\r\n }\r\n }\r\n\r\n hasChildren(option: NodeOption): boolean {\r\n return !!option.children && option.children.length > 0;\r\n }\r\n}\r\n\r\n","<div class=\"context-menu\" *ngIf=\"visible\" [ngStyle]=\"{ top: pos.y + 'px', left: pos.x + 'px' }\">\r\n <ul class=\"menu-root\">\r\n <ng-container *ngFor=\"let item of menuTree\">\r\n <ng-container *ngTemplateOutlet=\"renderNode; context: { $implicit: item }\"></ng-container>\r\n </ng-container>\r\n </ul>\r\n</div>\r\n\r\n<ng-template #renderNode let-node>\r\n <li class=\"menu-item\" [class.has-children]=\"hasChildren(node)\">\r\n <div (click)=\"onOptionClick(node)\">\r\n @if (node.NodesImg) {\r\n <img [src]=\"node.NodesImg\" class=\"icon\" />\r\n }\r\n @if (!node.NodesImg) {\r\n <div class=\"icon\"></div>\r\n }\r\n {{ node.NodesText }}\r\n </div>\r\n <ul class=\"submenu\" *ngIf=\"hasChildren(node)\">\r\n <ng-container *ngFor=\"let child of node.children\">\r\n <ng-container *ngTemplateOutlet=\"renderNode; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n</ng-template>","/*\r\n * Public API Surface of concepto-message\r\n */\r\n\r\nexport * from './lib/concepto-user-controls.service';\r\nexport * from './lib/concepto-user-controls.component';\r\nexport * from './lib/concepto-message/concepto-message.component';\r\nexport * from './lib/concepto-context-menu/concepto-context-menu.component';","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"concepto-user-controls.mjs","sources":["../../../projects/concepto-user-controls/src/lib/concepto-user-controls.service.ts","../../../projects/concepto-user-controls/src/lib/concepto-user-controls.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-message/concepto-message.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-message/concepto-message.component.html","../../../projects/concepto-user-controls/src/lib/concepto-context-menu/concepto-context-menu.component.ts","../../../projects/concepto-user-controls/src/lib/concepto-context-menu/concepto-context-menu.component.html","../../../projects/concepto-user-controls/src/lib/concepto-tree/concepto-tree.component.ts","../../../projects/concepto-user-controls/src/lib/entity-comparison/core/services/entity-comparison.service.ts","../../../projects/concepto-user-controls/src/lib/entity-comparison/components/entity-comparison.component.ts","../../../projects/concepto-user-controls/src/lib/entity-comparison/components/entity-comparison.component.html","../../../projects/concepto-user-controls/src/public-api.ts","../../../projects/concepto-user-controls/src/concepto-user-controls.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class ConceptoUserControlsService {\r\n\r\n constructor() { }\r\n}\r\n","import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'lib-concepto-message',\r\n standalone: true,\r\n imports: [],\r\n template: `\r\n <p>\r\n concepto-message works!\r\n </p>\r\n `,\r\n styles: ``\r\n})\r\nexport class ConceptoUserControls {\r\n\r\n}\r\n","import { NgIf } from '@angular/common';\r\nimport { Component, Input, Output, EventEmitter } from '@angular/core';\r\n\r\n\r\n@Component({\r\n selector: 'concepto-message',\r\n standalone: true,\r\n imports: [NgIf],\r\n templateUrl: './concepto-message.component.html',\r\n styleUrl: './concepto-message.component.css'\r\n})\r\nexport class ConceptoMessageComponent {\r\n @Input() title: string = 'Mensaje';\r\n @Input() message: string = 'Este es un mensaje';\r\n @Input() visible: boolean = false;\r\n\r\n @Output() closed = new EventEmitter<void>();\r\n @Output() accepted = new EventEmitter<void>();\r\n\r\n close() {\r\n this.closed.emit();\r\n }\r\n\r\n accept() {\r\n this.accepted.emit();\r\n }\r\n\r\n show() {\r\n this.visible = true;\r\n }\r\n}\r\n","<div class=\"modal-backdrop\" *ngIf=\"visible\">\r\n <div class=\"modal\">\r\n <div class=\"modal-header\">\r\n <h3>{{ title }}</h3>\r\n <button class=\"close-button\" (click)=\"close()\">×</button>\r\n </div>\r\n <div class=\"modal-body\">\r\n <p>{{ message }}</p>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button (click)=\"accept()\">Aceptar</button>\r\n </div>\r\n </div>\r\n</div>","import { Component, ElementRef, HostListener, Input } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\n\r\n\r\nexport interface NodeOption {\r\n NodesId: string;\r\n NodesText: string;\r\n NodesParentId: string;\r\n NodesImg: string;\r\n children?: NodeOption[];\r\n}\r\n\r\n@Component({\r\n selector: 'concepto-context-menu',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './concepto-context-menu.component.html',\r\n styleUrl: './concepto-context-menu.component.css'\r\n})\r\nexport class ConceptoContextMenuComponent {\r\n\r\n @Input() nodes: NodeOption[] = [];\r\n menuTree: NodeOption[] = [];\r\n visible = false;\r\n pos = { x: 0, y: 0 };\r\n\r\n constructor(private elRef: ElementRef) {}\r\n\r\n ngOnChanges(): void {\r\n const map = new Map<string, NodeOption>();\r\n this.nodes.forEach(n => map.set(n.NodesId, { ...n, children: [] }));\r\n\r\n const roots: NodeOption[] = [];\r\n map.forEach(n => {\r\n if (n.NodesParentId && map.has(n.NodesParentId)) {\r\n map.get(n.NodesParentId)?.children?.push(n);\r\n } else {\r\n roots.push(n);\r\n }\r\n });\r\n this.menuTree = roots;\r\n }\r\n\r\n showAtEvent(event: MouseEvent): void {\r\n event.preventDefault();\r\n const x = event.clientX;\r\n const y = event.clientY;\r\n this.visible = true;\r\n setTimeout(() => {\r\n const menu = this.elRef.nativeElement.querySelector('.context-menu');\r\n const rect = menu.getBoundingClientRect();\r\n this.pos.x = x + rect.width > window.innerWidth ? window.innerWidth - rect.width - 10 : x;\r\n this.pos.y = y + rect.height > window.innerHeight ? window.innerHeight - rect.height - 10 : y;\r\n });\r\n }\r\n\r\n hide(): void {\r\n this.visible = false;\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n if (!this.elRef.nativeElement.contains(event.target)) {\r\n this.hide();\r\n }\r\n }\r\n\r\n onOptionClick(option: NodeOption): void {\r\n if (!option.children || option.children.length === 0) {\r\n console.log('Selected:', option);\r\n this.hide();\r\n }\r\n }\r\n\r\n hasChildren(option: NodeOption): boolean {\r\n return !!option.children && option.children.length > 0;\r\n }\r\n}\r\n\r\n","<div class=\"context-menu\" *ngIf=\"visible\" [ngStyle]=\"{ top: pos.y + 'px', left: pos.x + 'px' }\">\r\n <ul class=\"menu-root\">\r\n <ng-container *ngFor=\"let item of menuTree\">\r\n <ng-container *ngTemplateOutlet=\"renderNode; context: { $implicit: item }\"></ng-container>\r\n </ng-container>\r\n </ul>\r\n</div>\r\n\r\n<ng-template #renderNode let-node>\r\n <li class=\"menu-item\" [class.has-children]=\"hasChildren(node)\">\r\n <div (click)=\"onOptionClick(node)\">\r\n @if (node.NodesImg) {\r\n <img [src]=\"node.NodesImg\" class=\"icon\" />\r\n }\r\n @if (!node.NodesImg) {\r\n <div class=\"icon\"></div>\r\n }\r\n {{ node.NodesText }}\r\n </div>\r\n <ul class=\"submenu\" *ngIf=\"hasChildren(node)\">\r\n <ng-container *ngFor=\"let child of node.children\">\r\n <ng-container *ngTemplateOutlet=\"renderNode; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n</ng-template>","import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'lib-concepto-tree',\r\n standalone: true,\r\n imports: [],\r\n template: `\r\n <p>\r\n concepto-tree works!\r\n </p>\r\n `,\r\n styles: ``\r\n})\r\nexport class ConceptoTreeComponent {\r\n\r\n}\r\n","import { Injectable } from '@angular/core';\r\nimport { ComparisonResult } from '../../components/entity-comparison.component';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class EntityComparisonService {\r\n\r\n parseEntities(xmlString: string): any[] {\r\n const parser = new DOMParser();\r\n const xmlDoc = parser.parseFromString(xmlString, 'text/xml');\r\n \r\n const exportItems = xmlDoc.querySelectorAll('ExportItem[Type=\"Entity\"]');\r\n const entities: any[] = [];\r\n \r\n exportItems.forEach((item) => {\r\n const entity = item.querySelector('Entity_WithoutRedundancies');\r\n if (!entity) return;\r\n \r\n const structureJson = entity.querySelector('EntityStructureJson')?.textContent;\r\n if (!structureJson) return;\r\n \r\n try {\r\n const structure = JSON.parse(structureJson);\r\n entities.push({\r\n id: entity.querySelector('EntityId')?.textContent || '',\r\n name: entity.querySelector('EntityName')?.textContent || '',\r\n structure: structure\r\n });\r\n } catch (e) {\r\n console.error('Error parsing EntityStructureJson:', e);\r\n }\r\n });\r\n \r\n return entities;\r\n }\r\n\r\n compareStructures(leftStructure: any[], rightStructure: any[]): ComparisonResult {\r\n const leftOnly: any[] = [];\r\n const rightOnly: any[] = [];\r\n const modified: any[] = [];\r\n const unchanged: any[] = [];\r\n \r\n // Create maps for quick lookup\r\n const leftMap = this.createNodeMap(leftStructure);\r\n const rightMap = this.createNodeMap(rightStructure);\r\n \r\n // Find nodes only in left or modified\r\n for (const [key, leftNode] of leftMap.entries()) {\r\n if (!rightMap.has(key)) {\r\n leftOnly.push(leftNode);\r\n } else {\r\n const rightNode = rightMap.get(key);\r\n const diff = this.compareNodes(leftNode, rightNode!);\r\n if (diff.length > 0) {\r\n modified.push({ ...leftNode, modifiedProperties: diff });\r\n } else {\r\n unchanged.push(leftNode);\r\n }\r\n }\r\n }\r\n \r\n // Find nodes only in right\r\n for (const [key, rightNode] of rightMap.entries()) {\r\n if (!leftMap.has(key)) {\r\n rightOnly.push(rightNode);\r\n }\r\n }\r\n \r\n return { leftOnly, rightOnly, modified, unchanged };\r\n }\r\n\r\n private createNodeMap(structure: any[], parentPath: string = ''): Map<string, any> {\r\n const map = new Map<string, any>();\r\n \r\n for (const node of structure) {\r\n const nodePath = parentPath ? `${parentPath}/${node.Id}-${node.name}` : `${node.Id}-${node.name}`;\r\n map.set(nodePath, node);\r\n \r\n if (node.Children) {\r\n try {\r\n const children = JSON.parse(node.Children);\r\n const childMap = this.createNodeMap(children, nodePath);\r\n for (const [key, value] of childMap.entries()) {\r\n map.set(key, value);\r\n }\r\n } catch (e) {\r\n // Ignore parsing errors\r\n }\r\n }\r\n }\r\n \r\n return map;\r\n }\r\n\r\n private compareNodes(left: any, right: any): string[] {\r\n const modifiedProps: string[] = [];\r\n \r\n try {\r\n const leftProps = JSON.parse(left.Properties || '{}');\r\n const rightProps = JSON.parse(right.Properties || '{}');\r\n \r\n const allKeys = new Set([\r\n ...Object.keys(leftProps),\r\n ...Object.keys(rightProps)\r\n ]);\r\n \r\n for (const key of allKeys) {\r\n if (leftProps[key] !== rightProps[key]) {\r\n modifiedProps.push(key);\r\n }\r\n }\r\n } catch (e) {\r\n // Ignore parsing errors\r\n }\r\n \r\n return modifiedProps;\r\n }\r\n}","import { Component } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { EntityComparisonService } from '../core/services/entity-comparison.service';\r\n\r\nexport interface ComparisonResult {\r\n leftOnly: any[];\r\n rightOnly: any[];\r\n modified: any[];\r\n unchanged: any[];\r\n}\r\n\r\n@Component({\r\n selector: 'app-entity-comparison',\r\n standalone: true,\r\n imports: [CommonModule],\r\n providers: [EntityComparisonService],\r\n templateUrl: './entity-comparison.component.html',\r\n styleUrls: ['./entity-comparison.component.css']\r\n})\r\nexport class EntityComparisonComponent {\r\n leftFile: string = '';\r\n rightFile: string = '';\r\n leftEntities: any[] = [];\r\n rightEntities: any[] = [];\r\n selectedLeftEntity: any = null;\r\n selectedRightEntity: any = null;\r\n comparisonResult: ComparisonResult | null = null;\r\n expandedNodes = new Set<string>();\r\n isFullScreen = false;\r\n currentDifferenceIndex = 0;\r\n differences: any[] = [];\r\n\r\n constructor(private comparisonService: EntityComparisonService) {}\r\n\r\n onLeftFileSelected(event: any): void {\r\n const file: File = event.target.files[0];\r\n if (!file) return;\r\n\r\n this.leftFile = file.name;\r\n const reader = new FileReader();\r\n \r\n reader.onload = (e: any) => {\r\n const xmlContent = e.target.result;\r\n this.leftEntities = this.comparisonService.parseEntities(xmlContent);\r\n this.selectedLeftEntity = null;\r\n this.comparisonResult = null;\r\n };\r\n \r\n reader.readAsText(file);\r\n }\r\n\r\n onRightFileSelected(event: any): void {\r\n const file: File = event.target.files[0];\r\n if (!file) return;\r\n\r\n this.rightFile = file.name;\r\n const reader = new FileReader();\r\n \r\n reader.onload = (e: any) => {\r\n const xmlContent = e.target.result;\r\n this.rightEntities = this.comparisonService.parseEntities(xmlContent);\r\n this.selectedRightEntity = null;\r\n this.comparisonResult = null;\r\n };\r\n \r\n reader.readAsText(file);\r\n }\r\n\r\n selectLeftEntity(entity: any): void {\r\n this.selectedLeftEntity = entity;\r\n this.compareIfBothSelected();\r\n }\r\n\r\n selectRightEntity(entity: any): void {\r\n this.selectedRightEntity = entity;\r\n this.compareIfBothSelected();\r\n }\r\n\r\n private compareIfBothSelected(): void {\r\n if (this.selectedLeftEntity && this.selectedRightEntity) {\r\n this.comparisonResult = this.comparisonService.compareStructures(\r\n this.selectedLeftEntity.structure,\r\n this.selectedRightEntity.structure\r\n );\r\n this.buildDifferencesList();\r\n this.currentDifferenceIndex = 0;\r\n }\r\n }\r\n\r\n private buildDifferencesList(): void {\r\n if (!this.comparisonResult) {\r\n this.differences = [];\r\n return;\r\n }\r\n\r\n this.differences = [\r\n ...this.comparisonResult.leftOnly.map(node => ({ node, type: 'removed' })),\r\n ...this.comparisonResult.rightOnly.map(node => ({ node, type: 'added' })),\r\n ...this.comparisonResult.modified.map(node => ({ node, type: 'modified' }))\r\n ];\r\n }\r\n\r\n navigateToNextDifference(): void {\r\n if (this.differences.length === 0) return;\r\n\r\n this.currentDifferenceIndex = (this.currentDifferenceIndex + 1) % this.differences.length;\r\n this.scrollToDifference();\r\n }\r\n\r\n navigateToPreviousDifference(): void {\r\n if (this.differences.length === 0) return;\r\n\r\n this.currentDifferenceIndex = this.currentDifferenceIndex === 0\r\n ? this.differences.length - 1\r\n : this.currentDifferenceIndex - 1;\r\n this.scrollToDifference();\r\n }\r\n\r\n private scrollToDifference(): void {\r\n if (this.differences.length === 0) return;\r\n\r\n const currentDiff = this.differences[this.currentDifferenceIndex];\r\n const nodeId = `${currentDiff.node.Id}-${currentDiff.node.name}`;\r\n\r\n // Expand parent nodes FIRST to make the difference visible\r\n this.expandPathToNode(currentDiff.node);\r\n\r\n // Wait for DOM to update after expansion, then scroll\r\n setTimeout(() => {\r\n const leftElement = document.querySelector(`[data-node-id=\"left-${nodeId}\"]`);\r\n const rightElement = document.querySelector(`[data-node-id=\"right-${nodeId}\"]`);\r\n\r\n if (leftElement) {\r\n leftElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\r\n }\r\n if (rightElement) {\r\n rightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\r\n }\r\n }, 100);\r\n }\r\n\r\n private expandPathToNode(node: any): void {\r\n // Find and expand all parent nodes in both structures\r\n this.expandParentsInStructure(this.selectedLeftEntity.structure, node, 'left');\r\n this.expandParentsInStructure(this.selectedRightEntity.structure, node, 'right');\r\n }\r\n\r\n private expandParentsInStructure(structure: any[], targetNode: any, side: string): boolean {\r\n for (const node of structure) {\r\n const nodeId = `${side}-${node.Id}-${node.name}`;\r\n const targetId = `${targetNode.Id}-${targetNode.name}`;\r\n const currentId = `${node.Id}-${node.name}`;\r\n\r\n // If this is the target node, we found it\r\n if (currentId === targetId) {\r\n return true;\r\n }\r\n\r\n // Check children if they exist\r\n if (this.hasChildren(node)) {\r\n const children = this.getChildren(node);\r\n if (this.expandParentsInStructure(children, targetNode, side)) {\r\n // If the target is in this branch, expand this node\r\n this.expandedNodes.add(nodeId);\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n get hasDifferences(): boolean {\r\n return this.differences.length > 0;\r\n }\r\n\r\n get currentDifferenceNumber(): number {\r\n return this.currentDifferenceIndex + 1;\r\n }\r\n\r\n get totalDifferences(): number {\r\n return this.differences.length;\r\n }\r\n\r\n toggleNode(nodeId: string): void {\r\n if (this.expandedNodes.has(nodeId)) {\r\n this.expandedNodes.delete(nodeId);\r\n } else {\r\n this.expandedNodes.add(nodeId);\r\n }\r\n }\r\n\r\n isExpanded(nodeId: string): boolean {\r\n return this.expandedNodes.has(nodeId);\r\n }\r\n\r\n getNodeId(node: any, prefix: string): string {\r\n return `${prefix}-${node.Id}-${node.name}`;\r\n }\r\n\r\n hasChildren(node: any): boolean {\r\n return node.Children && JSON.parse(node.Children).length > 0;\r\n }\r\n\r\n getChildren(node: any): any[] {\r\n if (!node.Children) return [];\r\n try {\r\n return JSON.parse(node.Children);\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n getProperties(node: any): any {\r\n if (!node.Properties) return {};\r\n try {\r\n return JSON.parse(node.Properties);\r\n } catch {\r\n return {};\r\n }\r\n }\r\n\r\n getPropertyKeys(node: any): string[] {\r\n const props = this.getProperties(node);\r\n return Object.keys(props);\r\n }\r\n\r\n getChangeType(node: any): string {\r\n if (!this.comparisonResult) return '';\r\n \r\n const nodeId = `${node.Id}-${node.name}`;\r\n \r\n if (this.comparisonResult.leftOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {\r\n return 'removed';\r\n }\r\n if (this.comparisonResult.rightOnly.some(n => `${n.Id}-${n.name}` === nodeId)) {\r\n return 'added';\r\n }\r\n if (this.comparisonResult.modified.some(n => `${n.Id}-${n.name}` === nodeId)) {\r\n return 'modified';\r\n }\r\n return 'unchanged';\r\n }\r\n\r\n getModifiedProperties(node: any): string[] {\r\n if (!this.comparisonResult) return [];\r\n\r\n const nodeId = `${node.Id}-${node.name}`;\r\n const modified = this.comparisonResult.modified.find(n => `${n.Id}-${n.name}` === nodeId);\r\n\r\n return modified?.modifiedProperties || [];\r\n }\r\n\r\n toggleFullScreen(): void {\r\n this.isFullScreen = !this.isFullScreen;\r\n }\r\n}","<div class=\"comparison-container\" [class.fullscreen]=\"isFullScreen\">\r\n <header class=\"header\">\r\n <h1>Entity Structure Comparison</h1>\r\n <p class=\"subtitle\">Compare EntityStructureJson between two entity exports</p>\r\n </header>\r\n\r\n <div class=\"file-selection\">\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label left\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onLeftFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Left Entity\r\n </label>\r\n <span *ngIf=\"leftFile\" class=\"file-name\">{{ leftFile }}</span>\r\n </div>\r\n\r\n <div class=\"vs-separator\">VS</div>\r\n\r\n <div class=\"file-input-group\">\r\n <label class=\"file-label right\">\r\n <input type=\"file\" accept=\".xml\" (change)=\"onRightFileSelected($event)\" hidden>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"17 8 12 3 7 8\" />\r\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\" />\r\n </svg>\r\n Load Right Entity\r\n </label>\r\n <span *ngIf=\"rightFile\" class=\"file-name\">{{ rightFile }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-selection\" *ngIf=\"leftEntities.length > 0 || rightEntities.length > 0\">\r\n <div class=\"entity-list\">\r\n <h3>Left Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of leftEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedLeftEntity === entity\" (click)=\"selectLeftEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"entity-list\">\r\n <h3>Right Entities</h3>\r\n <div class=\"entities\">\r\n <div *ngFor=\"let entity of rightEntities\" class=\"entity-card\"\r\n [class.selected]=\"selectedRightEntity === entity\" (click)=\"selectRightEntity(entity)\">\r\n <div class=\"entity-icon\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\r\n stroke-width=\"2\">\r\n <path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n </svg>\r\n </div>\r\n <div class=\"entity-info\">\r\n <div class=\"entity-name\">{{ entity.name }}</div>\r\n <div class=\"entity-id\">{{ entity.id }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary-section\" *ngIf=\"comparisonResult\">\r\n <div class=\"summary\">\r\n <div class=\"summary-item added\">\r\n <span class=\"count\">{{ comparisonResult.rightOnly.length }}</span>\r\n <span class=\"label\">Added</span>\r\n </div>\r\n <div class=\"summary-item removed\">\r\n <span class=\"count\">{{ comparisonResult.leftOnly.length }}</span>\r\n <span class=\"label\">Removed</span>\r\n </div>\r\n <div class=\"summary-item modified\">\r\n <span class=\"count\">{{ comparisonResult.modified.length }}</span>\r\n <span class=\"label\">Modified</span>\r\n </div>\r\n <div class=\"summary-item unchanged\">\r\n <span class=\"count\">{{ comparisonResult.unchanged.length }}</span>\r\n <span class=\"label\">Unchanged</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-results\" *ngIf=\"comparisonResult\">\r\n <div class=\"results-header\">\r\n <div class=\"header-title\">\r\n <h2>Comparison Results</h2>\r\n <div class=\"header-actions\">\r\n <div class=\"diff-navigation\" *ngIf=\"hasDifferences\">\r\n <button class=\"nav-btn\" (click)=\"navigateToPreviousDifference()\" title=\"Previous Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"18 15 12 9 6 15\" />\r\n </svg>\r\n </button>\r\n <span class=\"diff-counter\">{{ currentDifferenceNumber }} / {{ totalDifferences }}</span>\r\n <button class=\"nav-btn\" (click)=\"navigateToNextDifference()\" title=\"Next Difference\">\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n </div>\r\n <button class=\"fullscreen-btn\" (click)=\"toggleFullScreen()\" [title]=\"isFullScreen ? 'Exit Full Screen' : 'Enter Full Screen'\">\r\n <svg *ngIf=\"!isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\r\n </svg>\r\n <svg *ngIf=\"isFullScreen\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\r\n </svg>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"structure-comparison\">\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedLeftEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedLeftEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'left' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"comparison-section\">\r\n <h3>\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path d=\"M9 11l3 3L22 4\" />\r\n <path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\" />\r\n </svg>\r\n {{ selectedRightEntity.name }}\r\n </h3>\r\n <div class=\"structure-tree\">\r\n <ng-container *ngFor=\"let node of selectedRightEntity.structure\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: node, side: 'right' }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<ng-template #nodeTemplate let-node let-side=\"side\">\r\n <div class=\"tree-node\" [class]=\"getChangeType(node)\" [attr.data-node-id]=\"getNodeId(node, side)\">\r\n <div class=\"node-header\">\r\n <button *ngIf=\"hasChildren(node)\" class=\"expand-btn\" (click)=\"toggleNode(getNodeId(node, side))\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <polyline *ngIf=\"!isExpanded(getNodeId(node, side))\" points=\"9 18 15 12 9 6\" />\r\n <polyline *ngIf=\"isExpanded(getNodeId(node, side))\" points=\"6 9 12 15 18 9\" />\r\n </svg>\r\n </button>\r\n <div *ngIf=\"!hasChildren(node)\" class=\"spacer\"></div>\r\n\r\n <div class=\"node-icon\">\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\r\n <path *ngIf=\"node.nodeType === 'Group'\"\r\n d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\r\n <circle *ngIf=\"node.nodeType === 'Attribute'\" cx=\"12\" cy=\"12\" r=\"10\" />\r\n </svg>\r\n </div>\r\n\r\n <div class=\"node-content\">\r\n <span class=\"node-name\">{{ node.name }}</span>\r\n <span class=\"node-type\">{{ node.nodeType }}</span>\r\n <span class=\"change-badge\" *ngIf=\"getChangeType(node) !== 'unchanged'\">\r\n {{ getChangeType(node) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-properties\" *ngIf=\"getPropertyKeys(node).length > 0\">\r\n <div *ngFor=\"let key of getPropertyKeys(node)\" class=\"property\"\r\n [class.modified]=\"getModifiedProperties(node).includes(key)\">\r\n <span class=\"property-key\">{{ key }}:</span>\r\n <span class=\"property-value\">{{ getProperties(node)[key] }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"node-children\" *ngIf=\"hasChildren(node) && isExpanded(getNodeId(node, side))\">\r\n <ng-container *ngFor=\"let child of getChildren(node)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"nodeTemplate; context: { $implicit: child, side: side }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>","/*\r\n * Public API Surface of concepto-message\r\n */\r\n\r\nexport * from './lib/concepto-user-controls.service';\r\nexport * from './lib/concepto-user-controls.component';\r\nexport * from './lib/concepto-message/concepto-message.component';\r\nexport * from './lib/concepto-context-menu/concepto-context-menu.component';\r\nexport * from './lib/concepto-tree/concepto-tree.component';\r\nexport * from './lib/entity-comparison/components/entity-comparison.component';\r\nexport * from './lib/entity-comparison/core/services/entity-comparison.service';","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.EntityComparisonService","i2"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class ConceptoTreeComponent {
|
|
3
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ConceptoTreeComponent, never>;
|
|
4
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ConceptoTreeComponent, "lib-concepto-tree", never, {}, {}, never, never, true, never>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EntityComparisonService } from '../core/services/entity-comparison.service';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export interface ComparisonResult {
|
|
4
|
+
leftOnly: any[];
|
|
5
|
+
rightOnly: any[];
|
|
6
|
+
modified: any[];
|
|
7
|
+
unchanged: any[];
|
|
8
|
+
}
|
|
9
|
+
export declare class EntityComparisonComponent {
|
|
10
|
+
private comparisonService;
|
|
11
|
+
leftFile: string;
|
|
12
|
+
rightFile: string;
|
|
13
|
+
leftEntities: any[];
|
|
14
|
+
rightEntities: any[];
|
|
15
|
+
selectedLeftEntity: any;
|
|
16
|
+
selectedRightEntity: any;
|
|
17
|
+
comparisonResult: ComparisonResult | null;
|
|
18
|
+
expandedNodes: Set<string>;
|
|
19
|
+
isFullScreen: boolean;
|
|
20
|
+
currentDifferenceIndex: number;
|
|
21
|
+
differences: any[];
|
|
22
|
+
constructor(comparisonService: EntityComparisonService);
|
|
23
|
+
onLeftFileSelected(event: any): void;
|
|
24
|
+
onRightFileSelected(event: any): void;
|
|
25
|
+
selectLeftEntity(entity: any): void;
|
|
26
|
+
selectRightEntity(entity: any): void;
|
|
27
|
+
private compareIfBothSelected;
|
|
28
|
+
private buildDifferencesList;
|
|
29
|
+
navigateToNextDifference(): void;
|
|
30
|
+
navigateToPreviousDifference(): void;
|
|
31
|
+
private scrollToDifference;
|
|
32
|
+
private expandPathToNode;
|
|
33
|
+
private expandParentsInStructure;
|
|
34
|
+
get hasDifferences(): boolean;
|
|
35
|
+
get currentDifferenceNumber(): number;
|
|
36
|
+
get totalDifferences(): number;
|
|
37
|
+
toggleNode(nodeId: string): void;
|
|
38
|
+
isExpanded(nodeId: string): boolean;
|
|
39
|
+
getNodeId(node: any, prefix: string): string;
|
|
40
|
+
hasChildren(node: any): boolean;
|
|
41
|
+
getChildren(node: any): any[];
|
|
42
|
+
getProperties(node: any): any;
|
|
43
|
+
getPropertyKeys(node: any): string[];
|
|
44
|
+
getChangeType(node: any): string;
|
|
45
|
+
getModifiedProperties(node: any): string[];
|
|
46
|
+
toggleFullScreen(): void;
|
|
47
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<EntityComparisonComponent, never>;
|
|
48
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<EntityComparisonComponent, "app-entity-comparison", never, {}, {}, never, never, true, never>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ComparisonResult } from '../../components/entity-comparison.component';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class EntityComparisonService {
|
|
4
|
+
parseEntities(xmlString: string): any[];
|
|
5
|
+
compareStructures(leftStructure: any[], rightStructure: any[]): ComparisonResult;
|
|
6
|
+
private createNodeMap;
|
|
7
|
+
private compareNodes;
|
|
8
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<EntityComparisonService, never>;
|
|
9
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<EntityComparisonService>;
|
|
10
|
+
}
|
package/package.json
CHANGED
package/public-api.d.ts
CHANGED
|
@@ -2,3 +2,6 @@ export * from './lib/concepto-user-controls.service';
|
|
|
2
2
|
export * from './lib/concepto-user-controls.component';
|
|
3
3
|
export * from './lib/concepto-message/concepto-message.component';
|
|
4
4
|
export * from './lib/concepto-context-menu/concepto-context-menu.component';
|
|
5
|
+
export * from './lib/concepto-tree/concepto-tree.component';
|
|
6
|
+
export * from './lib/entity-comparison/components/entity-comparison.component';
|
|
7
|
+
export * from './lib/entity-comparison/core/services/entity-comparison.service';
|