pro-xy-ui 0.5.8 → 0.6.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/.esformatter +28 -28
- package/.eslintrc +15 -15
- package/bin/pro-xy-ui +25 -27
- package/index.html +38 -38
- package/lib/app/_common/app-menu.js +42 -42
- package/lib/app/_common/app-menu.ts +41 -41
- package/lib/app/_common/patchResize.js +33 -33
- package/lib/app/_common/patchResize.ts +32 -32
- package/lib/app/_common/pipes.js +33 -33
- package/lib/app/_common/pipes.ts +21 -21
- package/lib/app/_common/rxjs-extensions.js +20 -20
- package/lib/app/_common/rxjs-extensions.ts +21 -21
- package/lib/app/app.component.css +39 -39
- package/lib/app/app.component.html +35 -35
- package/lib/app/app.module.js +92 -92
- package/lib/app/app.module.ts +89 -89
- package/lib/app/main.js +11 -11
- package/lib/app/main.ts +11 -11
- package/lib/app/model/config.js +69 -69
- package/lib/app/model/config.ts +79 -79
- package/lib/app/model/http.js +116 -116
- package/lib/app/service/config.defaultConfig.js +74 -74
- package/lib/app/service/config.defaultConfig.ts +72 -72
- package/lib/app/service/config.service.js +44 -44
- package/lib/app/service/config.service.ts +39 -39
- package/lib/app/service/socket.service.js +109 -109
- package/lib/app/service/socket.service.ts +131 -131
- package/lib/app/service/traffic.service.js +98 -98
- package/lib/app/views/auto-responder/auto-responder.component.css +37 -37
- package/lib/app/views/auto-responder/auto-responder.component.html +41 -41
- package/lib/app/views/auto-responder/auto-responder.component.js +102 -102
- package/lib/app/views/auto-responder/auto-responder.component.ts +110 -110
- package/lib/app/views/composer/composer.component.css +7 -7
- package/lib/app/views/composer/composer.component.html +9 -9
- package/lib/app/views/composer/composer.component.js +147 -147
- package/lib/app/views/composer/composer.component.ts +148 -148
- package/lib/app/views/config/config.component.css +13 -13
- package/lib/app/views/config/config.component.html +14 -14
- package/lib/app/views/config/config.component.js +163 -163
- package/lib/app/views/config/diff.dialog.html +3 -3
- package/lib/app/views/config/diff.dialog.js +29 -29
- package/lib/app/views/config/diff.dialog.ts +17 -17
- package/lib/app/views/config/plugins.dialog.html +17 -17
- package/lib/app/views/config/plugins.dialog.js +29 -29
- package/lib/app/views/config/plugins.dialog.ts +17 -17
- package/lib/app/views/config-list-base.js +44 -44
- package/lib/app/views/config-list-base.ts +66 -66
- package/lib/app/views/connection/connection.component.html +9 -9
- package/lib/app/views/connection/connection.component.js +87 -89
- package/lib/app/views/connection/connection.component.ts +83 -85
- package/lib/app/views/delay/delay.component.css +23 -23
- package/lib/app/views/delay/delay.component.html +35 -35
- package/lib/app/views/delay/delay.component.js +34 -34
- package/lib/app/views/find/find.component.css +30 -30
- package/lib/app/views/find/find.component.html +10 -10
- package/lib/app/views/find/find.component.js +113 -113
- package/lib/app/views/find/find.component.ts +111 -111
- package/lib/app/views/inspector/inspector.component.css +14 -14
- package/lib/app/views/inspector/inspector.component.html +17 -17
- package/lib/app/views/inspector/inspector.component.js +64 -64
- package/lib/app/views/inspector/inspector.component.ts +54 -54
- package/lib/app/views/logs/logs.component.css +7 -7
- package/lib/app/views/logs/logs.component.html +13 -13
- package/lib/app/views/logs/logs.component.js +56 -56
- package/lib/app/views/logs/logs.component.ts +46 -46
- package/lib/app/views/requests/request-row.component.css +15 -15
- package/lib/app/views/requests/request-row.component.html +7 -7
- package/lib/app/views/requests/request-row.component.js +68 -68
- package/lib/app/views/requests/request.bodyMenu.js +41 -41
- package/lib/app/views/requests/request.bodyMenu.ts +61 -61
- package/lib/app/views/requests/request.headMenu.js +25 -25
- package/lib/app/views/requests/requests.component.css +49 -49
- package/lib/app/views/requests/requests.component.html +33 -33
- package/lib/app/views/requests/requests.component.js +127 -127
- package/lib/app/views/status/status.component.css +18 -18
- package/lib/app/views/status/status.component.html +8 -8
- package/lib/app/views/status/status.component.js +49 -49
- package/lib/app/views/status/status.component.ts +45 -45
- package/lib/app/views/url-replace/url-replace.component.css +32 -32
- package/lib/app/views/url-replace/url-replace.component.html +36 -36
- package/lib/app/views/url-replace/url-replace.component.js +34 -34
- package/lib/index.js +23 -23
- package/lib/lib.d.ts +2 -2
- package/lib/style.css +100 -100
- package/lib/systemjs.config.js +75 -75
- package/package.json +64 -64
- package/readme.md +5 -5
- package/tsconfig.json +23 -23
- package/typings.json +11 -11
- package/npm-shrinkwrap.json +0 -2568
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
import { Component, OnInit, ChangeDetectionStrategy, Input} from '@angular/core';
|
|
2
|
-
import { Http, Headers, RequestOptions, RequestMethod } from '@angular/http';
|
|
3
|
-
import { SocketService } from '../../service/socket.service';
|
|
4
|
-
import { ReqRes } from '../../model/http';
|
|
5
|
-
var app = nw.require('nw.gui').App;
|
|
6
|
-
var validUrl = nw.require('valid-url');
|
|
7
|
-
var url = nw.require('url');
|
|
8
|
-
|
|
9
|
-
var SAMPLE_REQUEST = 'POST http://jsonplaceholder.typicode.com/posts\ncontent-type: application/json\naccept: */*\n\n{"sample":1}';
|
|
10
|
-
|
|
11
|
-
@Component({
|
|
12
|
-
moduleId: module.id,
|
|
13
|
-
templateUrl: 'composer.component.html',
|
|
14
|
-
styleUrls: ['composer.component.css'],
|
|
15
|
-
selector: 'composer',
|
|
16
|
-
host: { class: 'flex-grow' },
|
|
17
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
18
|
-
})
|
|
19
|
-
export class ComposerComponent implements OnInit {
|
|
20
|
-
|
|
21
|
-
constructor(private http: Http, private socketService: SocketService) { }
|
|
22
|
-
|
|
23
|
-
config: any
|
|
24
|
-
|
|
25
|
-
ngOnInit(): void {
|
|
26
|
-
this.loadSample();
|
|
27
|
-
this.socketService.configObservable.subscribe(config => { this.config = config; })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
invalid = false
|
|
31
|
-
|
|
32
|
-
@Input()
|
|
33
|
-
set reqRes(reqRes: ReqRes) {
|
|
34
|
-
if (!reqRes) { return; }
|
|
35
|
-
this.model = `${reqRes.method} ${reqRes.url}\n${reqRes.reqHeadersStr}\n\n${reqRes.reqBody}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
_message = ""
|
|
39
|
-
get message() { return this._message; }
|
|
40
|
-
set message(message) {
|
|
41
|
-
this._message = message;
|
|
42
|
-
this.invalid = !!message;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
parsedModel: any
|
|
46
|
-
|
|
47
|
-
_model: string
|
|
48
|
-
get model() { return this._model; }
|
|
49
|
-
set model(model) {
|
|
50
|
-
this._model = model;
|
|
51
|
-
try {
|
|
52
|
-
this.parsedModel = this.parse(this.model);
|
|
53
|
-
this.message = null;
|
|
54
|
-
} catch (e) {
|
|
55
|
-
this.message = e.message;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
parse(value: string) {
|
|
60
|
-
if (!value) {
|
|
61
|
-
throw new Error("Method and URL are required");
|
|
62
|
-
}
|
|
63
|
-
var lines = value.split(/\r\n?|\n/);
|
|
64
|
-
|
|
65
|
-
var parts = lines.shift().split(" ");
|
|
66
|
-
|
|
67
|
-
var method = parts[0].trim();
|
|
68
|
-
if (!/^(get|post|put|delete|options|head)$/i.test(method)) {
|
|
69
|
-
throw new Error(`Unknown method: ${method}`);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
var uri = parts.slice(1).join(" ").trim();
|
|
73
|
-
//if (!validUrl.isUri(url)) { //this is now working for some urls, eg: http://toplist.cz/count.asp?id=1128198&logo=mc&http&t=ForcaBarca.sk%2C%20informa%u010Dn%FD%20servis%20FC%20Barcelona&wi=1920&he=1080&cd=24
|
|
74
|
-
//http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
|
|
75
|
-
|
|
76
|
-
//if (!/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(url)) {
|
|
77
|
-
validateUrl(uri);
|
|
78
|
-
|
|
79
|
-
var bodySepIdx = lines.indexOf("");
|
|
80
|
-
var headerLines = ~bodySepIdx ? lines.slice(0, bodySepIdx) : lines;
|
|
81
|
-
|
|
82
|
-
var headers = headerLines.reduce((obj, line) => {
|
|
83
|
-
var parts = line.split(":");
|
|
84
|
-
var name = parts.shift().trim();
|
|
85
|
-
var value = parts.join(":").trim();
|
|
86
|
-
if (!isValidHeaderName(name)) {
|
|
87
|
-
throw new Error(`Invalid header name '${name}'`);
|
|
88
|
-
}
|
|
89
|
-
if (!value) {
|
|
90
|
-
throw new Error(`Invalid header line '${line}'`);
|
|
91
|
-
}
|
|
92
|
-
obj[name] = value;
|
|
93
|
-
return obj;
|
|
94
|
-
}, {});
|
|
95
|
-
|
|
96
|
-
var body = "";
|
|
97
|
-
if (~bodySepIdx) {
|
|
98
|
-
body = lines.slice(bodySepIdx + 1).join("\n");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return { method, url: uri, headers, body };
|
|
102
|
-
|
|
103
|
-
function isValidHeaderName(val) {
|
|
104
|
-
//https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
|
105
|
-
return /^[ -~]+$/.test(val) && !/[\(\)<>@,;:\\<>\/\[\]\?={} \t"]/.test(val);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function validateUrl(val) {
|
|
109
|
-
var parsed = url.parse(val);
|
|
110
|
-
if (!parsed.protocol || !parsed.host) {
|
|
111
|
-
throw new Error("Only absolute URLs are allowed");
|
|
112
|
-
}
|
|
113
|
-
if (!/^http:$/i.test(parsed.protocol)) {
|
|
114
|
-
throw new Error("Only HTTP protocol is allowed");
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
send(reqRes?: ReqRes) {
|
|
120
|
-
var req = this.parsedModel;
|
|
121
|
-
if (reqRes) {
|
|
122
|
-
req = {
|
|
123
|
-
url: reqRes.url,
|
|
124
|
-
method:reqRes.method,
|
|
125
|
-
headers:reqRes.reqHeaders,
|
|
126
|
-
body:reqRes.reqBody
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let options = new RequestOptions({
|
|
131
|
-
//AR:do not add no-cache, we do not want to spoil headers, we will clear the cache (disabling cache in nw.js seems to be buggy, see issues)
|
|
132
|
-
//headers: new Headers(Object.assign({ pragma: "no-cache" }, req.headers)),
|
|
133
|
-
headers: new Headers(req.headers),
|
|
134
|
-
method: req.method,
|
|
135
|
-
body: req.body
|
|
136
|
-
});
|
|
137
|
-
app.clearCache();
|
|
138
|
-
app.setProxyConfig(`http=localhost:${this.config.port},direct://;direct://`);
|
|
139
|
-
this.http.request(req.url, options)
|
|
140
|
-
.toPromise()
|
|
141
|
-
.catch(() => null) // no op
|
|
142
|
-
.then(() => app.setProxyConfig(""));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
loadSample() {
|
|
146
|
-
this.model = SAMPLE_REQUEST;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
1
|
+
import { Component, OnInit, ChangeDetectionStrategy, Input} from '@angular/core';
|
|
2
|
+
import { Http, Headers, RequestOptions, RequestMethod } from '@angular/http';
|
|
3
|
+
import { SocketService } from '../../service/socket.service';
|
|
4
|
+
import { ReqRes } from '../../model/http';
|
|
5
|
+
var app = nw.require('nw.gui').App;
|
|
6
|
+
var validUrl = nw.require('valid-url');
|
|
7
|
+
var url = nw.require('url');
|
|
8
|
+
|
|
9
|
+
var SAMPLE_REQUEST = 'POST http://jsonplaceholder.typicode.com/posts\ncontent-type: application/json\naccept: */*\n\n{"sample":1}';
|
|
10
|
+
|
|
11
|
+
@Component({
|
|
12
|
+
moduleId: module.id,
|
|
13
|
+
templateUrl: 'composer.component.html',
|
|
14
|
+
styleUrls: ['composer.component.css'],
|
|
15
|
+
selector: 'composer',
|
|
16
|
+
host: { class: 'flex-grow' },
|
|
17
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
18
|
+
})
|
|
19
|
+
export class ComposerComponent implements OnInit {
|
|
20
|
+
|
|
21
|
+
constructor(private http: Http, private socketService: SocketService) { }
|
|
22
|
+
|
|
23
|
+
config: any
|
|
24
|
+
|
|
25
|
+
ngOnInit(): void {
|
|
26
|
+
this.loadSample();
|
|
27
|
+
this.socketService.configObservable.subscribe(config => { this.config = config; })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
invalid = false
|
|
31
|
+
|
|
32
|
+
@Input()
|
|
33
|
+
set reqRes(reqRes: ReqRes) {
|
|
34
|
+
if (!reqRes) { return; }
|
|
35
|
+
this.model = `${reqRes.method} ${reqRes.url}\n${reqRes.reqHeadersStr}\n\n${reqRes.reqBody}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_message = ""
|
|
39
|
+
get message() { return this._message; }
|
|
40
|
+
set message(message) {
|
|
41
|
+
this._message = message;
|
|
42
|
+
this.invalid = !!message;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
parsedModel: any
|
|
46
|
+
|
|
47
|
+
_model: string
|
|
48
|
+
get model() { return this._model; }
|
|
49
|
+
set model(model) {
|
|
50
|
+
this._model = model;
|
|
51
|
+
try {
|
|
52
|
+
this.parsedModel = this.parse(this.model);
|
|
53
|
+
this.message = null;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
this.message = e.message;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
parse(value: string) {
|
|
60
|
+
if (!value) {
|
|
61
|
+
throw new Error("Method and URL are required");
|
|
62
|
+
}
|
|
63
|
+
var lines = value.split(/\r\n?|\n/);
|
|
64
|
+
|
|
65
|
+
var parts = lines.shift().split(" ");
|
|
66
|
+
|
|
67
|
+
var method = parts[0].trim();
|
|
68
|
+
if (!/^(get|post|put|delete|options|head)$/i.test(method)) {
|
|
69
|
+
throw new Error(`Unknown method: ${method}`);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
var uri = parts.slice(1).join(" ").trim();
|
|
73
|
+
//if (!validUrl.isUri(url)) { //this is now working for some urls, eg: http://toplist.cz/count.asp?id=1128198&logo=mc&http&t=ForcaBarca.sk%2C%20informa%u010Dn%FD%20servis%20FC%20Barcelona&wi=1920&he=1080&cd=24
|
|
74
|
+
//http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
|
|
75
|
+
|
|
76
|
+
//if (!/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(url)) {
|
|
77
|
+
validateUrl(uri);
|
|
78
|
+
|
|
79
|
+
var bodySepIdx = lines.indexOf("");
|
|
80
|
+
var headerLines = ~bodySepIdx ? lines.slice(0, bodySepIdx) : lines;
|
|
81
|
+
|
|
82
|
+
var headers = headerLines.reduce((obj, line) => {
|
|
83
|
+
var parts = line.split(":");
|
|
84
|
+
var name = parts.shift().trim();
|
|
85
|
+
var value = parts.join(":").trim();
|
|
86
|
+
if (!isValidHeaderName(name)) {
|
|
87
|
+
throw new Error(`Invalid header name '${name}'`);
|
|
88
|
+
}
|
|
89
|
+
if (!value) {
|
|
90
|
+
throw new Error(`Invalid header line '${line}'`);
|
|
91
|
+
}
|
|
92
|
+
obj[name] = value;
|
|
93
|
+
return obj;
|
|
94
|
+
}, {});
|
|
95
|
+
|
|
96
|
+
var body = "";
|
|
97
|
+
if (~bodySepIdx) {
|
|
98
|
+
body = lines.slice(bodySepIdx + 1).join("\n");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { method, url: uri, headers, body };
|
|
102
|
+
|
|
103
|
+
function isValidHeaderName(val) {
|
|
104
|
+
//https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
|
105
|
+
return /^[ -~]+$/.test(val) && !/[\(\)<>@,;:\\<>\/\[\]\?={} \t"]/.test(val);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function validateUrl(val) {
|
|
109
|
+
var parsed = url.parse(val);
|
|
110
|
+
if (!parsed.protocol || !parsed.host) {
|
|
111
|
+
throw new Error("Only absolute URLs are allowed");
|
|
112
|
+
}
|
|
113
|
+
if (!/^http:$/i.test(parsed.protocol)) {
|
|
114
|
+
throw new Error("Only HTTP protocol is allowed");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
send(reqRes?: ReqRes) {
|
|
120
|
+
var req = this.parsedModel;
|
|
121
|
+
if (reqRes) {
|
|
122
|
+
req = {
|
|
123
|
+
url: reqRes.url,
|
|
124
|
+
method:reqRes.method,
|
|
125
|
+
headers:reqRes.reqHeaders,
|
|
126
|
+
body:reqRes.reqBody
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let options = new RequestOptions({
|
|
131
|
+
//AR:do not add no-cache, we do not want to spoil headers, we will clear the cache (disabling cache in nw.js seems to be buggy, see issues)
|
|
132
|
+
//headers: new Headers(Object.assign({ pragma: "no-cache" }, req.headers)),
|
|
133
|
+
headers: new Headers(req.headers),
|
|
134
|
+
method: req.method,
|
|
135
|
+
body: req.body
|
|
136
|
+
});
|
|
137
|
+
app.clearCache();
|
|
138
|
+
app.setProxyConfig(`http=localhost:${this.config.port},direct://;direct://`);
|
|
139
|
+
this.http.request(req.url, options)
|
|
140
|
+
.toPromise()
|
|
141
|
+
.catch(() => null) // no op
|
|
142
|
+
.then(() => app.setProxyConfig(""));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
loadSample() {
|
|
146
|
+
this.model = SAMPLE_REQUEST;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
pre {
|
|
2
|
-
height: 100%;
|
|
3
|
-
font-size: 12px;
|
|
4
|
-
line-height: 12px;
|
|
5
|
-
background: white;
|
|
6
|
-
margin-bottom: 0 !important;
|
|
7
|
-
font-family:Menlo,Monaco,Consolas,"Courier New",monospace
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
pre:focus {
|
|
11
|
-
border-color: #5cb3fd;
|
|
12
|
-
outline: 0;
|
|
13
|
-
}
|
|
1
|
+
pre {
|
|
2
|
+
height: 100%;
|
|
3
|
+
font-size: 12px;
|
|
4
|
+
line-height: 12px;
|
|
5
|
+
background: white;
|
|
6
|
+
margin-bottom: 0 !important;
|
|
7
|
+
font-family:Menlo,Monaco,Consolas,"Courier New",monospace
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pre:focus {
|
|
11
|
+
border-color: #5cb3fd;
|
|
12
|
+
outline: 0;
|
|
13
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<div class="mb-2 flex-grow">
|
|
2
|
-
<pre #preNode contenteditable="true" (keyup.control.s)="trySave()" (keyup)="model=$event.target.innerText" (blur)="hBlur()" (paste)="hPaste($event)"></pre>
|
|
3
|
-
</div>
|
|
4
|
-
<div>
|
|
5
|
-
<button class="btn btn-sm btn-primary" (click)="save()" [disabled]="!dirty || invalid">Save</button>
|
|
6
|
-
<button class="btn btn-sm btn-secondary" (click)="reset()" [disabled]="!dirty" [class.btn-warning]="isConcurentModification" [title]="resetTitle">
|
|
7
|
-
Reset
|
|
8
|
-
<span *ngIf="isConcurentModification"> !!!</span>
|
|
9
|
-
</button>
|
|
10
|
-
<button class="btn btn-sm btn-secondary" (click)="showDiff()" [disabled]="!dirty">Show diff</button>
|
|
11
|
-
<button class="btn btn-sm btn-secondary" (click)="this.model = this.defaultConfigJson">Load default</button>
|
|
12
|
-
|
|
13
|
-
<em style="color:red">{{message}}</em>
|
|
14
|
-
</div>
|
|
1
|
+
<div class="mb-2 flex-grow">
|
|
2
|
+
<pre #preNode contenteditable="true" (keyup.control.s)="trySave()" (keyup)="model=$event.target.innerText" (blur)="hBlur()" (paste)="hPaste($event)"></pre>
|
|
3
|
+
</div>
|
|
4
|
+
<div>
|
|
5
|
+
<button class="btn btn-sm btn-primary" (click)="save()" [disabled]="!dirty || invalid">Save</button>
|
|
6
|
+
<button class="btn btn-sm btn-secondary" (click)="reset()" [disabled]="!dirty" [class.btn-warning]="isConcurentModification" [title]="resetTitle">
|
|
7
|
+
Reset
|
|
8
|
+
<span *ngIf="isConcurentModification"> !!!</span>
|
|
9
|
+
</button>
|
|
10
|
+
<button class="btn btn-sm btn-secondary" (click)="showDiff()" [disabled]="!dirty">Show diff</button>
|
|
11
|
+
<button class="btn btn-sm btn-secondary" (click)="this.model = this.defaultConfigJson">Load default</button>
|
|
12
|
+
|
|
13
|
+
<em style="color:red">{{message}}</em>
|
|
14
|
+
</div>
|