ngx-emfular-tool 1.0.0
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/README.md +63 -0
- package/fesm2022/ngx-emfular-tool.mjs +333 -0
- package/fesm2022/ngx-emfular-tool.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/alerting/alert/alert.component.d.ts +10 -0
- package/lib/alerting/alert.service.d.ts +10 -0
- package/lib/history/history.service.d.ts +29 -0
- package/lib/io/input-handler.d.ts +4 -0
- package/lib/io/io.service.d.ts +17 -0
- package/package.json +42 -0
- package/public-api.d.ts +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# NgxEmfularHelper
|
|
2
|
+
|
|
3
|
+
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0.
|
|
4
|
+
|
|
5
|
+
## Code scaffolding
|
|
6
|
+
|
|
7
|
+
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ng generate component component-name
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ng generate --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Building
|
|
20
|
+
|
|
21
|
+
To build the library, run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ng build tool
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
|
|
28
|
+
|
|
29
|
+
### Publishing the Library
|
|
30
|
+
|
|
31
|
+
Once the project is built, you can publish your library by following these steps:
|
|
32
|
+
|
|
33
|
+
1. Navigate to the `dist` directory:
|
|
34
|
+
```bash
|
|
35
|
+
cd dist/tool
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Run the `npm publish` command to publish your library to the npm registry:
|
|
39
|
+
```bash
|
|
40
|
+
npm publish
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Running unit tests
|
|
44
|
+
|
|
45
|
+
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ng test
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Running end-to-end tests
|
|
52
|
+
|
|
53
|
+
For end-to-end (e2e) testing, run:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ng e2e
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
|
60
|
+
|
|
61
|
+
## Additional Resources
|
|
62
|
+
|
|
63
|
+
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { __decorate, __param } from 'tslib';
|
|
2
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Inject, PLATFORM_ID, Component, Injectable } from '@angular/core';
|
|
5
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
6
|
+
import { MatIcon } from '@angular/material/icon';
|
|
7
|
+
import * as i1 from '@angular/material/dialog';
|
|
8
|
+
|
|
9
|
+
let HistoryService = class HistoryService {
|
|
10
|
+
// history in a circular buffer
|
|
11
|
+
//bufferSize entries: entries are 0 to bufferSize-1, -1 is not defined
|
|
12
|
+
prefix;
|
|
13
|
+
bufferSize;
|
|
14
|
+
oldestEntryName;
|
|
15
|
+
newestEntryName;
|
|
16
|
+
currentEntryName;
|
|
17
|
+
oldestEntry = -1;
|
|
18
|
+
newestEntry = -1;
|
|
19
|
+
currentEntry = -1;
|
|
20
|
+
stateSubject = new BehaviorSubject(null);
|
|
21
|
+
state$ = this.stateSubject.asObservable();
|
|
22
|
+
constructor(prefix = 'history_', bufferSize = 50, platformId) {
|
|
23
|
+
this.prefix = prefix;
|
|
24
|
+
this.oldestEntryName = prefix + 'oldestEntry';
|
|
25
|
+
this.newestEntryName = prefix + 'newestEntry';
|
|
26
|
+
this.currentEntryName = prefix + 'currentEntry';
|
|
27
|
+
this.bufferSize = bufferSize;
|
|
28
|
+
if (isPlatformBrowser(platformId)) {
|
|
29
|
+
this.init();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
init() {
|
|
33
|
+
this.oldestEntry = this.readNumber(this.oldestEntryName);
|
|
34
|
+
this.newestEntry = this.readNumber(this.newestEntryName);
|
|
35
|
+
this.currentEntry = this.readNumber(this.currentEntryName);
|
|
36
|
+
if (this.oldestEntry == -1 || this.newestEntry == -1 || this.currentEntry == -1) {
|
|
37
|
+
this.clearHistory();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
let entry = this.loadFromStorage(this.currentEntry);
|
|
41
|
+
this.stateSubject.next(entry);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
clearHistory() {
|
|
45
|
+
this.oldestEntry = -1;
|
|
46
|
+
this.newestEntry = -1;
|
|
47
|
+
this.currentEntry = -1;
|
|
48
|
+
this.saveNumber(this.oldestEntryName, this.oldestEntry);
|
|
49
|
+
this.saveNumber(this.newestEntryName, this.newestEntry);
|
|
50
|
+
this.saveNumber(this.currentEntryName, this.currentEntry);
|
|
51
|
+
for (let i = 1; i < this.bufferSize; i++) {
|
|
52
|
+
this.deleteEntry(i);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
save(elem) {
|
|
56
|
+
// needs to invalidate any element between the newest Entry and the current Entry, then save new Entry
|
|
57
|
+
this.invalidateTooYoungEntries();
|
|
58
|
+
// also remove one oldest entry if you need to make space
|
|
59
|
+
let next = this.incrementNumber(this.currentEntry);
|
|
60
|
+
if (next == this.oldestEntry || this.oldestEntry == -1) {
|
|
61
|
+
this.invalidateOldestEntry();
|
|
62
|
+
}
|
|
63
|
+
this.newestEntry = next;
|
|
64
|
+
this.currentEntry = next;
|
|
65
|
+
this.saveNumber(this.newestEntryName, this.newestEntry);
|
|
66
|
+
this.saveNumber(this.currentEntryName, this.currentEntry);
|
|
67
|
+
this.saveToStorage(next, elem);
|
|
68
|
+
}
|
|
69
|
+
undo() {
|
|
70
|
+
if (this.currentEntry == this.oldestEntry) { //current Entry is oldest, no decrement possible
|
|
71
|
+
console.error("Cannot undo - already reached oldest entry");
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.currentEntry = this.decrementNumber(this.currentEntry);
|
|
76
|
+
this.saveNumber(this.currentEntryName, this.currentEntry);
|
|
77
|
+
let entry = this.loadFromStorage(this.currentEntry);
|
|
78
|
+
this.stateSubject.next(entry);
|
|
79
|
+
return entry;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
isUndoNotPossible() {
|
|
83
|
+
return (this.currentEntry == this.oldestEntry);
|
|
84
|
+
}
|
|
85
|
+
redo() {
|
|
86
|
+
if (this.currentEntry == this.newestEntry) {
|
|
87
|
+
console.error("Cannot redo - already reached newest entry");
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.currentEntry = this.incrementNumber(this.currentEntry);
|
|
92
|
+
this.saveNumber(this.currentEntryName, this.currentEntry);
|
|
93
|
+
let entry = this.loadFromStorage(this.currentEntry);
|
|
94
|
+
this.stateSubject.next(entry);
|
|
95
|
+
return entry;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
isRedoNotPossible() {
|
|
99
|
+
return (this.currentEntry == this.newestEntry);
|
|
100
|
+
}
|
|
101
|
+
loadFromStorage(index) {
|
|
102
|
+
let itemString = localStorage.getItem(this.prefix + index);
|
|
103
|
+
if (itemString == null) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return JSON.parse(itemString);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
saveToStorage(index, elem) {
|
|
111
|
+
localStorage.setItem(this.prefix + index, JSON.stringify(elem));
|
|
112
|
+
}
|
|
113
|
+
deleteEntry(index) {
|
|
114
|
+
localStorage.removeItem(this.prefix + index);
|
|
115
|
+
}
|
|
116
|
+
invalidateTooYoungEntries() {
|
|
117
|
+
while (this.newestEntry != this.currentEntry) {
|
|
118
|
+
this.deleteEntry(this.newestEntry);
|
|
119
|
+
this.newestEntry = this.decrementNumber(this.newestEntry);
|
|
120
|
+
}
|
|
121
|
+
this.saveNumber(this.newestEntryName, this.newestEntry);
|
|
122
|
+
}
|
|
123
|
+
invalidateOldestEntry() {
|
|
124
|
+
this.deleteEntry(this.oldestEntry);
|
|
125
|
+
this.oldestEntry = this.incrementNumber(this.oldestEntry);
|
|
126
|
+
this.saveNumber(this.oldestEntryName, this.oldestEntry);
|
|
127
|
+
}
|
|
128
|
+
readNumber(name) {
|
|
129
|
+
let maybeN = localStorage.getItem(name);
|
|
130
|
+
if (maybeN) {
|
|
131
|
+
return parseInt(maybeN, 10);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
saveNumber(name, num) {
|
|
138
|
+
localStorage.setItem(name, num.toString(10));
|
|
139
|
+
}
|
|
140
|
+
decrementNumber(num) {
|
|
141
|
+
if (num === 0)
|
|
142
|
+
return this.bufferSize - 1;
|
|
143
|
+
else
|
|
144
|
+
return num - 1;
|
|
145
|
+
}
|
|
146
|
+
incrementNumber(num) {
|
|
147
|
+
if (num === this.bufferSize - 1)
|
|
148
|
+
return 0;
|
|
149
|
+
else
|
|
150
|
+
return num + 1;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
HistoryService = __decorate([
|
|
154
|
+
__param(2, Inject(PLATFORM_ID))
|
|
155
|
+
], HistoryService);
|
|
156
|
+
|
|
157
|
+
class AlertComponent {
|
|
158
|
+
dialogRef;
|
|
159
|
+
message;
|
|
160
|
+
constructor(dialogRef) {
|
|
161
|
+
this.dialogRef = dialogRef;
|
|
162
|
+
}
|
|
163
|
+
closeMe() {
|
|
164
|
+
this.dialogRef.close();
|
|
165
|
+
}
|
|
166
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AlertComponent, deps: [{ token: i1.MatDialogRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
167
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: AlertComponent, isStandalone: true, selector: "alert-component", ngImport: i0, template: "<div style=\"text-align: center\">\n <h3>Warning</h3>\n <p>{{message}}</p>\n <button class=\"tool-button-text\" (click)=\"closeMe()\">\n <mat-icon aria-hidden=\"false\" fontIcon=\"check\"></mat-icon>\n Ok\n </button>\n</div>\n", styles: [".tool-button-text{margin:1rem;text-align:center;vertical-align:middle;color:orange;height:3rem;width:6rem;background:#fff;border:2px solid orange;border-radius:4px}.tool-button-text:hover{box-shadow:0 12px 16px #0000003d,0 17px 50px #00000030}\n"], dependencies: [{ kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
|
|
168
|
+
}
|
|
169
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AlertComponent, decorators: [{
|
|
170
|
+
type: Component,
|
|
171
|
+
args: [{ selector: 'alert-component', imports: [
|
|
172
|
+
MatIcon,
|
|
173
|
+
], template: "<div style=\"text-align: center\">\n <h3>Warning</h3>\n <p>{{message}}</p>\n <button class=\"tool-button-text\" (click)=\"closeMe()\">\n <mat-icon aria-hidden=\"false\" fontIcon=\"check\"></mat-icon>\n Ok\n </button>\n</div>\n", styles: [".tool-button-text{margin:1rem;text-align:center;vertical-align:middle;color:orange;height:3rem;width:6rem;background:#fff;border:2px solid orange;border-radius:4px}.tool-button-text:hover{box-shadow:0 12px 16px #0000003d,0 17px 50px #00000030}\n"] }]
|
|
174
|
+
}], ctorParameters: () => [{ type: i1.MatDialogRef }] });
|
|
175
|
+
|
|
176
|
+
class AlertService {
|
|
177
|
+
dialog;
|
|
178
|
+
static instance;
|
|
179
|
+
constructor(dialog) {
|
|
180
|
+
this.dialog = dialog;
|
|
181
|
+
AlertService.instance = this;
|
|
182
|
+
}
|
|
183
|
+
alert(msg) {
|
|
184
|
+
const dialogRef = this.dialog.open(AlertComponent, { width: '20%', height: '30%' });
|
|
185
|
+
dialogRef.componentInstance.message = msg;
|
|
186
|
+
}
|
|
187
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AlertService, deps: [{ token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
188
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AlertService, providedIn: 'root' });
|
|
189
|
+
}
|
|
190
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AlertService, decorators: [{
|
|
191
|
+
type: Injectable,
|
|
192
|
+
args: [{
|
|
193
|
+
providedIn: 'root'
|
|
194
|
+
}]
|
|
195
|
+
}], ctorParameters: () => [{ type: i1.MatDialog }] });
|
|
196
|
+
|
|
197
|
+
class InputHandler {
|
|
198
|
+
//helps with converting an event from a number input (!) into a number...
|
|
199
|
+
// should be called on (input) while (ngModelChange) delivers the value immediately on $event
|
|
200
|
+
static getNewValueFromEvent($event) {
|
|
201
|
+
const input = $event.target;
|
|
202
|
+
return Number(input.value);
|
|
203
|
+
}
|
|
204
|
+
// this needs to be called on all input elements to allow them to accept the same value twice
|
|
205
|
+
static clearElem(event) {
|
|
206
|
+
let target = event.target;
|
|
207
|
+
target.value = '';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class IoService {
|
|
212
|
+
constructor() { }
|
|
213
|
+
async loadStringFromFile(event) {
|
|
214
|
+
const target = event.target;
|
|
215
|
+
const files = target.files;
|
|
216
|
+
return files[0].text();
|
|
217
|
+
}
|
|
218
|
+
saveFile(contentBlob, fileName) {
|
|
219
|
+
const link = document.createElement('a');
|
|
220
|
+
link.style.display = 'none';
|
|
221
|
+
// Attach the content to the anchor
|
|
222
|
+
link.href = URL.createObjectURL(contentBlob);
|
|
223
|
+
link.download = fileName;
|
|
224
|
+
// Append to DOM and simulate click (this will trigger the download)
|
|
225
|
+
document.body.appendChild(link);
|
|
226
|
+
link.click();
|
|
227
|
+
// Cleanup
|
|
228
|
+
document.body.removeChild(link);
|
|
229
|
+
}
|
|
230
|
+
saveJson(json, title) {
|
|
231
|
+
const contentBlob = new Blob([json], { type: 'application/json' });
|
|
232
|
+
this.saveFile(contentBlob, title + '.json');
|
|
233
|
+
}
|
|
234
|
+
cleanCopySVGAsBlob(svg) {
|
|
235
|
+
const clonedSvg = svg.cloneNode(true);
|
|
236
|
+
clonedSvg.removeAttribute('ng-version'); // remove Angular artifacts
|
|
237
|
+
const svgText = new XMLSerializer().serializeToString(clonedSvg);
|
|
238
|
+
return new Blob([svgText], { type: 'image/svg+xml' });
|
|
239
|
+
}
|
|
240
|
+
saveSVG(svgContent, title) {
|
|
241
|
+
const contentBlob = this.cleanCopySVGAsBlob(svgContent);
|
|
242
|
+
this.saveFile(contentBlob, title + '.svg');
|
|
243
|
+
}
|
|
244
|
+
saveSvgAsPng(svgContent, title) {
|
|
245
|
+
const contentBlob = this.cleanCopySVGAsBlob(svgContent);
|
|
246
|
+
this.convertSvgBlobToPngOrJpegAndDownload(contentBlob, title, true);
|
|
247
|
+
}
|
|
248
|
+
saveSvgAsJpeg(svgContent, title) {
|
|
249
|
+
const contentBlob = this.cleanCopySVGAsBlob(svgContent);
|
|
250
|
+
this.convertSvgBlobToPngOrJpegAndDownload(contentBlob, title, false);
|
|
251
|
+
}
|
|
252
|
+
convertSvgBlobToPngOrJpegAndDownload(svgBlob, title, usePng) {
|
|
253
|
+
const reader = new FileReader();
|
|
254
|
+
reader.onload = () => {
|
|
255
|
+
const svgText = reader.result;
|
|
256
|
+
const img = new Image();
|
|
257
|
+
const svgBase64 = btoa(new TextEncoder()
|
|
258
|
+
.encode(svgText)
|
|
259
|
+
.reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
|
260
|
+
img.src = `data:image/svg+xml;base64,${svgBase64}`;
|
|
261
|
+
img.onload = () => {
|
|
262
|
+
this.loadImgToCanvas(img, title, usePng);
|
|
263
|
+
};
|
|
264
|
+
img.onerror = (err) => {
|
|
265
|
+
console.error('Error loading SVG image:', err);
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
reader.onerror = (err) => {
|
|
269
|
+
console.error('Error reading SVG blob:', err);
|
|
270
|
+
};
|
|
271
|
+
reader.readAsText(svgBlob);
|
|
272
|
+
}
|
|
273
|
+
canvasToPng(canvas, fileName) {
|
|
274
|
+
canvas.toBlob((pngBlob) => {
|
|
275
|
+
if (!pngBlob) {
|
|
276
|
+
console.error('Failed to convert canvas to PNG blob');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
this.saveFile(pngBlob, fileName + '.png');
|
|
281
|
+
}
|
|
282
|
+
}, 'image/png');
|
|
283
|
+
}
|
|
284
|
+
canvasToJpeg(canvas, fileName) {
|
|
285
|
+
canvas.toBlob((jpgBlob) => {
|
|
286
|
+
if (!jpgBlob) {
|
|
287
|
+
console.error('Failed to convert canvas to JPEG blob');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
this.saveFile(jpgBlob, fileName + '.jpeg');
|
|
292
|
+
}
|
|
293
|
+
}, 'image/jpeg', 1);
|
|
294
|
+
}
|
|
295
|
+
loadImgToCanvas(img, fileName, usePng = true) {
|
|
296
|
+
const canvas = document.createElement('canvas');
|
|
297
|
+
canvas.width = img.width;
|
|
298
|
+
canvas.height = img.height;
|
|
299
|
+
const ctx = canvas.getContext('2d');
|
|
300
|
+
if (!ctx) {
|
|
301
|
+
console.error('Canvas context not available');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
ctx.fillStyle = '#ffffff'; // white
|
|
305
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
306
|
+
ctx.drawImage(img, 0, 0);
|
|
307
|
+
if (usePng) {
|
|
308
|
+
this.canvasToPng(canvas, fileName);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.canvasToJpeg(canvas, fileName);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: IoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
315
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: IoService, providedIn: 'root' });
|
|
316
|
+
}
|
|
317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: IoService, decorators: [{
|
|
318
|
+
type: Injectable,
|
|
319
|
+
args: [{
|
|
320
|
+
providedIn: 'root'
|
|
321
|
+
}]
|
|
322
|
+
}], ctorParameters: () => [] });
|
|
323
|
+
|
|
324
|
+
/*
|
|
325
|
+
* Public API Surface of tool
|
|
326
|
+
*/
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Generated bundle index. Do not edit.
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
export { AlertComponent, AlertService, HistoryService, InputHandler, IoService };
|
|
333
|
+
//# sourceMappingURL=ngx-emfular-tool.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngx-emfular-tool.mjs","sources":["../../src/lib/history/history.service.ts","../../src/lib/alerting/alert/alert.component.ts","../../src/lib/alerting/alert/alert.component.html","../../src/lib/alerting/alert.service.ts","../../src/lib/io/input-handler.ts","../../src/lib/io/io.service.ts","../../src/public-api.ts","../../src/ngx-emfular-tool.ts"],"sourcesContent":["import {BehaviorSubject} from \"rxjs\";\nimport {Inject, PLATFORM_ID} from \"@angular/core\";\nimport { isPlatformBrowser } from \"@angular/common\";\n\nexport class HistoryService<T> {\n\n // history in a circular buffer\n //bufferSize entries: entries are 0 to bufferSize-1, -1 is not defined\n\n private readonly prefix: string;\n private readonly bufferSize: number;\n readonly oldestEntryName: string;\n readonly newestEntryName: string;\n readonly currentEntryName: string;\n private oldestEntry: number = -1;\n private newestEntry: number = -1;\n private currentEntry: number = -1;\n\n private stateSubject = new BehaviorSubject<T | null>(null);\n state$ = this.stateSubject.asObservable();\n\n constructor(\n prefix: string = 'history_',\n bufferSize: number = 50,\n @Inject(PLATFORM_ID) platformId: Object\n ) {\n this.prefix = prefix;\n this.oldestEntryName = prefix + 'oldestEntry';\n this.newestEntryName = prefix + 'newestEntry';\n this.currentEntryName = prefix + 'currentEntry';\n this.bufferSize = bufferSize;\n if (isPlatformBrowser(platformId)) {\n this.init()\n }\n }\n\n init() {\n this.oldestEntry = this.readNumber(this.oldestEntryName)\n this.newestEntry = this.readNumber(this.newestEntryName)\n this.currentEntry = this.readNumber(this.currentEntryName)\n if (this.oldestEntry == -1 || this.newestEntry == -1 || this.currentEntry == -1) {\n this.clearHistory()\n } else {\n let entry = this.loadFromStorage(this.currentEntry)\n this.stateSubject.next(entry)\n }\n }\n\n clearHistory() {\n this.oldestEntry = -1;\n this.newestEntry = -1;\n this.currentEntry = -1;\n this.saveNumber(this.oldestEntryName, this.oldestEntry)\n this.saveNumber(this.newestEntryName, this.newestEntry)\n this.saveNumber(this.currentEntryName, this.currentEntry)\n for (let i = 1; i < this.bufferSize; i++) {\n this.deleteEntry(i);\n }\n }\n\n save(elem: T) {\n // needs to invalidate any element between the newest Entry and the current Entry, then save new Entry\n this.invalidateTooYoungEntries()\n // also remove one oldest entry if you need to make space\n let next = this.incrementNumber(this.currentEntry)\n\n if (next == this.oldestEntry || this.oldestEntry == -1) {\n this.invalidateOldestEntry()\n }\n this.newestEntry = next\n this.currentEntry = next\n this.saveNumber(this.newestEntryName, this.newestEntry)\n this.saveNumber(this.currentEntryName, this.currentEntry)\n this.saveToStorage(next, elem)\n }\n\n undo(): T | null {\n if (this.currentEntry == this.oldestEntry) { //current Entry is oldest, no decrement possible\n console.error(\"Cannot undo - already reached oldest entry\")\n return null\n } else {\n this.currentEntry = this.decrementNumber(this.currentEntry)\n this.saveNumber(this.currentEntryName, this.currentEntry)\n let entry = this.loadFromStorage(this.currentEntry)\n this.stateSubject.next(entry)\n return entry\n }\n }\n\n isUndoNotPossible(): boolean {\n return (this.currentEntry == this.oldestEntry)\n }\n\n redo(): T | null {\n if (this.currentEntry == this.newestEntry) {\n console.error(\"Cannot redo - already reached newest entry\")\n return null\n } else {\n this.currentEntry = this.incrementNumber(this.currentEntry)\n this.saveNumber(this.currentEntryName, this.currentEntry)\n let entry = this.loadFromStorage(this.currentEntry)\n this.stateSubject.next(entry)\n return entry\n }\n }\n\n isRedoNotPossible(): boolean {\n return (this.currentEntry == this.newestEntry)\n }\n\n private loadFromStorage(index: number): T | null {\n let itemString = localStorage.getItem(this.prefix + index)\n if (itemString == null) {\n return null\n } else {\n return JSON.parse(itemString)\n }\n }\n\n private saveToStorage(index: number, elem: T): void {\n localStorage.setItem(this.prefix + index, JSON.stringify(elem))\n }\n\n private deleteEntry(index: number): void {\n localStorage.removeItem(this.prefix + index)\n }\n\n private invalidateTooYoungEntries(): void {\n while (this.newestEntry != this.currentEntry) {\n this.deleteEntry(this.newestEntry);\n this.newestEntry = this.decrementNumber(this.newestEntry)\n }\n this.saveNumber(this.newestEntryName, this.newestEntry)\n }\n\n private invalidateOldestEntry(): void {\n this.deleteEntry(this.oldestEntry)\n this.oldestEntry = this.incrementNumber(this.oldestEntry)\n this.saveNumber(this.oldestEntryName, this.oldestEntry)\n }\n\n private readNumber(name: string): number {\n let maybeN = localStorage.getItem(name)\n if (maybeN) {\n return parseInt(maybeN, 10)\n } else {\n return -1\n }\n }\n\n private saveNumber(name: string, num: number): void {\n localStorage.setItem(name, num.toString(10))\n }\n\n private decrementNumber(num: number): number {\n if (num === 0) return this.bufferSize-1\n else return num-1\n }\n\n private incrementNumber(num: number): number {\n if (num === this.bufferSize-1) return 0\n else return num+1\n }\n}\n","import { Component } from '@angular/core';\nimport {MatDialogRef} from \"@angular/material/dialog\";\nimport {MatIcon} from \"@angular/material/icon\";\n\n@Component({\n selector: 'alert-component',\n imports: [\n MatIcon,\n ],\n templateUrl: './alert.component.html',\n styleUrl: './alert.component.css'\n})\nexport class AlertComponent {\n\n public message!: string;\n\n constructor(\n public dialogRef: MatDialogRef<AlertComponent>\n ) {}\n\n closeMe(): void {\n this.dialogRef.close();\n }\n}\n","<div style=\"text-align: center\">\n <h3>Warning</h3>\n <p>{{message}}</p>\n <button class=\"tool-button-text\" (click)=\"closeMe()\">\n <mat-icon aria-hidden=\"false\" fontIcon=\"check\"></mat-icon>\n Ok\n </button>\n</div>\n","import { Injectable } from '@angular/core';\nimport {MatDialog} from \"@angular/material/dialog\";\nimport {AlertComponent} from \"./alert/alert.component\";\n\n@Injectable({\n providedIn: 'root'\n})\nexport class AlertService {\n\n static instance: AlertService;\n constructor(\n private dialog: MatDialog,\n ) {\n AlertService.instance = this;\n }\n\n\n alert(msg: string) {\n const dialogRef = this.dialog.open(\n AlertComponent,\n {width: '20%', height: '30%'}\n )\n dialogRef.componentInstance.message = msg;\n }\n}\n","export class InputHandler {\n\n //helps with converting an event from a number input (!) into a number...\n // should be called on (input) while (ngModelChange) delivers the value immediately on $event\n static getNewValueFromEvent($event: Event): number {\n const input = $event.target as HTMLInputElement;\n return Number(input.value);\n }\n\n // this needs to be called on all input elements to allow them to accept the same value twice\n static clearElem(event: Event) {\n let target = event.target as HTMLInputElement;\n target.value = ''\n }\n}\n","import {Injectable} from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class IoService {\n\n constructor() { }\n\n async loadStringFromFile(event: Event): Promise<string> {\n const target = event.target as HTMLInputElement;\n const files = target.files as FileList;\n return files[0].text();\n }\n\n saveFile(contentBlob: Blob, fileName: string) {\n const link = document.createElement('a')\n link.style.display = 'none'\n // Attach the content to the anchor\n link.href = URL.createObjectURL(contentBlob);\n link.download = fileName;\n // Append to DOM and simulate click (this will trigger the download)\n document.body.appendChild(link);\n link.click();\n // Cleanup\n document.body.removeChild(link);\n }\n\n saveJson(json: string, title: string) {\n const contentBlob = new Blob([json], {type: 'application/json'});\n this.saveFile(contentBlob, title+'.json');\n }\n\n cleanCopySVGAsBlob(svg: SVGElement): Blob {\n const clonedSvg = svg.cloneNode(true) as SVGElement;\n clonedSvg.removeAttribute('ng-version'); // remove Angular artifacts\n const svgText = new XMLSerializer().serializeToString(clonedSvg);\n return new Blob([svgText], { type: 'image/svg+xml' });\n }\n\n saveSVG(svgContent: SVGElement, title: string) {\n const contentBlob = this.cleanCopySVGAsBlob(svgContent);\n this.saveFile(contentBlob, title+'.svg');\n }\n\n saveSvgAsPng(svgContent: SVGElement, title: string) {\n const contentBlob = this.cleanCopySVGAsBlob(svgContent);\n this.convertSvgBlobToPngOrJpegAndDownload(contentBlob, title, true);\n }\n\n saveSvgAsJpeg(svgContent: SVGElement, title: string) {\n const contentBlob = this.cleanCopySVGAsBlob(svgContent);\n this.convertSvgBlobToPngOrJpegAndDownload(contentBlob, title, false);\n }\n\n private convertSvgBlobToPngOrJpegAndDownload(svgBlob: Blob, title: string, usePng?: boolean): void {\n const reader = new FileReader();\n\n reader.onload = () => {\n const svgText = reader.result as string;\n\n const img = new Image();\n const svgBase64 = btoa(\n new TextEncoder()\n .encode(svgText)\n .reduce((data, byte) => data + String.fromCharCode(byte), '')\n )\n img.src = `data:image/svg+xml;base64,${svgBase64}`;\n\n img.onload = () => {\n this.loadImgToCanvas(img, title, usePng)\n }\n\n img.onerror = (err) => {\n console.error('Error loading SVG image:', err);\n };\n };\n\n reader.onerror = (err) => {\n console.error('Error reading SVG blob:', err);\n };\n\n reader.readAsText(svgBlob);\n }\n\n private canvasToPng(canvas: HTMLCanvasElement, fileName: string) {\n canvas.toBlob((pngBlob) => {\n if (!pngBlob) {\n console.error('Failed to convert canvas to PNG blob');\n return;\n } else {\n this.saveFile(pngBlob, fileName+'.png')\n }\n }, 'image/png');\n }\n\n private canvasToJpeg(canvas: HTMLCanvasElement, fileName: string) {\n canvas.toBlob((jpgBlob) => {\n if (!jpgBlob) {\n console.error('Failed to convert canvas to JPEG blob');\n return;\n } else {\n this.saveFile(jpgBlob, fileName+'.jpeg')\n }\n }, 'image/jpeg', 1);\n }\n\n private loadImgToCanvas(img: HTMLImageElement, fileName: string, usePng=true) {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n console.error('Canvas context not available');\n return;\n }\n\n ctx.fillStyle = '#ffffff'; // white\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(img, 0, 0);\n\n if(usePng) {\n this.canvasToPng(canvas, fileName);\n } else {\n this.canvasToJpeg(canvas, fileName);\n }\n }\n}\n","/*\n * Public API Surface of tool\n */\nexport * from './lib/history/history.service';\n\nexport * from './lib/alerting/alert.service';\nexport * from './lib/alerting/alert/alert.component';\n\nexport * from './lib/io/input-handler';\nexport * from './lib/io/io.service'","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MatDialogRef } from "@angular/material/dialog";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class AlertComponent {
|
|
4
|
+
dialogRef: MatDialogRef<AlertComponent>;
|
|
5
|
+
message: string;
|
|
6
|
+
constructor(dialogRef: MatDialogRef<AlertComponent>);
|
|
7
|
+
closeMe(): void;
|
|
8
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AlertComponent, never>;
|
|
9
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AlertComponent, "alert-component", never, {}, {}, never, never, true, never>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MatDialog } from "@angular/material/dialog";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class AlertService {
|
|
4
|
+
private dialog;
|
|
5
|
+
static instance: AlertService;
|
|
6
|
+
constructor(dialog: MatDialog);
|
|
7
|
+
alert(msg: string): void;
|
|
8
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AlertService, never>;
|
|
9
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AlertService>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare class HistoryService<T> {
|
|
2
|
+
private readonly prefix;
|
|
3
|
+
private readonly bufferSize;
|
|
4
|
+
readonly oldestEntryName: string;
|
|
5
|
+
readonly newestEntryName: string;
|
|
6
|
+
readonly currentEntryName: string;
|
|
7
|
+
private oldestEntry;
|
|
8
|
+
private newestEntry;
|
|
9
|
+
private currentEntry;
|
|
10
|
+
private stateSubject;
|
|
11
|
+
state$: import("rxjs").Observable<T>;
|
|
12
|
+
constructor(prefix: string, bufferSize: number, platformId: Object);
|
|
13
|
+
init(): void;
|
|
14
|
+
clearHistory(): void;
|
|
15
|
+
save(elem: T): void;
|
|
16
|
+
undo(): T | null;
|
|
17
|
+
isUndoNotPossible(): boolean;
|
|
18
|
+
redo(): T | null;
|
|
19
|
+
isRedoNotPossible(): boolean;
|
|
20
|
+
private loadFromStorage;
|
|
21
|
+
private saveToStorage;
|
|
22
|
+
private deleteEntry;
|
|
23
|
+
private invalidateTooYoungEntries;
|
|
24
|
+
private invalidateOldestEntry;
|
|
25
|
+
private readNumber;
|
|
26
|
+
private saveNumber;
|
|
27
|
+
private decrementNumber;
|
|
28
|
+
private incrementNumber;
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class IoService {
|
|
3
|
+
constructor();
|
|
4
|
+
loadStringFromFile(event: Event): Promise<string>;
|
|
5
|
+
saveFile(contentBlob: Blob, fileName: string): void;
|
|
6
|
+
saveJson(json: string, title: string): void;
|
|
7
|
+
cleanCopySVGAsBlob(svg: SVGElement): Blob;
|
|
8
|
+
saveSVG(svgContent: SVGElement, title: string): void;
|
|
9
|
+
saveSvgAsPng(svgContent: SVGElement, title: string): void;
|
|
10
|
+
saveSvgAsJpeg(svgContent: SVGElement, title: string): void;
|
|
11
|
+
private convertSvgBlobToPngOrJpegAndDownload;
|
|
12
|
+
private canvasToPng;
|
|
13
|
+
private canvasToJpeg;
|
|
14
|
+
private loadImgToCanvas;
|
|
15
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<IoService, never>;
|
|
16
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<IoService>;
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ngx-emfular-tool",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Small Angular library with helper schematics for EMFular, currently alerting, history, and io facilities",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"EMFular",
|
|
7
|
+
"Angular",
|
|
8
|
+
"IO",
|
|
9
|
+
"alert"
|
|
10
|
+
],
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Susanne Göbel",
|
|
13
|
+
"email": "goebel@uni-koblenz.de"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/softlang/EMFular-tool"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@angular/cdk": "^19.2.7",
|
|
21
|
+
"@angular/common": "^19.2.7",
|
|
22
|
+
"@angular/core": "^19.2.7",
|
|
23
|
+
"@angular/forms": "^19.2.7",
|
|
24
|
+
"@types/uuid": "^10.0.0",
|
|
25
|
+
"@angular/material": "^19.2.7"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"tslib": "^2.3.0"
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"module": "fesm2022/ngx-emfular-tool.mjs",
|
|
32
|
+
"typings": "index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
"./package.json": {
|
|
35
|
+
"default": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./index.d.ts",
|
|
39
|
+
"default": "./fesm2022/ngx-emfular-tool.mjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|