@sumaris-net/ngx-components 18.26.13 → 18.26.14-alpha2
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/doc/changelog.md +44 -1
- package/esm2022/public_api.mjs +2 -1
- package/esm2022/src/app/admin/users/users.mjs +2 -2
- package/esm2022/src/app/core/about/about.modal.mjs +74 -13
- package/esm2022/src/app/core/account/account.page.mjs +1 -1
- package/esm2022/src/app/core/form/entity/entity-editor.class.mjs +2 -2
- package/esm2022/src/app/core/form/properties/properties.form.mjs +20 -3
- package/esm2022/src/app/core/form/properties/properties.table.mjs +3 -3
- package/esm2022/src/app/core/menu/menu.component.mjs +3 -3
- package/esm2022/src/app/core/register/register.form.mjs +1 -1
- package/esm2022/src/app/core/services/account.service.mjs +10 -5
- package/esm2022/src/app/core/services/config/core.config.mjs +6 -1
- package/esm2022/src/app/core/services/local-settings.service.mjs +3 -7
- package/esm2022/src/app/core/services/network.service.mjs +7 -1
- package/esm2022/src/app/core/services/platform.service.mjs +37 -7
- package/esm2022/src/app/core/settings/settings.page.mjs +55 -22
- package/esm2022/src/app/core/table/async-table.class.mjs +28 -15
- package/esm2022/src/app/core/table/column/row-field.component.mjs +8 -6
- package/esm2022/src/app/core/table/entities-async-table-datasource.class.mjs +1 -1
- package/esm2022/src/app/core/table/table.class.mjs +20 -14
- package/esm2022/src/app/shared/alerts.mjs +42 -8
- package/esm2022/src/app/shared/capacitor/plugins.mjs +2 -1
- package/esm2022/src/app/shared/form/field.component.mjs +7 -3
- package/esm2022/src/app/shared/gitlab/gitlab.utils.mjs +170 -0
- package/esm2022/src/app/shared/image/gallery/image-gallery.component.mjs +5 -5
- package/esm2022/src/app/shared/image/gallery/slideshow/image-gallery-slideshow.component.mjs +2 -2
- package/esm2022/src/app/shared/image/image.service.mjs +3 -3
- package/esm2022/src/app/shared/markdown/markdown.component.mjs +6 -5
- package/esm2022/src/app/shared/markdown/markdown.utils.mjs +3 -12
- package/esm2022/src/app/shared/markdown/testing/markdown.test.mjs +2 -2
- package/esm2022/src/app/shared/material/material.animations.mjs +7 -6
- package/esm2022/src/app/shared/material/paginator/material.paginator-i18n.mjs +25 -14
- package/esm2022/src/app/shared/platforms.mjs +23 -1
- package/esm2022/src/app/shared/upload-file/upload-file-popover.component.mjs +10 -4
- package/esm2022/src/app/shared/upload-file/upload-file.component.mjs +41 -4
- package/esm2022/src/app/shared/upload-file/upload-file.model.mjs +1 -1
- package/esm2022/src/app/shared/version/versions.mjs +10 -3
- package/esm2022/src/app/social/feed/feed.component.mjs +3 -3
- package/esm2022/src/environments/environment.class.mjs +2 -1
- package/esm2022/src/environments/environment.mjs +2 -1
- package/fesm2022/sumaris-net.ngx-components.mjs +595 -144
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +14 -13
- package/public_api.d.ts +1 -0
- package/src/app/core/about/about.modal.d.ts +9 -2
- package/src/app/core/form/entity/entity-editor.class.d.ts +4 -1
- package/src/app/core/form/properties/properties.form.d.ts +1 -0
- package/src/app/core/services/account.service.d.ts +1 -1
- package/src/app/core/services/config/core.config.d.ts +1 -0
- package/src/app/core/services/platform.service.d.ts +2 -0
- package/src/app/core/settings/settings.page.d.ts +2 -1
- package/src/app/core/table/async-table.class.d.ts +15 -3
- package/src/app/core/table/column/row-field.component.d.ts +3 -2
- package/src/app/core/table/table.class.d.ts +13 -3
- package/src/app/shared/alerts.d.ts +39 -4
- package/src/app/shared/capacitor/plugins.d.ts +1 -0
- package/src/app/shared/form/field.component.d.ts +2 -1
- package/src/app/shared/gitlab/gitlab.utils.d.ts +109 -0
- package/src/app/shared/image/gallery/image-gallery.component.d.ts +4 -0
- package/src/app/shared/markdown/markdown.utils.d.ts +0 -2
- package/src/app/shared/material/paginator/material.paginator-i18n.d.ts +5 -1
- package/src/app/shared/platforms.d.ts +1 -0
- package/src/app/shared/upload-file/upload-file-popover.component.d.ts +5 -1
- package/src/app/shared/upload-file/upload-file.component.d.ts +6 -2
- package/src/app/shared/upload-file/upload-file.model.d.ts +7 -0
- package/src/assets/i18n/en-US.json +4 -1
- package/src/assets/i18n/en.json +4 -1
- package/src/assets/i18n/fr.json +4 -1
- package/src/assets/manifest.json +1 -1
- package/src/environments/environment.class.d.ts +1 -0
- package/src/theme/_ngx-components.table.scss +5 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { HttpUtils } from '../http/http.utils';
|
|
2
|
+
import { UrlUtils } from '../file/url.utils';
|
|
3
|
+
export class GitlabUtils {
|
|
4
|
+
static API_V4_PATH = '/api/v4/';
|
|
5
|
+
static API_V4_PROJECTS_PATH = '/api/v4/projects/';
|
|
6
|
+
/**
|
|
7
|
+
* Checks whether a URL points to a Gitlab instance.
|
|
8
|
+
*
|
|
9
|
+
* A URL is considered a Gitlab URL either when its host starts with 'gitlab.'
|
|
10
|
+
* (e.g. 'https://gitlab.ifremer.fr/...'), or when it is a repository file URL
|
|
11
|
+
* (see {@link isRepoFileUrl}) - in which case the host is NOT checked, since
|
|
12
|
+
* a self-hosted Gitlab instance may use a custom domain name.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* GitlabUtils.isGitlabUrl('https://gitlab.ifremer.fr/sih-public/sumaris/sumaris-app'); // true
|
|
16
|
+
* GitlabUtils.isGitlabUrl('https://my-custom-domain.com/foo/-/raw/master/README.md'); // true (repo file URL)
|
|
17
|
+
* GitlabUtils.isGitlabUrl('https://github.com/foo/bar'); // false
|
|
18
|
+
*/
|
|
19
|
+
static isGitlabUrl(url) {
|
|
20
|
+
if (!url)
|
|
21
|
+
return false;
|
|
22
|
+
return url.startsWith('https://gitlab.') || GitlabUtils.isRepoFileUrl(url);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether a URL is a Gitlab repository file URL, i.e. a URL used to
|
|
26
|
+
* view a single file in a repository ('/-/blob/') or to download its raw
|
|
27
|
+
* content ('/-/raw/').
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* GitlabUtils.isRepoFileUrl('https://gitlab.ifremer.fr/a/b/-/raw/master/README.md'); // true
|
|
31
|
+
* GitlabUtils.isRepoFileUrl('https://gitlab.ifremer.fr/a/b/-/blob/master/README.md'); // true
|
|
32
|
+
* GitlabUtils.isRepoFileUrl('https://gitlab.ifremer.fr/api/v4/projects/a%2Fb'); // false
|
|
33
|
+
*/
|
|
34
|
+
static isRepoFileUrl(url) {
|
|
35
|
+
if (!url)
|
|
36
|
+
return false;
|
|
37
|
+
return url.includes('/-/raw/') || url.includes('/-/blob/');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Converts a repository file URL into its 'raw content' equivalent, by
|
|
41
|
+
* replacing the '/-/blob/' (file viewer) or '/-/tree/' (directory viewer)
|
|
42
|
+
* segment with '/-/raw/'.
|
|
43
|
+
*
|
|
44
|
+
* If the URL is not a repository file URL (see {@link isRepoFileUrl}), it is
|
|
45
|
+
* returned unchanged.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* GitlabUtils.getRawRepoFileUrl('https://gitlab.ifremer.fr/a/b/-/blob/master/README.md');
|
|
49
|
+
* // 'https://gitlab.ifremer.fr/a/b/-/raw/master/README.md'
|
|
50
|
+
*/
|
|
51
|
+
static getRawRepoFileUrl(url) {
|
|
52
|
+
if (!GitlabUtils.isRepoFileUrl(url))
|
|
53
|
+
return url;
|
|
54
|
+
return url.replace('/-/blob/', '/-/raw/').replace('/-/tree/', '/-/raw/');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks whether a URL targets the Gitlab REST API (any endpoint under
|
|
58
|
+
* '/api/v4/'). Repository file URLs (see {@link isRepoFileUrl}) are always
|
|
59
|
+
* excluded, even if they happen to contain '/api/v4/' somewhere in their path.
|
|
60
|
+
*/
|
|
61
|
+
static isApiUrl(url) {
|
|
62
|
+
if (!url || GitlabUtils.isRepoFileUrl(url))
|
|
63
|
+
return false;
|
|
64
|
+
return url.includes(GitlabUtils.API_V4_PATH);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Checks whether a URL targets the Gitlab 'projects' REST API (any endpoint
|
|
68
|
+
* under '/api/v4/projects/'), e.g. project releases, branches, etc.
|
|
69
|
+
*/
|
|
70
|
+
static isApiProjectsUrl(url) {
|
|
71
|
+
if (!url || GitlabUtils.isRepoFileUrl(url))
|
|
72
|
+
return false;
|
|
73
|
+
return url.includes(GitlabUtils.API_V4_PROJECTS_PATH);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Checks whether a URL targets the Gitlab project releases REST API, e.g.
|
|
77
|
+
* 'https://gitlab.ifremer.fr/api/v4/projects/{id}/releases' (all releases)
|
|
78
|
+
* or 'https://gitlab.ifremer.fr/api/v4/projects/{id}/releases/{tag_name}'
|
|
79
|
+
* (a single release).
|
|
80
|
+
*/
|
|
81
|
+
static isProjectReleasesApiUrl(url) {
|
|
82
|
+
return GitlabUtils.isApiProjectsUrl(url) && url.includes('/releases');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Fetches releases from the Gitlab project releases REST API.
|
|
86
|
+
*
|
|
87
|
+
* The given URL can either target the collection endpoint
|
|
88
|
+
* ('.../releases', returning all releases), or a single release endpoint
|
|
89
|
+
* ('.../releases/{tag_name}', returning one release, wrapped into an array).
|
|
90
|
+
*
|
|
91
|
+
* @return the list of releases, an empty array if the API call failed for a
|
|
92
|
+
* single release, or `null` if the URL is not a project releases API URL.
|
|
93
|
+
*/
|
|
94
|
+
static async getProjectReleases(http, url) {
|
|
95
|
+
if (!GitlabUtils.isProjectReleasesApiUrl(url))
|
|
96
|
+
return null;
|
|
97
|
+
// Many releases API urls end with /releases
|
|
98
|
+
if (url.trim().endsWith('/releases')) {
|
|
99
|
+
return await HttpUtils.getJson(http, url);
|
|
100
|
+
}
|
|
101
|
+
// One release API url
|
|
102
|
+
try {
|
|
103
|
+
const release = await HttpUtils.getJson(http, url);
|
|
104
|
+
return release ? [release] : [];
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
console.error('Failed to fetch release from the Gitlab API ' + url, err);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extracts the URL-encoded project path (e.g. 'group%2Fsubgroup%2Fproject')
|
|
113
|
+
* from a Gitlab 'projects' API URL. The returned value is NOT decoded, so it
|
|
114
|
+
* still contains '%2F' in place of '/'.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* GitlabUtils.getProjectNameFromApiUrl('https://gitlab.ifremer.fr/api/v4/projects/sih-public%2Fsumaris%2Fsumaris-app/releases');
|
|
118
|
+
* // 'sih-public%2Fsumaris%2Fsumaris-app'
|
|
119
|
+
*/
|
|
120
|
+
static getProjectNameFromApiUrl(url) {
|
|
121
|
+
if (!url?.includes(GitlabUtils.API_V4_PROJECTS_PATH))
|
|
122
|
+
return null;
|
|
123
|
+
return url.slice(url.indexOf(GitlabUtils.API_V4_PROJECTS_PATH) + GitlabUtils.API_V4_PROJECTS_PATH.length)
|
|
124
|
+
.split('/', 2)[0];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the Gitlab web page associated to a URL:
|
|
128
|
+
* - for an API 'projects' URL (see {@link isApiUrl}), this is the project's
|
|
129
|
+
* home page, e.g. 'https://gitlab.ifremer.fr/sih-public/sumaris/sumaris-app';
|
|
130
|
+
* - for a repository file URL (see {@link isRepoFileUrl}), this is the
|
|
131
|
+
* project's raw content base path (e.g. '.../my-project/-/raw/'), since
|
|
132
|
+
* the project path cannot be reliably distinguished from the file path
|
|
133
|
+
* once slashes are un-encoded.
|
|
134
|
+
*
|
|
135
|
+
* @return `null` if the project page cannot be resolved from the given URL.
|
|
136
|
+
*/
|
|
137
|
+
static getProjectPage(url) {
|
|
138
|
+
if (GitlabUtils.isRepoFileUrl(url)) {
|
|
139
|
+
const fixUrl = GitlabUtils.getRawRepoFileUrl(url);
|
|
140
|
+
const index = fixUrl.indexOf('/-/raw/');
|
|
141
|
+
if (index >= 0)
|
|
142
|
+
return fixUrl.slice(0, index + '/-/raw/'.length);
|
|
143
|
+
}
|
|
144
|
+
else if (GitlabUtils.isApiUrl(url)) {
|
|
145
|
+
const index = url.indexOf(GitlabUtils.API_V4_PROJECTS_PATH);
|
|
146
|
+
if (index >= 0) {
|
|
147
|
+
const baseUrl = url.slice(0, index);
|
|
148
|
+
const projectName = GitlabUtils.getProjectNameFromApiUrl(url);
|
|
149
|
+
return UrlUtils.concat(baseUrl, projectName.replace(/%2F/g, '/'));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Builds the URL of the Gitlab web page listing a project's releases (e.g.
|
|
156
|
+
* 'https://gitlab.ifremer.fr/sih-public/sumaris/sumaris-app/-/releases'),
|
|
157
|
+
* optionally anchored on a specific version/tag.
|
|
158
|
+
*
|
|
159
|
+
* @param url any URL from which the project page can be resolved (see {@link getProjectPage})
|
|
160
|
+
* @param version optional tag name (e.g. '2.16.2') appended to the releases page
|
|
161
|
+
* @return `null` if the project page cannot be resolved from the given URL.
|
|
162
|
+
*/
|
|
163
|
+
static getProjectReleasePage(url, version) {
|
|
164
|
+
const basePath = GitlabUtils.getProjectPage(url);
|
|
165
|
+
if (!basePath)
|
|
166
|
+
return null;
|
|
167
|
+
return UrlUtils.concat(UrlUtils.concat(basePath, '/-/releases'), version);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2l0bGFiLnV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9zaGFyZWQvZ2l0bGFiL2dpdGxhYi51dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sb0JBQW9CLENBQUM7QUFFN0MsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBUTdDLE1BQU0sT0FBTyxXQUFXO0lBQ3RCLE1BQU0sQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxvQkFBb0IsR0FBRyxtQkFBbUIsQ0FBQztJQUVsRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxNQUFNLENBQUMsV0FBVyxDQUFDLEdBQVc7UUFDNUIsSUFBSSxDQUFDLEdBQUc7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUN2QixPQUFPLEdBQUcsQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQUMsSUFBSSxXQUFXLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSCxNQUFNLENBQUMsYUFBYSxDQUFDLEdBQVc7UUFDOUIsSUFBSSxDQUFDLEdBQUc7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUN2QixPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxNQUFNLENBQUMsaUJBQWlCLENBQUMsR0FBVztRQUNsQyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUM7WUFBRSxPQUFPLEdBQUcsQ0FBQztRQUNoRCxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFD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QUMsRUFBRSxDQUFDO1lBQ3JDLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDLENBQUM7WUFDNUQsSUFBSSxLQUFLLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQ2YsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sV0FBVyxHQUFHLFdBQVcsQ0FBQyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDOUQsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ3BFLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxNQUFNLENBQUMscUJBQXFCLENBQUMsR0FBVyxFQUFFLE9BQWdCO1FBQ3hELE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPLElBQUksQ0FBQztRQUUzQixPQUFPLFFBQVEsQ0FBQyxNQUFNLENBQ3BCLFFBQVEsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLGFBQWEsQ0FBQyxFQUN4QyxPQUFPLENBQ1IsQ0FBQztJQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0h0dHBVdGlsc30gZnJvbSAnLi4vaHR0cC9odHRwLnV0aWxzJztcbmltcG9ydCB7SHR0cENsaWVudH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgVXJsVXRpbHMgfSBmcm9tICcuLi9maWxlL3VybC51dGlscyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUdpdGxhYlJlbGVhc2Uge1xuICBuYW1lOiBzdHJpbmc7XG4gIHRhZ19uYW1lOiBzdHJpbmc7XG4gIGRlc2NyaXB0aW9uOiBzdHJpbmdcbn1cblxuZXhwb3J0IGNsYXNzIEdpdGxhYlV0aWxzIHtcbiAgc3RhdGljIEFQSV9WNF9QQVRIID0gJy9hcGkvdjQvJztcbiAgc3RhdGljIEFQSV9WNF9QUk9KRUNUU19QQVRIID0gJy9hcGkvdjQvcHJvamVjdHMvJztcblxuICAvKipcbiAgICogQ2hlY2tzIHdoZXRoZXIgYSBVUkwgcG9pbnRzIHRvIGEgR2l0bGFiIGluc3RhbmNlLlxuICAgKlxuICAgKiBBIFVSTCBpcyBjb25zaWRlcmVkIGEgR2l0bGFiIFVSTCBlaXRoZXIgd2hlbiBpdHMgaG9zdCBzdGFydHMgd2l0aCAnZ2l0bGFiLidcbiAgICogKGUuZy4gJ2h0dHBzOi8vZ2l0bGFiLmlmcmVtZXIuZnIvLi4uJyksIG9yIHdoZW4gaXQgaXMgYSByZXBvc2l0b3J5IGZpbGUgVVJMXG4gICAqIChzZWUge0BsaW5rIGlzUmVwb0ZpbGVVcmx9KSAtIGluIHdoaWNoIGNhc2UgdGhlIGhvc3QgaXMgTk9UIGNoZWNrZWQsIHNpbmNlXG4gICAqIGEgc2VsZi1ob3N0ZWQgR2l0bGFiIGluc3RhbmNlIG1heSB1c2UgYSBjdXN0b20gZG9tYWluIG5hbWUuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIEdpdGxhYlV0aWxzLmlzR2l0bGFiVXJsKCdodHRwczovL2dpdGxhYi5pZnJlbWVyLmZyL3NpaC1wdWJsaWMvc3VtYXJpcy9zdW1hcmlzLWFwcCcpOyAvLyB0cnVlXG4gICAqIEdpdGxhYlV0aWxzLmlzR2l0bGFiVXJsKCdodHRwczovL215LWN1c3RvbS1kb21haW4uY29tL2Zvby8tL3Jhdy9tYXN0ZXIvUkVBRE1FLm1kJyk7IC8vIHRydWUgKHJlcG8gZmlsZSBVUkwpXG4gICAqIEdpdGxhYlV0aWxzLmlzR2l0bGFiVXJsKCdodHRwczovL2dpdGh1Yi5jb20vZm9vL2JhcicpOyAvLyBmYWxzZVxuICAgKi9cbiAgc3RhdGljIGlzR2l0bGFiVXJsKHVybDogc3RyaW5nKSB7XG4gICAgaWYgKCF1cmwpIHJldHVybiBmYWxzZTtcbiAgICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vZ2l0bGFiLicpIHx8IEdpdGxhYlV0aWxzLmlzUmVwb0ZpbGVVcmwodXJsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3Mgd2hldGhlciBhIFVSTCBpcyBhIEdpdGxhYiByZXBvc2l0b3J5IGZpbGUgVVJMLCBpLmUuIGEgVVJMIHVzZWQgdG9cbiAgICogdmlldyBhIHNpbmdsZSBmaWxlIGluIGEgcmVwb3NpdG9yeSAoJy8tL2Jsb2IvJykgb3IgdG8gZG93bmxvYWQgaXRzIHJhd1xuICAgKiBjb250ZW50ICgnLy0vcmF3LycpLlxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBHaXRsYWJVdGlscy5pc1JlcG9GaWxlVXJsKCdodHRwczovL2dpdGxhYi5pZnJlbWVyLmZyL2EvYi8tL3Jhdy9tYXN0ZXIvUkVBRE1FLm1kJyk7IC8vIHRydWVcbiAgICogR2l0bGFiVXRpbHMuaXNSZXBvRmlsZVVybCgnaHR0cHM6Ly9naXRsYWIuaWZyZW1lci5mci9hL2IvLS9ibG9iL21hc3Rlci9SRUFETUUubWQnKTsgLy8gdHJ1ZVxuICAgKiBHaXRsYWJVdGlscy5pc1JlcG9GaWxlVXJsKCdodHRwczovL2dpdGxhYi5pZnJlbWVyLmZyL2FwaS92NC9wcm9qZWN0cy9hJTJGYicpOyAvLyBmYWxzZVxuICAgKi9cbiAgc3RhdGljIGlzUmVwb0ZpbGVVcmwodXJsOiBzdHJpbmcpIHtcbiAgICBpZiAoIXVybCkgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiB1cmwuaW5jbHVkZXMoJy8tL3Jhdy8nKSB8fCB1cmwuaW5jbHVkZXMoJy8tL2Jsb2IvJyk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydHMgYSByZXBvc2l0b3J5IGZpbGUgVVJMIGludG8gaXRzICdyYXcgY29udGVudCcgZXF1aXZhbGVudCwgYnlcbiAgICogcmVwbGFjaW5nIHRoZSAnLy0vYmxvYi8nIChmaWxlIHZpZXdlcikgb3IgJy8tL3RyZWUvJyAoZGlyZWN0b3J5IHZpZXdlcilcbiAgICogc2VnbWVudCB3aXRoICcvLS9yYXcvJy5cbiAgICpcbiAgICogSWYgdGhlIFVSTCBpcyBub3QgYSByZXBvc2l0b3J5IGZpbGUgVVJMIChzZWUge0BsaW5rIGlzUmVwb0ZpbGVVcmx9KSwgaXQgaXNcbiAgICogcmV0dXJuZWQgdW5jaGFuZ2VkLlxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBHaXRsYWJVdGlscy5nZXRSYXdSZXBvRmlsZVVybCgnaHR0cHM6Ly9naXRsYWIuaWZyZW1lci5mci9hL2IvLS9ibG9iL21hc3Rlci9SRUFETUUubWQnKTtcbiAgICogLy8gJ2h0dHBzOi8vZ2l0bGFiLmlmcmVtZXIuZnIvYS9iLy0vcmF3L21hc3Rlci9SRUFETUUubWQnXG4gICAqL1xuICBzdGF0aWMgZ2V0UmF3UmVwb0ZpbGVVcmwodXJsOiBzdHJpbmcpIHtcbiAgICBpZiAoIUdpdGxhYlV0aWxzLmlzUmVwb0ZpbGVVcmwodXJsKSkgcmV0dXJuIHVybDtcbiAgICByZXR1cm4gdXJsLnJlcGxhY2UoJy8tL2Jsb2IvJywgJy8tL3Jhdy8nKS5yZXBsYWNlKCcvLS90cmVlLycsICcvLS9yYXcvJyk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIHdoZXRoZXIgYSBVUkwgdGFyZ2V0cyB0aGUgR2l0bGFiIFJFU1QgQVBJIChhbnkgZW5kcG9pbnQgdW5kZXJcbiAgICogJy9hcGkvdjQvJykuIFJlcG9zaXRvcnkgZmlsZSBVUkxzIChzZWUge0BsaW5rIGlzUmVwb0ZpbGVVcmx9KSBhcmUgYWx3YXlzXG4gICAqIGV4Y2x1ZGVkLCBldmVuIGlmIHRoZXkgaGFwcGVuIHRvIGNvbnRhaW4gJy9hcGkvdjQvJyBzb21ld2hlcmUgaW4gdGhlaXIgcGF0aC5cbiAgICovXG4gIHN0YXRpYyBpc0FwaVVybCh1cmw6IHN0cmluZykge1xuICAgIGlmICghdXJsIHx8IEdpdGxhYlV0aWxzLmlzUmVwb0ZpbGVVcmwodXJsKSkgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiB1cmwuaW5jbHVkZXMoR2l0bGFiVXRpbHMuQVBJX1Y0X1BBVEgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyB3aGV0aGVyIGEgVVJMIHRhcmdldHMgdGhlIEdpdGxhYiAncHJvamVjdHMnIFJFU1QgQVBJIChhbnkgZW5kcG9pbnRcbiAgICogdW5kZXIgJy9hcGkvdjQvcHJvamVjdHMvJyksIGUuZy4gcHJvamVjdCByZWxlYXNlcywgYnJhbmNoZXMsIGV0Yy5cbiAgICovXG4gIHN0YXRpYyBpc0FwaVByb2plY3RzVXJsKHVybDogc3RyaW5nKSB7XG4gICAgaWYgKCF1cmwgfHwgR2l0bGFiVXRpbHMuaXNSZXBvRmlsZVVybCh1cmwpKSByZXR1cm4gZmFsc2U7XG4gICAgcmV0dXJuIHVybC5pbmNsdWRlcyhHaXRsYWJVdGlscy5BUElfVjRfUFJPSkVDVFNfUEFUSCk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIHdoZXRoZXIgYSBVUkwgdGFyZ2V0cyB0aGUgR2l0bGFiIHByb2plY3QgcmVsZWFzZXMgUkVTVCBBUEksIGUuZy5cbiAgICogJ2h0dHBzOi8vZ2l0bGFiLmlmcmVtZXIuZnIvYXBpL3Y0L3Byb2plY3RzL3tpZH0vcmVsZWFzZXMnIChhbGwgcmVsZWFzZXMpXG4gICAqIG9yICdodHRwczovL2dpdGxhYi5pZnJlbWVyLmZyL2FwaS92NC9wcm9qZWN0cy97aWR9L3JlbGVhc2VzL3t0YWdfbmFtZX0nXG4gICAqIChhIHNpbmdsZSByZWxlYXNlKS5cbiAgICovXG4gIHN0YXRpYyBpc1Byb2plY3RSZWxlYXNlc0FwaVVybCh1cmw6IHN0cmluZykge1xuICAgIHJldHVybiBHaXRsYWJVdGlscy5pc0FwaVByb2plY3RzVXJsKHVybCkgJiYgdXJsLmluY2x1ZGVzKCcvcmVsZWFzZXMnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaGVzIHJlbGVhc2VzIGZyb20gdGhlIEdpdGxhYiBwcm9qZWN0IHJlbGVhc2VzIFJFU1QgQVBJLlxuICAgKlxuICAgKiBUaGUgZ2l2ZW4gVVJMIGNhbiBlaXRoZXIgdGFyZ2V0IHRoZSBjb2xsZWN0aW9uIGVuZHBvaW50XG4gICAqICgnLi4uL3JlbGVhc2VzJywgcmV0dXJuaW5nIGFsbCByZWxlYXNlcyksIG9yIGEgc2luZ2xlIHJlbGVhc2UgZW5kcG9pbnRcbiAgICogKCcuLi4vcmVsZWFzZXMve3RhZ19uYW1lfScsIHJldHVybmluZyBvbmUgcmVsZWFzZSwgd3JhcHBlZCBpbnRvIGFuIGFycmF5KS5cbiAgICpcbiAgICogQHJldHVybiB0aGUgbGlzdCBvZiByZWxlYXNlcywgYW4gZW1wdHkgYXJyYXkgaWYgdGhlIEFQSSBjYWxsIGZhaWxlZCBmb3IgYVxuICAgKiAgIHNpbmdsZSByZWxlYXNlLCBvciBgbnVsbGAgaWYgdGhlIFVSTCBpcyBub3QgYSBwcm9qZWN0IHJlbGVhc2VzIEFQSSBVUkwuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZ2V0UHJvamVjdFJlbGVhc2VzKGh0dHA6IEh0dHBDbGllbnQsIHVybDogc3RyaW5nKSB7XG4gICAgaWYgKCFHaXRsYWJVdGlscy5pc1Byb2plY3RSZWxlYXNlc0FwaVVybCh1cmwpKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIE1hbnkgcmVsZWFzZXMgQVBJIHVybHMgZW5kIHdpdGggL3JlbGVhc2VzXG4gICAgaWYgKHVybC50cmltKCkuZW5kc1dpdGgoJy9yZWxlYXNlcycpKSB7XG4gICAgICByZXR1cm4gYXdhaXQgSHR0cFV0aWxzLmdldEpzb248SUdpdGxhYlJlbGVhc2VbXT4oaHR0cCwgdXJsKTtcbiAgICB9XG5cbiAgICAvLyBPbmUgcmVsZWFzZSBBUEkgdXJsXG4gICAgdHJ5IHtcbiAgICBjb25zdCByZWxlYXNlID0gYXdhaXQgSHR0cFV0aWxzLmdldEpzb248SUdpdGxhYlJlbGVhc2U+KGh0dHAsIHVybCk7XG4gICAgcmV0dXJuIHJlbGVhc2UgPyBbcmVsZWFzZV0gOiBbXTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBmZXRjaCByZWxlYXNlIGZyb20gdGhlIEdpdGxhYiBBUEkgJyArIHVybCwgZXJyKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFeHRyYWN0cyB0aGUgVVJMLWVuY29kZWQgcHJvamVjdCBwYXRoIChlLmcuICdncm91cCUyRnN1Ymdyb3VwJTJGcHJvamVjdCcpXG4gICAqIGZyb20gYSBHaXRsYWIgJ3Byb2plY3RzJyBBUEkgVVJMLiBUaGUgcmV0dXJuZWQgdmFsdWUgaXMgTk9UIGRlY29kZWQsIHNvIGl0XG4gICAqIHN0aWxsIGNvbnRhaW5zICclMkYnIGluIHBsYWNlIG9mICcvJy5cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogR2l0bGFiVXRpbHMuZ2V0UHJvamVjdE5hbWVGcm9tQXBpVXJsKCdodHRwczovL2dpdGxhYi5pZnJlbWVyLmZyL2FwaS92NC9wcm9qZWN0cy9zaWgtcHVibGljJTJGc3VtYXJpcyUyRnN1bWFyaXMtYXBwL3JlbGVhc2VzJyk7XG4gICAqIC8vICdzaWgtcHVibGljJTJGc3VtYXJpcyUyRnN1bWFyaXMtYXBwJ1xuICAgKi9cbiAgc3RhdGljIGdldFByb2plY3ROYW1lRnJvbUFwaVVybCh1cmw6IHN0cmluZykge1xuICAgIGlmICghdXJsPy5pbmNsdWRlcyhHaXRsYWJVdGlscy5BUElfVjRfUFJPSkVDVFNfUEFUSCkpIHJldHVybiBudWxsO1xuXG4gICAgcmV0dXJuIHVybC5zbGljZSh1cmwuaW5kZXhPZihHaXRsYWJVdGlscy5BUElfVjRfUFJPSkVDVFNfUEFUSCkgKyBHaXRsYWJVdGlscy5BUElfVjRfUFJPSkVDVFNfUEFUSC5sZW5ndGgpXG4gICAgICAuc3BsaXQoJy8nLCAyKVswXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlcyB0aGUgR2l0bGFiIHdlYiBwYWdlIGFzc29jaWF0ZWQgdG8gYSBVUkw6XG4gICAqIC0gZm9yIGFuIEFQSSAncHJvamVjdHMnIFVSTCAoc2VlIHtAbGluayBpc0FwaVVybH0pLCB0aGlzIGlzIHRoZSBwcm9qZWN0J3NcbiAgICogICBob21lIHBhZ2UsIGUuZy4gJ2h0dHBzOi8vZ2l0bGFiLmlmcmVtZXIuZnIvc2loLXB1YmxpYy9zdW1hcmlzL3N1bWFyaXMtYXBwJztcbiAgICogLSBmb3IgYSByZXBvc2l0b3J5IGZpbGUgVVJMIChzZWUge0BsaW5rIGlzUmVwb0ZpbGVVcmx9KSwgdGhpcyBpcyB0aGVcbiAgICogICBwcm9qZWN0J3MgcmF3IGNvbnRlbnQgYmFzZSBwYXRoIChlLmcuICcuLi4vbXktcHJvamVjdC8tL3Jhdy8nKSwgc2luY2VcbiAgICogICB0aGUgcHJvamVjdCBwYXRoIGNhbm5vdCBiZSByZWxpYWJseSBkaXN0aW5ndWlzaGVkIGZyb20gdGhlIGZpbGUgcGF0aFxuICAgKiAgIG9uY2Ugc2xhc2hlcyBhcmUgdW4tZW5jb2RlZC5cbiAgICpcbiAgICogQHJldHVybiBgbnVsbGAgaWYgdGhlIHByb2plY3QgcGFnZSBjYW5ub3QgYmUgcmVzb2x2ZWQgZnJvbSB0aGUgZ2l2ZW4gVVJMLlxuICAgKi9cbiAgc3RhdGljIGdldFByb2plY3RQYWdlKHVybDogc3RyaW5nKSB7XG4gICAgaWYgKEdpdGxhYlV0aWxzLmlzUmVwb0ZpbGVVcmwodXJsKSkge1xuICAgICAgY29uc3QgZml4VXJsID0gR2l0bGFiVXRpbHMuZ2V0UmF3UmVwb0ZpbGVVcmwodXJsKTtcbiAgICAgIGNvbnN0IGluZGV4ID0gZml4VXJsLmluZGV4T2YoJy8tL3Jhdy8nKTtcbiAgICAgIGlmIChpbmRleCA+PSAwKSByZXR1cm4gZml4VXJsLnNsaWNlKDAsIGluZGV4ICsgJy8tL3Jhdy8nLmxlbmd0aCk7XG4gICAgfSBlbHNlIGlmIChHaXRsYWJVdGlscy5pc0FwaVVybCh1cmwpKSB7XG4gICAgICBjb25zdCBpbmRleCA9IHVybC5pbmRleE9mKEdpdGxhYlV0aWxzLkFQSV9WNF9QUk9KRUNUU19QQVRIKTtcbiAgICAgIGlmIChpbmRleCA+PSAwKSB7XG4gICAgICAgIGNvbnN0IGJhc2VVcmwgPSB1cmwuc2xpY2UoMCwgaW5kZXgpO1xuICAgICAgICBjb25zdCBwcm9qZWN0TmFtZSA9IEdpdGxhYlV0aWxzLmdldFByb2plY3ROYW1lRnJvbUFwaVVybCh1cmwpO1xuICAgICAgICByZXR1cm4gVXJsVXRpbHMuY29uY2F0KGJhc2VVcmwsIHByb2plY3ROYW1lLnJlcGxhY2UoLyUyRi9nLCAnLycpKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIFVSTCBvZiB0aGUgR2l0bGFiIHdlYiBwYWdlIGxpc3RpbmcgYSBwcm9qZWN0J3MgcmVsZWFzZXMgKGUuZy5cbiAgICogJ2h0dHBzOi8vZ2l0bGFiLmlmcmVtZXIuZnIvc2loLXB1YmxpYy9zdW1hcmlzL3N1bWFyaXMtYXBwLy0vcmVsZWFzZXMnKSxcbiAgICogb3B0aW9uYWxseSBhbmNob3JlZCBvbiBhIHNwZWNpZmljIHZlcnNpb24vdGFnLlxuICAgKlxuICAgKiBAcGFyYW0gdXJsIGFueSBVUkwgZnJvbSB3aGljaCB0aGUgcHJvamVjdCBwYWdlIGNhbiBiZSByZXNvbHZlZCAoc2VlIHtAbGluayBnZXRQcm9qZWN0UGFnZX0pXG4gICAqIEBwYXJhbSB2ZXJzaW9uIG9wdGlvbmFsIHRhZyBuYW1lIChlLmcuICcyLjE2LjInKSBhcHBlbmRlZCB0byB0aGUgcmVsZWFzZXMgcGFnZVxuICAgKiBAcmV0dXJuIGBudWxsYCBpZiB0aGUgcHJvamVjdCBwYWdlIGNhbm5vdCBiZSByZXNvbHZlZCBmcm9tIHRoZSBnaXZlbiBVUkwuXG4gICAqL1xuICBzdGF0aWMgZ2V0UHJvamVjdFJlbGVhc2VQYWdlKHVybDogc3RyaW5nLCB2ZXJzaW9uPzogc3RyaW5nKSB7XG4gICAgY29uc3QgYmFzZVBhdGggPSBHaXRsYWJVdGlscy5nZXRQcm9qZWN0UGFnZSh1cmwpO1xuICAgIGlmICghYmFzZVBhdGgpIHJldHVybiBudWxsO1xuXG4gICAgcmV0dXJuIFVybFV0aWxzLmNvbmNhdChcbiAgICAgIFVybFV0aWxzLmNvbmNhdChiYXNlUGF0aCwgJy8tL3JlbGVhc2VzJyksXG4gICAgICB2ZXJzaW9uXG4gICAgKTtcbiAgfVxufVxuIl19
|