turbogui-angular 15.2.0 → 15.3.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/esm2022/main/controller/dialog.service.mjs +3 -3
- package/esm2022/main/controller/locales.service.mjs +492 -0
- package/esm2022/main/controller/turbo-api-caller.service.mjs +41 -9
- package/esm2022/main/controller/views.service.mjs +20 -17
- package/esm2022/main/model/classes/View.mjs +2 -2
- package/esm2022/main/view/components/busy-state-base/busy-state-base.component.mjs +1 -1
- package/esm2022/main/view/components/button-container/button-container.component.mjs +1 -1
- package/esm2022/main/view/components/button-image/button-image.component.mjs +1 -1
- package/esm2022/main/view/components/dialog-date-selection/dialog-date-selection.component.mjs +1 -1
- package/esm2022/main/view/components/dialog-error/dialog-error.component.mjs +1 -1
- package/esm2022/main/view/components/dialog-multiple-option/dialog-multiple-option.component.mjs +1 -1
- package/esm2022/main/view/components/dialog-single-input/dialog-single-input.component.mjs +25 -8
- package/esm2022/public_api.mjs +2 -2
- package/fesm2022/turbogui-angular.mjs +559 -41
- package/fesm2022/turbogui-angular.mjs.map +1 -1
- package/main/controller/dialog.service.d.ts +2 -2
- package/main/controller/locales.service.d.ts +295 -0
- package/main/controller/locales.service.d.ts.map +1 -0
- package/main/controller/turbo-api-caller.service.d.ts +28 -8
- package/main/controller/turbo-api-caller.service.d.ts.map +1 -1
- package/main/controller/views.service.d.ts +11 -10
- package/main/controller/views.service.d.ts.map +1 -1
- package/main/model/classes/View.d.ts +1 -1
- package/main/view/components/busy-state-base/busy-state-base.component.d.ts +0 -9
- package/main/view/components/busy-state-base/busy-state-base.component.d.ts.map +1 -1
- package/main/view/components/button-container/button-container.component.d.ts +0 -3
- package/main/view/components/button-container/button-container.component.d.ts.map +1 -1
- package/main/view/components/button-image/button-image.component.d.ts +0 -3
- package/main/view/components/button-image/button-image.component.d.ts.map +1 -1
- package/main/view/components/dialog-date-selection/dialog-date-selection.component.d.ts +0 -3
- package/main/view/components/dialog-date-selection/dialog-date-selection.component.d.ts.map +1 -1
- package/main/view/components/dialog-error/dialog-error.component.d.ts +0 -3
- package/main/view/components/dialog-error/dialog-error.component.d.ts.map +1 -1
- package/main/view/components/dialog-multiple-option/dialog-multiple-option.component.d.ts +0 -3
- package/main/view/components/dialog-multiple-option/dialog-multiple-option.component.d.ts.map +1 -1
- package/main/view/components/dialog-single-input/dialog-single-input.component.d.ts +5 -5
- package/main/view/components/dialog-single-input/dialog-single-input.component.d.ts.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +1 -1
- package/public_api.d.ts.map +1 -1
- package/esm2022/main/controller/localization.service.mjs +0 -25
- package/main/controller/localization.service.d.ts +0 -10
- package/main/controller/localization.service.d.ts.map +0 -1
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TurboGUI is A library that helps with the most common and generic UI elements and functionalities
|
|
3
|
+
*
|
|
4
|
+
* Website : -> http://www.turbogui.org
|
|
5
|
+
* License : -> Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License.
|
|
6
|
+
* License Url : -> http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
* CopyRight : -> Copyright 2018 Edertone Advanded Solutions. https://www.edertone.com
|
|
8
|
+
*/
|
|
9
|
+
import { Injectable } from '@angular/core';
|
|
10
|
+
import { SingletoneStrictClass } from '../model/classes/SingletoneStrictClass';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
/**
|
|
13
|
+
* Manages application text translations and languages
|
|
14
|
+
*/
|
|
15
|
+
export class LocalesService extends SingletoneStrictClass {
|
|
16
|
+
/**
|
|
17
|
+
* Fully featured translation manager to be used with any application that requires text internationalization.
|
|
18
|
+
*/
|
|
19
|
+
constructor() {
|
|
20
|
+
super(LocalesService);
|
|
21
|
+
/**
|
|
22
|
+
* if the class has been correctly initialized and translations have been correctly loaded
|
|
23
|
+
*/
|
|
24
|
+
this._isInitialized = false;
|
|
25
|
+
/**
|
|
26
|
+
* @see getLocales()
|
|
27
|
+
*/
|
|
28
|
+
this._locales = [];
|
|
29
|
+
/**
|
|
30
|
+
* @see getLanguages()
|
|
31
|
+
*/
|
|
32
|
+
this._languages = [];
|
|
33
|
+
/**
|
|
34
|
+
* Stores all the loaded localization data by library name, bundle name, key and locales
|
|
35
|
+
*/
|
|
36
|
+
this._loadedTranslations = {};
|
|
37
|
+
/**
|
|
38
|
+
* Stores a memory cache to improve performance when outputing translations
|
|
39
|
+
*/
|
|
40
|
+
this._keyValuesCache = {};
|
|
41
|
+
/**
|
|
42
|
+
* @see setWildCardsFormat()
|
|
43
|
+
*/
|
|
44
|
+
this._wildCardsFormat = '{N}';
|
|
45
|
+
/**
|
|
46
|
+
* @see setMissingKeyFormat()
|
|
47
|
+
*/
|
|
48
|
+
this._missingKeyFormat = '$exception';
|
|
49
|
+
/**
|
|
50
|
+
* Stores a hash value that is used to improve the performance for translation t() methods.
|
|
51
|
+
* This is computed based on _wildCardsFormat plus _missingKeyFormat plus the current primary locale
|
|
52
|
+
* Methods that change these values will recalculate the hash string, so when calling translation methods, the
|
|
53
|
+
* performance will be as fast as possible.
|
|
54
|
+
*/
|
|
55
|
+
this._cacheHashBaseString = '';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wildcards are string fragments that are placed inside the translated texts. Their main purpose is to be replaced at
|
|
59
|
+
* runtime by custom values like for example a user name, a date, a numeric value, etc..
|
|
60
|
+
*
|
|
61
|
+
* This class helps with this process by including a parameter called 'toReplace' on all ->t methods which allows us
|
|
62
|
+
* to specify a string or list of strings that will replace the respective wildcards on the translated text. Each wildcard
|
|
63
|
+
* must follow the format specified here, and contain a numeric digit that will be used to find the replacement text at the
|
|
64
|
+
* 'toReplace' list. For example, if we define $N as the wildcard format, and we have a translation that contains $0, $1, $2,
|
|
65
|
+
* $0 will be replaced with the first element on toReplace, $1 with the second and so.
|
|
66
|
+
*
|
|
67
|
+
* We usually set this before initializing the class translation data
|
|
68
|
+
*
|
|
69
|
+
* Notice that N is mandayory on the wildcards format and the first index value is 0.
|
|
70
|
+
*
|
|
71
|
+
* @param value The wildcards format we want to set
|
|
72
|
+
*
|
|
73
|
+
* @returns The value that's been set
|
|
74
|
+
*/
|
|
75
|
+
setWildCardsFormat(value) {
|
|
76
|
+
if (!value.includes('N')) {
|
|
77
|
+
throw new Error("N is mandatory to replace wildcards");
|
|
78
|
+
}
|
|
79
|
+
this._cacheHashBaseString = value + this._missingKeyFormat + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
80
|
+
this._wildCardsFormat = value;
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Defines the behaviour for get(), getStartCase(), etc... methods when a key is not found on
|
|
85
|
+
* a bundle or the bundle does not exist
|
|
86
|
+
*
|
|
87
|
+
* If missingKeyFormat is an empty string, all missing keys will return an empty value (not recommended)
|
|
88
|
+
*
|
|
89
|
+
* If missingKeyFormat contains a string, that string will be always returned for missing keys
|
|
90
|
+
*
|
|
91
|
+
* If missingKeyFormat contains a string with one of the following predefined wildcards:<br>
|
|
92
|
+
* - $key will be replaced with key name. Example: get("NAME") will output [NAME] if key is not found and missingKeyFormat = '[$key]'<br>
|
|
93
|
+
* - $exception (default value) will throw an exception with the problem cause description.
|
|
94
|
+
*
|
|
95
|
+
* @param value The missing key format we want to set
|
|
96
|
+
*
|
|
97
|
+
* @returns The value that's been set
|
|
98
|
+
*/
|
|
99
|
+
setMissingKeyFormat(value) {
|
|
100
|
+
this._cacheHashBaseString = this._wildCardsFormat + value + ((this._locales.length > 0) ? this._locales[0] : '');
|
|
101
|
+
this._missingKeyFormat = value;
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @see setMissingKeyFormat()
|
|
106
|
+
*/
|
|
107
|
+
getMissingKeyFormat() {
|
|
108
|
+
return this._missingKeyFormat;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Initializes the translation system by loading and parsing bundle files from the specified translations path.
|
|
112
|
+
*
|
|
113
|
+
* @param translationsPath - Url where the translations Json structure of libraries/bundles/locales/keys is available.
|
|
114
|
+
* @param locales An array of locale codes (e.g., ['en_US', 'es_ES', 'fr_FR']) to load. These will be added to the translation
|
|
115
|
+
* path using the following format: translationsPath/en_US-es_ES-fr_FR. The order of this array will determine the
|
|
116
|
+
* translation priority
|
|
117
|
+
* @param parameters Any extra parameters to be attached to the translationsPath after the locales like /param1/param2/ etc
|
|
118
|
+
*
|
|
119
|
+
* @return A promise that will resolve if the translations get correctly loaded, or reject with an error if load fails
|
|
120
|
+
*/
|
|
121
|
+
initialize(translationsPath, locales, parameters) {
|
|
122
|
+
this._isInitialized = false;
|
|
123
|
+
this._loadedTranslations = {};
|
|
124
|
+
// Validate received locales are correct
|
|
125
|
+
for (const locale of locales) {
|
|
126
|
+
this._validateLocaleString(locale);
|
|
127
|
+
}
|
|
128
|
+
let translationsFullPath = translationsPath + '/' + locales.join('-') + '/' + parameters.join('/');
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
fetch(translationsFullPath).then(response => {
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
133
|
+
}
|
|
134
|
+
return response.json();
|
|
135
|
+
}).then(data => {
|
|
136
|
+
this._loadedTranslations = data;
|
|
137
|
+
this._isInitialized = true;
|
|
138
|
+
this._locales = locales;
|
|
139
|
+
this._languages = locales.map((l) => l.substring(0, 2));
|
|
140
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
141
|
+
resolve(undefined);
|
|
142
|
+
}).catch(error => {
|
|
143
|
+
reject(new Error(`ERROR LOADING LOCALES FROM: ${translationsFullPath}\n` + error));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check if the class has been correctly initialized and translations have been correctly loaded
|
|
149
|
+
*/
|
|
150
|
+
isInitialized() {
|
|
151
|
+
return this._isInitialized;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Aux method to verify that this class is correctly initialized with translation data
|
|
155
|
+
*/
|
|
156
|
+
_validateInitialized() {
|
|
157
|
+
if (!this._isInitialized) {
|
|
158
|
+
throw new Error('LocalesManager not initialized');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Checks if the specified locale is currently loaded for the currently defined bundles and paths.
|
|
163
|
+
*
|
|
164
|
+
* @param locale A locale to check. For example 'en_US'
|
|
165
|
+
*
|
|
166
|
+
* @return True if the locale is currently loaded on the class, false if not.
|
|
167
|
+
*/
|
|
168
|
+
isLocaleLoaded(locale) {
|
|
169
|
+
this._validateLocaleString(locale);
|
|
170
|
+
return this._locales.includes(locale);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Aux method to validate that a locale string is correctly formatted
|
|
174
|
+
*
|
|
175
|
+
* @param string $locale A locale string
|
|
176
|
+
*/
|
|
177
|
+
_validateLocaleString(locale) {
|
|
178
|
+
if (!/^[a-z]{2}_[A-Z]{2}$/.test(locale)) {
|
|
179
|
+
throw new Error('locale must be a valid xx_XX value');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Checks if the specified 2 digit language is currently loaded for the currently defined bundles and paths.
|
|
184
|
+
*
|
|
185
|
+
* @param language A language to check. For example 'en'
|
|
186
|
+
*
|
|
187
|
+
* @return True if the language is currently loaded on the class, false if not.
|
|
188
|
+
*/
|
|
189
|
+
isLanguageLoaded(language) {
|
|
190
|
+
this._validateLanguageString(language);
|
|
191
|
+
return this._languages.includes(language);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Aux method to validate that a language string is correctly formatted
|
|
195
|
+
*
|
|
196
|
+
* @param language A 2 digit language string
|
|
197
|
+
*/
|
|
198
|
+
_validateLanguageString(language) {
|
|
199
|
+
if (!/^[a-z]{2}$/.test(language)) {
|
|
200
|
+
throw new Error('language must be a valid 2 digit value');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get the translation to the current primary locale for the given key, library and bundle
|
|
205
|
+
*
|
|
206
|
+
* @param string key The key we want to read from the specified resource bundle
|
|
207
|
+
* @param string bundlePath A string with the format 'library_name/bundle_name' that is used to locate the bundle were the key to translate is found
|
|
208
|
+
* @param array replaceWildcards A list of values that will replace wildcards that may be found on the translated text. Each wildcard
|
|
209
|
+
* will be replaced with the element whose index on toReplace matches it. Check the documentation for this.wildCardsFormat
|
|
210
|
+
* property to know more about how to setup wildcards.
|
|
211
|
+
*
|
|
212
|
+
* @see setWildCardsFormat()
|
|
213
|
+
*
|
|
214
|
+
* @return The translated text
|
|
215
|
+
*/
|
|
216
|
+
t(key, bundlePath, replaceWildcards = []) {
|
|
217
|
+
this._validateInitialized();
|
|
218
|
+
// Create a cache key to improve performance when requesting the same key translation several times
|
|
219
|
+
const cacheKey = `${this._cacheHashBaseString}${key}${bundlePath}${replaceWildcards.join('')}`;
|
|
220
|
+
if (!this._keyValuesCache[cacheKey]) {
|
|
221
|
+
this._forceNonEmptyString(key, '', 'key must be non empty string');
|
|
222
|
+
this._forceNonEmptyString(bundlePath, '', 'bundlePath must be non empty string');
|
|
223
|
+
const [library, bundle] = bundlePath.split('/');
|
|
224
|
+
this._forceNonEmptyString(library, '', 'no library specified on bundlePath');
|
|
225
|
+
this._forceNonEmptyString(bundle, '', 'no bundle specified on bundlePath');
|
|
226
|
+
const replacementsCount = replaceWildcards.length;
|
|
227
|
+
// Loop all the locales to find the first one with a value for the specified key
|
|
228
|
+
for (const locale of this._locales) {
|
|
229
|
+
if (this._loadedTranslations[library]?.[bundle]?.[locale]?.[key]) {
|
|
230
|
+
let result = this._loadedTranslations[library][bundle][locale][key];
|
|
231
|
+
// Replace all wildcards on the text with the specified replacements if any
|
|
232
|
+
for (let i = 0; i < replacementsCount; i++) {
|
|
233
|
+
result = this._replace(result, this._replace(this._wildCardsFormat, 'N', i.toString()), replaceWildcards[i]);
|
|
234
|
+
}
|
|
235
|
+
this._keyValuesCache[cacheKey] = result;
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Check if an exception needs to be thrown if the specified key is not found on this bundle
|
|
240
|
+
if (this._missingKeyFormat.includes('$exception')) {
|
|
241
|
+
throw new Error(`key <${key}> not found on ${bundlePath}`);
|
|
242
|
+
}
|
|
243
|
+
this._keyValuesCache[cacheKey] = this._replace(this._missingKeyFormat, '$key', key);
|
|
244
|
+
}
|
|
245
|
+
return this._keyValuesCache[cacheKey];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get the translation for the given key and bundle as a string with all words first character capitalized
|
|
249
|
+
* and all the rest of the word with lower case
|
|
250
|
+
*
|
|
251
|
+
* @see t()
|
|
252
|
+
*
|
|
253
|
+
* @returns The localized and case formatted text
|
|
254
|
+
*/
|
|
255
|
+
tStartCase(key, bundlePath, replaceWildcards = []) {
|
|
256
|
+
return this.t(key, bundlePath, replaceWildcards).split(' ')
|
|
257
|
+
.map((word) => word ? word[0].toUpperCase() + word.slice(1).toLowerCase() : '').join(' ');
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get the translation for the given key and bundle as an all upper case string
|
|
261
|
+
*
|
|
262
|
+
* @see t()
|
|
263
|
+
*
|
|
264
|
+
* @returns The localized and case formatted text
|
|
265
|
+
*/
|
|
266
|
+
tAllUpperCase(key, bundlePath, replaceWildcards = []) {
|
|
267
|
+
return this.t(key, bundlePath, replaceWildcards).toUpperCase();
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get the translation for the given key and bundle as an all lower case string
|
|
271
|
+
*
|
|
272
|
+
* @see t()
|
|
273
|
+
*
|
|
274
|
+
* @returns The localized and case formatted text
|
|
275
|
+
*/
|
|
276
|
+
tAllLowerCase(key, bundlePath, replaceWildcards = []) {
|
|
277
|
+
return this.t(key, bundlePath, replaceWildcards).toLowerCase();
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the translation for the given key and bundle as a string with the first character as Upper case
|
|
281
|
+
* and all the rest as lower case
|
|
282
|
+
*
|
|
283
|
+
* @see t()
|
|
284
|
+
*
|
|
285
|
+
* @returns The localized and case formatted text
|
|
286
|
+
*/
|
|
287
|
+
tFirstUpperRestLower(key, bundlePath, replaceWildcards = []) {
|
|
288
|
+
const string = this.t(key, bundlePath, replaceWildcards);
|
|
289
|
+
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* A list of strings containing the locales that are used by this class to translate the given keys, sorted by preference.
|
|
293
|
+
* Each string is formatted as a standard locale code with language and country joined by an underscore, like: en_US, fr_FR
|
|
294
|
+
*
|
|
295
|
+
* When a key and bundle are requested for translation, the class will check on the first language of this
|
|
296
|
+
* list for a translated text. If missing, the next one will be used, and so. This list is constructed after initialize
|
|
297
|
+
* methods is called.
|
|
298
|
+
*
|
|
299
|
+
* @example: After loading the following list of locales ['en_US', 'es_ES', 'fr_FR'] if we call LocalesManager.t('HELLO', 'lib1/greetings')
|
|
300
|
+
* the localization manager will try to locate the en_US value for the HELLO tag on the greetings bundle for the library lib1.
|
|
301
|
+
* If the tag is not found for the specified locale and bundle, the same search will be performed for the es_ES locale, and so, till a
|
|
302
|
+
* value is found or no more locales are defined.
|
|
303
|
+
*/
|
|
304
|
+
getLocales() {
|
|
305
|
+
return this._locales;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* A list of strings containing the languages that are used by this class to translate the given keys, sorted by preference.
|
|
309
|
+
* Each string is formatted as a 2 digit language code, like: en, fr
|
|
310
|
+
*
|
|
311
|
+
* This list is the same as the locales() one, but containing only the language part of each locale (the first two digits)
|
|
312
|
+
*
|
|
313
|
+
* @see getLocales()
|
|
314
|
+
*/
|
|
315
|
+
getLanguages() {
|
|
316
|
+
return this._languages;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Get the first locale from the list of loaded locales, which is the currently used to search for translated texts.
|
|
320
|
+
*
|
|
321
|
+
* @returns The locale that is defined as the primary one. For example: en_US, es_ES, ..
|
|
322
|
+
*/
|
|
323
|
+
getPrimaryLocale() {
|
|
324
|
+
this._validateInitialized();
|
|
325
|
+
return this._locales[0];
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get the first language from the list of loaded locales, which is the currently used to search for translated texts.
|
|
329
|
+
*
|
|
330
|
+
* @returns The 2 digit language code that is defined as the primary one. For example: en, es, ..
|
|
331
|
+
*/
|
|
332
|
+
getPrimaryLanguage() {
|
|
333
|
+
this._validateInitialized();
|
|
334
|
+
return this._languages[0];
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Define the locale that will be placed at the front of the currently loaded locales list (moving all the others one position to the right).
|
|
338
|
+
*
|
|
339
|
+
* This will be the first locale to use when trying to get a translation.
|
|
340
|
+
*
|
|
341
|
+
* @param locale A currently loaded locale that will be moved to the first position of the loaded locales list. If the specified locale
|
|
342
|
+
* is not currently loaded, an exception will happen.
|
|
343
|
+
*
|
|
344
|
+
* @returns void
|
|
345
|
+
*/
|
|
346
|
+
setPrimaryLocale(locale) {
|
|
347
|
+
this._validateInitialized();
|
|
348
|
+
if (!this.isLocaleLoaded(locale)) {
|
|
349
|
+
throw new Error(locale + ' not loaded');
|
|
350
|
+
}
|
|
351
|
+
let result = [locale];
|
|
352
|
+
for (let l of this._locales) {
|
|
353
|
+
if (l !== locale) {
|
|
354
|
+
result.push(l);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
this._locales = result;
|
|
358
|
+
this._languages = this._locales.map((l) => l.substring(0, 2));
|
|
359
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Moves the specified locales to the beginning of the locales list. This also alters the translation priority by setting the first
|
|
363
|
+
* provided locale as the most prioritary, the second as the next one and so.
|
|
364
|
+
*
|
|
365
|
+
* This method basically works exactly the same way as setPrimaryLocale but letting us add many locales at once.
|
|
366
|
+
*
|
|
367
|
+
* @see setPrimaryLocale()
|
|
368
|
+
*
|
|
369
|
+
* @param locales A list of locales to be moved to the beginning of the translation priority. First locales item will be the prefered
|
|
370
|
+
* locale for translation, second will be the next one in case some key is not translated for the first one and so. If any of the
|
|
371
|
+
* specified locales is not currently loaded, an exception will happen.
|
|
372
|
+
*
|
|
373
|
+
* @returns void
|
|
374
|
+
*/
|
|
375
|
+
setPrimaryLocales(locales) {
|
|
376
|
+
if (!Array.isArray(locales) ||
|
|
377
|
+
(new Set(locales).size !== locales.length) ||
|
|
378
|
+
locales.length === 0) {
|
|
379
|
+
throw new Error('locales must be non empty string array with no duplicate elements');
|
|
380
|
+
}
|
|
381
|
+
for (let i = locales.length - 1; i >= 0; i--) {
|
|
382
|
+
this.setPrimaryLocale(locales[i]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Define the 2 digit language that will be placed at the front of the currently loaded locales list (moving all the others one position to the right).
|
|
387
|
+
*
|
|
388
|
+
* This will be the first language to use when trying to get a translation.
|
|
389
|
+
*
|
|
390
|
+
* @param language A 2 digit language code that matches with any of the currently loaded locales, which will
|
|
391
|
+
* be moved to the first position of the loaded locales list. If the specified language does not match with
|
|
392
|
+
* a locale that is currently loaded, an exception will happen.
|
|
393
|
+
*
|
|
394
|
+
* @returns void
|
|
395
|
+
*/
|
|
396
|
+
setPrimaryLanguage(language) {
|
|
397
|
+
for (let locale of this._locales) {
|
|
398
|
+
if (locale.substring(0, 2) === language) {
|
|
399
|
+
this.setPrimaryLocale(locale);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
throw new Error(language + ' not loaded');
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Moves the locales that match the specified languages to the beginning of the locales list.
|
|
407
|
+
* Works the same as setPrimaryLocales() but with a list of the 2 digit language codes that match the respective locales.
|
|
408
|
+
*
|
|
409
|
+
* @see setPrimaryLocale()
|
|
410
|
+
* @see setPrimaryLanguage()
|
|
411
|
+
*
|
|
412
|
+
* @param languages A list of 2 digit language codes to be moved to the beginning of the translation priority. If any of the
|
|
413
|
+
* specified languages does not match with a locale that is currently loaded, an exception will happen.
|
|
414
|
+
*
|
|
415
|
+
* @returns void
|
|
416
|
+
*/
|
|
417
|
+
setPrimaryLanguages(languages) {
|
|
418
|
+
if (!Array.isArray(languages) ||
|
|
419
|
+
(new Set(languages).size !== languages.length) ||
|
|
420
|
+
languages.length === 0) {
|
|
421
|
+
throw new Error('languages must be non empty string array with no duplicate elements');
|
|
422
|
+
}
|
|
423
|
+
for (let i = languages.length - 1; i >= 0; i--) {
|
|
424
|
+
this.setPrimaryLanguage(languages[i]);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Change the loaded locales translation preference order. The same locales that are currently loaded must be passed
|
|
429
|
+
* but with a different order to change the translation priority.
|
|
430
|
+
*
|
|
431
|
+
* @param locales A list with the new locales translation priority
|
|
432
|
+
*
|
|
433
|
+
* @returns void
|
|
434
|
+
*/
|
|
435
|
+
setLocalesOrder(locales) {
|
|
436
|
+
if (locales.length !== this._locales.length) {
|
|
437
|
+
throw new Error('locales must contain all the currently loaded locales');
|
|
438
|
+
}
|
|
439
|
+
this._validateInitialized();
|
|
440
|
+
for (let locale of locales) {
|
|
441
|
+
if (!this.isLocaleLoaded(locale)) {
|
|
442
|
+
throw new Error(locale + ' not loaded');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
this._locales = locales;
|
|
446
|
+
this._languages = this._locales.map((l) => l.substring(0, 2));
|
|
447
|
+
this._cacheHashBaseString = this._wildCardsFormat + this._missingKeyFormat + this._locales[0];
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* This is an aux method to implement the TurboCommons StringUtils replace method.
|
|
451
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
452
|
+
*/
|
|
453
|
+
_replace(string, search, replacement) {
|
|
454
|
+
const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
455
|
+
return string.replace(new RegExp(escapedSearch, 'g'), replacement);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* This is an aux method to implement the TurboCommons StringUtils isEmpty method.
|
|
459
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
460
|
+
*/
|
|
461
|
+
_isEmpty(string) {
|
|
462
|
+
let isString = (typeof string === 'string' || string instanceof String);
|
|
463
|
+
// Throw exception if non string value was received
|
|
464
|
+
if (!isString) {
|
|
465
|
+
// Empty or null value is considered empty
|
|
466
|
+
if (string == null || string == '') {
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
throw new Error("value is not a string");
|
|
470
|
+
}
|
|
471
|
+
return string.replace(/[ \n\r\t]/g, '') === '';
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* This is an aux method to implement the TurboCommons StringUtils forceNonEmptyString method.
|
|
475
|
+
* It is exactly the same as the one on the library, but we implement it here to avoid having a dependency with TurboCommons
|
|
476
|
+
*/
|
|
477
|
+
_forceNonEmptyString(value, valueName = '', errorMessage = 'must be a non empty string') {
|
|
478
|
+
let isString = (typeof value === 'string' || value instanceof String);
|
|
479
|
+
if (!isString || this._isEmpty(value)) {
|
|
480
|
+
throw new Error(valueName + ' ' + errorMessage);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
484
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, providedIn: 'root' }); }
|
|
485
|
+
}
|
|
486
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.8", ngImport: i0, type: LocalesService, decorators: [{
|
|
487
|
+
type: Injectable,
|
|
488
|
+
args: [{
|
|
489
|
+
providedIn: 'root',
|
|
490
|
+
}]
|
|
491
|
+
}], ctorParameters: () => [] });
|
|
492
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWxlcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvdHVyYm9ndWktYW5ndWxhci9zcmMvbWFpbi9jb250cm9sbGVyL2xvY2FsZXMuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQzs7QUFHL0U7O0dBRUc7QUFJSCxNQUFNLE9BQU8sY0FBZSxTQUFRLHFCQUFxQjtJQXNEckQ7O09BRUc7SUFDSDtRQUVJLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQztRQXhEMUI7O1dBRUc7UUFDSyxtQkFBYyxHQUFHLEtBQUssQ0FBQztRQUcvQjs7V0FFRztRQUNLLGFBQVEsR0FBWSxFQUFFLENBQUM7UUFHL0I7O1dBRUc7UUFDSyxlQUFVLEdBQVksRUFBRSxDQUFDO1FBR2pDOztXQUVHO1FBQ0ssd0JBQW1CLEdBQU8sRUFBRSxDQUFDO1FBR3JDOztXQUVHO1FBQ0ssb0JBQWUsR0FBTyxFQUFFLENBQUM7UUFHakM7O1dBRUc7UUFDSyxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFHakM7O1dBRUc7UUFDSyxzQkFBaUIsR0FBRyxZQUFZLENBQUM7UUFHekM7Ozs7O1dBS0c7UUFDSyx5QkFBb0IsR0FBRyxFQUFFLENBQUM7SUFTbEMsQ0FBQztJQUdEOzs7Ozs7Ozs7Ozs7Ozs7OztPQWlCRztJQUNILGtCQUFrQixDQUFDLEtBQVk7UUFFM0IsSUFBRyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUM7WUFFcEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDO1NBQzFEO1FBRUQsSUFBSSxDQUFDLG9CQUFvQixHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsaUJBQWlCLEdBQUcsQ0FB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LG1EQUFtRDtRQUNuRCxJQUFHLENBQUMsUUFBUSxFQUFDO1lBRVQsMENBQTBDO1lBQzFDLElBQUcsTUFBTSxJQUFJLElBQUksSUFBSSxNQUFNLElBQUksRUFBRSxFQUFDO2dCQUU5QixPQUFPLElBQUksQ0FBQzthQUNmO1lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO1NBQzVDO1FBRUQsT0FBTyxNQUFNLENBQUMsT0FBTyxDQUFDLFlBQVksRUFBRSxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDbkQsQ0FBQztJQUdEOzs7T0FHRztJQUNLLG9CQUFvQixDQUFDLEtBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxFQUFFLFlBQVksR0FBRyw0QkFBNEI7UUFFL0YsSUFBSSxRQUFRLEdBQUcsQ0FBQyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxZQUFZLE1BQU0sQ0FBQyxDQUFDO1FBRXRFLElBQUcsQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBQztZQUVqQyxNQUFNLElBQUksS0FBSyxDQUFDLFNBQVMsR0FBRyxHQUFHLEdBQUcsWUFBWSxDQUFDLENBQUM7U0FDbkQ7SUFDTCxDQUFDOzhHQWxvQlEsY0FBYztrSEFBZCxjQUFjLGNBRmIsTUFBTTs7MkZBRVAsY0FBYztrQkFIMUIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogVHVyYm9HVUkgaXMgQSBsaWJyYXJ5IHRoYXQgaGVscHMgd2l0aCB0aGUgbW9zdCBjb21tb24gYW5kIGdlbmVyaWMgVUkgZWxlbWVudHMgYW5kIGZ1bmN0aW9uYWxpdGllc1xyXG4gKlxyXG4gKiBXZWJzaXRlIDogLT4gaHR0cDovL3d3dy50dXJib2d1aS5vcmdcclxuICogTGljZW5zZSA6IC0+IExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cclxuICogTGljZW5zZSBVcmwgOiAtPiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcclxuICogQ29weVJpZ2h0IDogLT4gQ29weXJpZ2h0IDIwMTggRWRlcnRvbmUgQWR2YW5kZWQgU29sdXRpb25zLiBodHRwczovL3d3dy5lZGVydG9uZS5jb21cclxuICovXHJcblxyXG5pbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IFNpbmdsZXRvbmVTdHJpY3RDbGFzcyB9IGZyb20gJy4uL21vZGVsL2NsYXNzZXMvU2luZ2xldG9uZVN0cmljdENsYXNzJztcclxuXHJcblxyXG4vKipcclxuICogTWFuYWdlcyBhcHBsaWNhdGlvbiB0ZXh0IHRyYW5zbGF0aW9ucyBhbmQgbGFuZ3VhZ2VzXHJcbiAqL1xyXG5ASW5qZWN0YWJsZSh7XHJcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgTG9jYWxlc1NlcnZpY2UgZXh0ZW5kcyBTaW5nbGV0b25lU3RyaWN0Q2xhc3Mge1xyXG5cclxuXHJcbiAgICAvKipcclxuICAgICAqIGlmIHRoZSBjbGFzcyBoYXMgYmVlbiBjb3JyZWN0bHkgaW5pdGlhbGl6ZWQgYW5kIHRyYW5zbGF0aW9ucyBoYXZlIGJlZW4gY29ycmVjdGx5IGxvYWRlZFxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIF9pc0luaXRpYWxpemVkID0gZmFsc2U7XHJcbiAgICAgICAgXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBAc2VlIGdldExvY2FsZXMoKVxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIF9sb2NhbGVzOnN0cmluZ1tdID0gW107XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBAc2VlIGdldExhbmd1YWdlcygpXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgX2xhbmd1YWdlczpzdHJpbmdbXSA9IFtdO1xyXG4gICAgICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIFN0b3JlcyBhbGwgdGhlIGxvYWRlZCBsb2NhbGl6YXRpb24gZGF0YSBieSBsaWJyYXJ5IG5hbWUsIGJ1bmRsZSBuYW1lLCBrZXkgYW5kIGxvY2FsZXNcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfbG9hZGVkVHJhbnNsYXRpb25zOmFueSA9IHt9O1xyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogU3RvcmVzIGEgbWVtb3J5IGNhY2hlIHRvIGltcHJvdmUgcGVyZm9ybWFuY2Ugd2hlbiBvdXRwdXRpbmcgdHJhbnNsYXRpb25zXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgX2tleVZhbHVlc0NhY2hlOmFueSA9IHt9O1xyXG5cclxuXHJcbiAgICAvKipcclxuICAgICAqIEBzZWUgc2V0V2lsZENhcmRzRm9ybWF0KClcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfd2lsZENhcmRzRm9ybWF0ID0gJ3tOfSc7XHJcblxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQHNlZSBzZXRNaXNzaW5nS2V5Rm9ybWF0KClcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfbWlzc2luZ0tleUZvcm1hdCA9ICckZXhjZXB0aW9uJztcclxuXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBTdG9yZXMgYSBoYXNoIHZhbHVlIHRoYXQgaXMgdXNlZCB0byBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZSBmb3IgdHJhbnNsYXRpb24gdCgpIG1ldGhvZHMuXHJcbiAgICAgKiBUaGlzIGlzIGNvbXB1dGVkIGJhc2VkIG9uIF93aWxkQ2FyZHNGb3JtYXQgcGx1cyBfbWlzc2luZ0tleUZvcm1hdCBwbHVzIHRoZSBjdXJyZW50IHByaW1hcnkgbG9jYWxlXHJcbiAgICAgKiBNZXRob2RzIHRoYXQgY2hhbmdlIHRoZXNlIHZhbHVlcyB3aWxsIHJlY2FsY3VsYXRlIHRoZSBoYXNoIHN0cmluZywgc28gd2hlbiBjYWxsaW5nIHRyYW5zbGF0aW9uIG1ldGhvZHMsIHRoZVxyXG4gICAgICogcGVyZm9ybWFuY2Ugd2lsbCBiZSBhcyBmYXN0IGFzIHBvc3NpYmxlLlxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIF9jYWNoZUhhc2hCYXNlU3RyaW5nID0gJyc7XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBGdWxseSBmZWF0dXJlZCB0cmFuc2xhdGlvbiBtYW5hZ2VyIHRvIGJlIHVzZWQgd2l0aCBhbnkgYXBwbGljYXRpb24gdGhhdCByZXF1aXJlcyB0ZXh0IGludGVybmF0aW9uYWxpemF0aW9uLlxyXG4gICAgICovXHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuXHJcbiAgICAgICAgc3VwZXIoTG9jYWxlc1NlcnZpY2UpO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogV2lsZGNhcmRzIGFyZSBzdHJpbmcgZnJhZ21lbnRzIHRoYXQgYXJlIHBsYWNlZCBpbnNpZGUgdGhlIHRyYW5zbGF0ZWQgdGV4dHMuIFRoZWlyIG1haW4gcHVycG9zZSBpcyB0byBiZSByZXBsYWNlZCBhdFxyXG4gICAgICogcnVudGltZSBieSBjdXN0b20gdmFsdWVzIGxpa2UgZm9yIGV4YW1wbGUgYSB1c2VyIG5hbWUsIGEgZGF0ZSwgYSBudW1lcmljIHZhbHVlLCBldGMuLlxyXG4gICAgICpcclxuICAgICAqIFRoaXMgY2xhc3MgaGVscHMgd2l0aCB0aGlzIHByb2Nlc3MgYnkgaW5jbHVkaW5nIGEgcGFyYW1ldGVyIGNhbGxlZCAndG9SZXBsYWNlJyBvbiBhbGwgLT50IG1ldGhvZHMgd2hpY2ggYWxsb3dzIHVzXHJcbiAgICAgKiB0byBzcGVjaWZ5IGEgc3RyaW5nIG9yIGxpc3Qgb2Ygc3RyaW5ncyB0aGF0IHdpbGwgcmVwbGFjZSB0aGUgcmVzcGVjdGl2ZSB3aWxkY2FyZHMgb24gdGhlIHRyYW5zbGF0ZWQgdGV4dC4gRWFjaCB3aWxkY2FyZFxyXG4gICAgICogbXVzdCBmb2xsb3cgdGhlIGZvcm1hdCBzcGVjaWZpZWQgaGVyZSwgYW5kIGNvbnRhaW4gYSBudW1lcmljIGRpZ2l0IHRoYXQgd2lsbCBiZSB1c2VkIHRvIGZpbmQgdGhlIHJlcGxhY2VtZW50IHRleHQgYXQgdGhlXHJcbiAgICAgKiAndG9SZXBsYWNlJyBsaXN0LiBGb3IgZXhhbXBsZSwgaWYgd2UgZGVmaW5lICROIGFzIHRoZSB3aWxkY2FyZCBmb3JtYXQsIGFuZCB3ZSBoYXZlIGEgdHJhbnNsYXRpb24gdGhhdCBjb250YWlucyAkMCwgJDEsICQyLFxyXG4gICAgICogJDAgd2lsbCBiZSByZXBsYWNlZCB3aXRoIHRoZSBmaXJzdCBlbGVtZW50IG9uIHRvUmVwbGFjZSwgJDEgd2l0aCB0aGUgc2Vjb25kIGFuZCBzby5cclxuICAgICAqXHJcbiAgICAgKiBXZSB1c3VhbGx5IHNldCB0aGlzIGJlZm9yZSBpbml0aWFsaXppbmcgdGhlIGNsYXNzIHRyYW5zbGF0aW9uIGRhdGFcclxuICAgICAqXHJcbiAgICAgKiBOb3RpY2UgdGhhdCBOIGlzIG1hbmRheW9yeSBvbiB0aGUgd2lsZGNhcmRzIGZvcm1hdCBhbmQgdGhlIGZpcnN0IGluZGV4IHZhbHVlIGlzIDAuXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHZhbHVlIFRoZSB3aWxkY2FyZHMgZm9ybWF0IHdlIHdhbnQgdG8gc2V0XHJcbiAgICAgKiBcclxuICAgICAqIEByZXR1cm5zIFRoZSB2YWx1ZSB0aGF0J3MgYmVlbiBzZXRcclxuICAgICAqL1xyXG4gICAgc2V0V2lsZENhcmRzRm9ybWF0KHZhbHVlOnN0cmluZykge1xyXG5cclxuICAgICAgICBpZighdmFsdWUuaW5jbHVkZXMoJ04nKSl7XHJcbiAgICAgICAgXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk4gaXMgbWFuZGF0b3J5IHRvIHJlcGxhY2Ugd2lsZGNhcmRzXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fY2FjaGVIYXNoQmFzZVN0cmluZyA9IHZhbHVlICsgdGhpcy5fbWlzc2luZ0tleUZvcm1hdCArICgodGhpcy5fbG9jYWxlcy5sZW5ndGggPiAwKSA/IHRoaXMuX2xvY2FsZXNbMF0gOiAnJyk7XHJcblxyXG4gICAgICAgIHRoaXMuX3dpbGRDYXJkc0Zvcm1hdCA9IHZhbHVlO1xyXG4gICAgICAgIFxyXG4gICAgICAgIHJldHVybiB2YWx1ZTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIERlZmluZXMgdGhlIGJlaGF2aW91ciBmb3IgZ2V0KCksIGdldFN0YXJ0Q2FzZSgpLCBldGMuLi4gbWV0aG9kcyB3aGVuIGEga2V5IGlzIG5vdCBmb3VuZCBvblxyXG4gICAgICogYSBidW5kbGUgb3IgdGhlIGJ1bmRsZSBkb2VzIG5vdCBleGlzdFxyXG4gICAgICpcclxuICAgICAqIElmIG1pc3NpbmdLZXlGb3JtYXQgaXMgYW4gZW1wdHkgc3RyaW5nLCBhbGwgbWlzc2luZyBrZXlzIHdpbGwgcmV0dXJuIGFuIGVtcHR5IHZhbHVlIChub3QgcmVjb21tZW5kZWQpXHJcbiAgICAgKlxyXG4gICAgICogSWYgbWlzc2luZ0tleUZvcm1hdCBjb250YWlucyBhIHN0cmluZywgdGhhdCBzdHJpbmcgd2lsbCBiZSBhbHdheXMgcmV0dXJuZWQgZm9yIG1pc3Npbmcga2V5c1xyXG4gICAgICpcclxuICAgICAqIElmIG1pc3NpbmdLZXlGb3JtYXQgY29udGFpbnMgYSBzdHJpbmcgd2l0aCBvbmUgb2YgdGhlIGZvbGxvd2luZyBwcmVkZWZpbmVkIHdpbGRjYXJkczo8YnI+XHJcbiAgICAgKiAgICAtICRrZXkgd2lsbCBiZSByZXBsYWNlZCB3aXRoIGtleSBuYW1lLiBFeGFtcGxlOiBnZXQoXCJOQU1FXCIpIHdpbGwgb3V0cHV0IFtOQU1FXSBpZiBrZXkgaXMgbm90IGZvdW5kIGFuZCBtaXNzaW5nS2V5Rm9ybWF0ID0gJ1ska2V5XSc8YnI+XHJcbiAgICAgKiAgICAtICRleGNlcHRpb24gKGRlZmF1bHQgdmFsdWUpIHdpbGwgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggdGhlIHByb2JsZW0gY2F1c2UgZGVzY3JpcHRpb24uXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHZhbHVlIFRoZSBtaXNzaW5nIGtleSBmb3JtYXQgd2Ugd2FudCB0byBzZXRcclxuICAgICAqIFxyXG4gICAgICogQHJldHVybnMgVGhlIHZhbHVlIHRoYXQncyBiZWVuIHNldFxyXG4gICAgICovXHJcbiAgICBzZXRNaXNzaW5nS2V5Rm9ybWF0KHZhbHVlOnN0cmluZykge1xyXG5cclxuICAgICAgICB0aGlzLl9jYWNoZUhhc2hCYXNlU3RyaW5nID0gdGhpcy5fd2lsZENhcmRzRm9ybWF0ICsgdmFsdWUgKyAoKHRoaXMuX2xvY2FsZXMubGVuZ3RoID4gMCkgPyB0aGlzLl9sb2NhbGVzWzBdIDogJycpO1xyXG5cclxuICAgICAgICB0aGlzLl9taXNzaW5nS2V5Rm9ybWF0ID0gdmFsdWU7XHJcbiAgICAgICAgXHJcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogQHNlZSBzZXRNaXNzaW5nS2V5Rm9ybWF0KClcclxuICAgICAqL1xyXG4gICAgZ2V0TWlzc2luZ0tleUZvcm1hdCgpIHtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21pc3NpbmdLZXlGb3JtYXQ7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBJbml0aWFsaXplcyB0aGUgdHJhbnNsYXRpb24gc3lzdGVtIGJ5IGxvYWRpbmcgYW5kIHBhcnNpbmcgYnVuZGxlIGZpbGVzIGZyb20gdGhlIHNwZWNpZmllZCB0cmFuc2xhdGlvbnMgcGF0aC5cclxuICAgICAqIFxyXG4gICAgICogQHBhcmFtIHRyYW5zbGF0aW9uc1BhdGggLSBVcmwgd2hlcmUgdGhlIHRyYW5zbGF0aW9ucyBKc29uIHN0cnVjdHVyZSBvZiBsaWJyYXJpZXMvYnVuZGxlcy9sb2NhbGVzL2tleXMgaXMgYXZhaWxhYmxlLlxyXG4gICAgICogQHBhcmFtIGxvY2FsZXMgQW4gYXJyYXkgb2YgbG9jYWxlIGNvZGVzIChlLmcuLCBbJ2VuX1VTJywgJ2VzX0VTJywgJ2ZyX0ZSJ10pIHRvIGxvYWQuIFRoZXNlIHdpbGwgYmUgYWRkZWQgdG8gdGhlIHRyYW5zbGF0aW9uXHJcbiAgICAgKiAgICAgICAgcGF0aCB1c2luZyB0aGUgZm9sbG93aW5nIGZvcm1hdDogdHJhbnNsYXRpb25zUGF0aC9lbl9VUy1lc19FUy1mcl9GUi4gVGhlIG9yZGVyIG9mIHRoaXMgYXJyYXkgd2lsbCBkZXRlcm1pbmUgdGhlXHJcbiAgICAgKiAgICAgICAgdHJhbnNsYXRpb24gcHJpb3JpdHlcclxuICAgICAqIEBwYXJhbSBwYXJhbWV0ZXJzIEFueSBleHRyYSBwYXJhbWV0ZXJzIHRvIGJlIGF0dGFjaGVkIHRvIHRoZSB0cmFuc2xhdGlvbnNQYXRoIGFmdGVyIHRoZSBsb2NhbGVzIGxpa2UgL3BhcmFtMS9wYXJhbTIvIGV0Y1xyXG4gICAgICogXHJcbiAgICAgKiBAcmV0dXJuIEEgcHJvbWlzZSB0aGF0IHdpbGwgcmVzb2x2ZSBpZiB0aGUgdHJhbnNsYXRpb25zIGdldCBjb3JyZWN0bHkgbG9hZGVkLCBvciByZWplY3Qgd2l0aCBhbiBlcnJvciBpZiBsb2FkIGZhaWxzIFxyXG4gICAgICovXHJcbiAgICBpbml0aWFsaXplKHRyYW5zbGF0aW9uc1BhdGg6c3RyaW5nLCBsb2NhbGVzOnN0cmluZ1tdLCBwYXJhbWV0ZXJzOnN0cmluZ1tdKXtcclxuICAgIFxyXG4gICAgICAgIHRoaXMuX2lzSW5pdGlhbGl6ZWQgPSBmYWxzZTtcclxuICAgICAgICB0aGlzLl9sb2FkZWRUcmFuc2xhdGlvbnMgPSB7fTtcclxuICAgICAgICBcclxuICAgICAgICAvLyBWYWxpZGF0ZSByZWNlaXZlZCBsb2NhbGVzIGFyZSBjb3JyZWN0XHJcbiAgICAgICAgZm9yKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl92YWxpZGF0ZUxvY2FsZVN0cmluZyhsb2NhbGUpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBcclxuICAgICAgICBsZXQgdHJhbnNsYXRpb25zRnVsbFBhdGggPSB0cmFuc2xhdGlvbnNQYXRoICsgJy8nICsgbG9jYWxlcy5qb2luKCctJykgKyAnLycgKyBwYXJhbWV0ZXJzLmpvaW4oJy8nKTtcclxuICAgICAgICBcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgIFxyXG4gICAgICAgICAgICBmZXRjaCh0cmFuc2xhdGlvbnNGdWxsUGF0aCkudGhlbihyZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcclxuICAgICAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBIVFRQIGVycm9yISBzdGF0dXM6ICR7cmVzcG9uc2Uuc3RhdHVzfWApO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2UuanNvbigpO1xyXG4gICAgICAgICAgXHJcbiAgICAgICAgICAgIH0pLnRoZW4oZGF0YSA9PiB7XHJcbiAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIHRoaXMuX2xvYWRlZFRyYW5zbGF0aW9ucyA9IGRhdGE7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9pc0luaXRpYWxpemVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2xvY2FsZXMgPSBsb2NhbGVzO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fbGFuZ3VhZ2VzID0gbG9jYWxlcy5tYXAoKGw6IHN0cmluZykgPT4gbC5zdWJzdHJpbmcoMCwgMikpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fY2FjaGVIYXNoQmFzZVN0cmluZyA9IHRoaXMuX3dpbGRDYXJkc0Zvcm1hdCArIHRoaXMuX21pc3NpbmdLZXlGb3JtYXQgKyB0aGlzLl9sb2NhbGVzWzBdO1xyXG4gICAgICAgICAgICAgICAgXHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICBcclxuICAgICAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEVSUk9SIExPQURJTkcgTE9DQUxFUyBGUk9NOiAke3RyYW5zbGF0aW9uc0Z1bGxQYXRofVxcbmAgKyBlcnJvcikpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIENoZWNrIGlmIHRoZSBjbGFzcyBoYXMgYmVlbiBjb3JyZWN0bHkgaW5pdGlhbGl6ZWQgYW5kIHRyYW5zbGF0aW9ucyBoYXZlIGJlZW4gY29ycmVjdGx5IGxvYWRlZFxyXG4gICAgICovXHJcbiAgICBpc0luaXRpYWxpemVkKCl7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9pc0luaXRpYWxpemVkO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICAvKipcclxuICAgICAqIEF1eCBtZXRob2QgdG8gdmVyaWZ5IHRoYXQgdGhpcyBjbGFzcyBpcyBjb3JyZWN0bHkgaW5pdGlhbGl6ZWQgd2l0aCB0cmFuc2xhdGlvbiBkYXRhXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgX3ZhbGlkYXRlSW5pdGlhbGl6ZWQoKXtcclxuXHJcbiAgICAgICAgaWYoIXRoaXMuX2lzSW5pdGlhbGl6ZWQpe1xyXG5cclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdMb2NhbGVzTWFuYWdlciBub3QgaW5pdGlhbGl6ZWQnKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBDaGVja3MgaWYgdGhlIHNwZWNpZmllZCBsb2NhbGUgaXMgY3VycmVudGx5IGxvYWRlZCBmb3IgdGhlIGN1cnJlbnRseSBkZWZpbmVkIGJ1bmRsZXMgYW5kIHBhdGhzLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBsb2NhbGUgQSBsb2NhbGUgdG8gY2hlY2suIEZvciBleGFtcGxlICdlbl9VUydcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJuIFRydWUgaWYgdGhlIGxvY2FsZSBpcyBjdXJyZW50bHkgbG9hZGVkIG9uIHRoZSBjbGFzcywgZmFsc2UgaWYgbm90LlxyXG4gICAgICovXHJcbiAgICBpc0xvY2FsZUxvYWRlZChsb2NhbGU6c3RyaW5nKXtcclxuXHJcbiAgICAgICAgdGhpcy5fdmFsaWRhdGVMb2NhbGVTdHJpbmcobG9jYWxlKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvY2FsZXMuaW5jbHVkZXMobG9jYWxlKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEF1eCBtZXRob2QgdG8gdmFsaWRhdGUgdGhhdCBhIGxvY2FsZSBzdHJpbmcgaXMgY29ycmVjdGx5IGZvcm1hdHRlZFxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBzdHJpbmcgJGxvY2FsZSBBIGxvY2FsZSBzdHJpbmdcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfdmFsaWRhdGVMb2NhbGVTdHJpbmcobG9jYWxlOnN0cmluZyl7XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYoIS9eW2Etel17Mn1fW0EtWl17Mn0kLy50ZXN0KGxvY2FsZSkpIHtcclxuXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbG9jYWxlIG11c3QgYmUgYSB2YWxpZCB4eF9YWCB2YWx1ZScpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIENoZWNrcyBpZiB0aGUgc3BlY2lmaWVkIDIgZGlnaXQgbGFuZ3VhZ2UgaXMgY3VycmVudGx5IGxvYWRlZCBmb3IgdGhlIGN1cnJlbnRseSBkZWZpbmVkIGJ1bmRsZXMgYW5kIHBhdGhzLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBsYW5ndWFnZSBBIGxhbmd1YWdlIHRvIGNoZWNrLiBGb3IgZXhhbXBsZSAnZW4nXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybiBUcnVlIGlmIHRoZSBsYW5ndWFnZSBpcyBjdXJyZW50bHkgbG9hZGVkIG9uIHRoZSBjbGFzcywgZmFsc2UgaWYgbm90LlxyXG4gICAgICovXHJcbiAgICBpc0xhbmd1YWdlTG9hZGVkKGxhbmd1YWdlOnN0cmluZyl7XHJcblxyXG4gICAgICAgIHRoaXMuX3ZhbGlkYXRlTGFuZ3VhZ2VTdHJpbmcobGFuZ3VhZ2UpO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmd1YWdlKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEF1eCBtZXRob2QgdG8gdmFsaWRhdGUgdGhhdCBhIGxhbmd1YWdlIHN0cmluZyBpcyBjb3JyZWN0bHkgZm9ybWF0dGVkXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIGxhbmd1YWdlIEEgMiBkaWdpdCBsYW5ndWFnZSBzdHJpbmdcclxuICAgICAqL1xyXG4gICAgX3ZhbGlkYXRlTGFuZ3VhZ2VTdHJpbmcobGFuZ3VhZ2U6c3RyaW5nKXtcclxuXHJcbiAgICAgICAgaWYgKCEvXlthLXpdezJ9JC8udGVzdChsYW5ndWFnZSkpIHtcclxuXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbGFuZ3VhZ2UgbXVzdCBiZSBhIHZhbGlkIDIgZGlnaXQgdmFsdWUnKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBHZXQgdGhlIHRyYW5zbGF0aW9uIHRvIHRoZSBjdXJyZW50IHByaW1hcnkgbG9jYWxlIGZvciB0aGUgZ2l2ZW4ga2V5LCBsaWJyYXJ5IGFuZCBidW5kbGVcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gc3RyaW5nIGtleSBUaGUga2V5IHdlIHdhbnQgdG8gcmVhZCBmcm9tIHRoZSBzcGVjaWZpZWQgcmVzb3VyY2UgYnVuZGxlXHJcbiAgICAgKiBAcGFyYW0gc3RyaW5nIGJ1bmRsZVBhdGggQSBzdHJpbmcgd2l0aCB0aGUgZm9ybWF0ICdsaWJyYXJ5X25hbWUvYnVuZGxlX25hbWUnIHRoYXQgaXMgdXNlZCB0byBsb2NhdGUgdGhlIGJ1bmRsZSB3ZXJlIHRoZSBrZXkgdG8gdHJhbnNsYXRlIGlzIGZvdW5kXHJcbiAgICAgKiBAcGFyYW0gYXJyYXkgcmVwbGFjZVdpbGRjYXJkcyBBIGxpc3Qgb2YgdmFsdWVzIHRoYXQgd2lsbCByZXBsYWNlIHdpbGRjYXJkcyB0aGF0IG1heSBiZSBmb3VuZCBvbiB0aGUgdHJhbnNsYXRlZCB0ZXh0LiBFYWNoIHdpbGRjYXJkXHJcbiAgICAgKiAgICAgICAgd2lsbCBiZSByZXBsYWNlZCB3aXRoIHRoZSBlbGVtZW50IHdob3NlIGluZGV4IG9uIHRvUmVwbGFjZSBtYXRjaGVzIGl0LiBDaGVjayB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhpcy53aWxkQ2FyZHNGb3JtYXRcclxuICAgICAqICAgICAgICBwcm9wZXJ0eSB0byBrbm93IG1vcmUgYWJvdXQgaG93IHRvIHNldHVwIHdpbGRjYXJkcy5cclxuICAgICAqXHJcbiAgICAgKiBAc2VlIHNldFdpbGRDYXJkc0Zvcm1hdCgpXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybiBUaGUgdHJhbnNsYXRlZCB0ZXh0XHJcbiAgICAgKi9cclxuICAgIHQoa2V5OnN0cmluZywgYnVuZGxlUGF0aDpzdHJpbmcsIHJlcGxhY2VXaWxkY2FyZHM6c3RyaW5nW10gPSBbXSkge1xyXG5cclxuICAgICAgICB0aGlzLl92YWxpZGF0ZUluaXRpYWxpemVkKCk7XHJcblxyXG4gICAgICAgIC8vIENyZWF0ZSBhIGNhY2hlIGtleSB0byBpbXByb3ZlIHBlcmZvcm1hbmNlIHdoZW4gcmVxdWVzdGluZyB0aGUgc2FtZSBrZXkgdHJhbnNsYXRpb24gc2V2ZXJhbCB0aW1lc1xyXG4gICAgICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7dGhpcy5fY2FjaGVIYXNoQmFzZVN0cmluZ30ke2tleX0ke2J1bmRsZVBhdGh9JHtyZXBsYWNlV2lsZGNhcmRzLmpvaW4oJycpfWA7XHJcblxyXG4gICAgICAgIGlmICghdGhpcy5fa2V5VmFsdWVzQ2FjaGVbY2FjaGVLZXldKSB7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl9mb3JjZU5vbkVtcHR5U3RyaW5nKGtleSwgJycsICdrZXkgbXVzdCBiZSBub24gZW1wdHkgc3RyaW5nJyk7XHJcbiAgICAgICAgICAgIHRoaXMuX2ZvcmNlTm9uRW1wdHlTdHJpbmcoYnVuZGxlUGF0aCwgJycsICdidW5kbGVQYXRoIG11c3QgYmUgbm9uIGVtcHR5IHN0cmluZycpO1xyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgY29uc3QgW2xpYnJhcnksIGJ1bmRsZV0gPSBidW5kbGVQYXRoLnNwbGl0KCcvJyk7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl9mb3JjZU5vbkVtcHR5U3RyaW5nKGxpYnJhcnksICcnLCAnbm8gbGlicmFyeSBzcGVjaWZpZWQgb24gYnVuZGxlUGF0aCcpO1xyXG4gICAgICAgICAgICB0aGlzLl9mb3JjZU5vbkVtcHR5U3RyaW5nKGJ1bmRsZSwgJycsICdubyBidW5kbGUgc3BlY2lmaWVkIG9uIGJ1bmRsZVBhdGgnKTtcclxuICAgICAgICAgICAgXHJcbiAgICAgICAgICAgIGNvbnN0IHJlcGxhY2VtZW50c0NvdW50ID0gcmVwbGFjZVdpbGRjYXJkcy5sZW5ndGg7XHJcblxyXG4gICAgICAgICAgICAvLyBMb29wIGFsbCB0aGUgbG9jYWxlcyB0byBmaW5kIHRoZSBmaXJzdCBvbmUgd2l0aCBhIHZhbHVlIGZvciB0aGUgc3BlY2lmaWVkIGtleVxyXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiB0aGlzLl9sb2NhbGVzKSB7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuX2xvYWRlZFRyYW5zbGF0aW9uc1tsaWJyYXJ5XT8uW2J1bmRsZV0/Lltsb2NhbGVdPy5ba2V5XSkge1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0gdGhpcy5fbG9hZGVkVHJhbnNsYXRpb25zW2xpYnJhcnldW2J1bmRsZV1bbG9jYWxlXVtrZXldO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAvLyBSZXBsYWNlIGFsbCB3aWxkY2FyZHMgb24gdGhlIHRleHQgd2l0aCB0aGUgc3BlY2lmaWVkIHJlcGxhY2VtZW50cyBpZiBhbnlcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJlcGxhY2VtZW50c0NvdW50OyBpKyspIHtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IHRoaXMuX3JlcGxhY2UocmVzdWx0LCB0aGlzLl9yZXBsYWNlKHRoaXMuX3dpbGRDYXJkc0Zvcm1hdCwgJ04nLCBpLnRvU3RyaW5nKCkpLCByZXBsYWNlV2lsZGNhcmRzW2ldKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2tleVZhbHVlc0NhY2hlW2NhY2hlS2V5XSA9IHJlc3VsdDtcclxuICAgICAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBDaGVjayBpZiBhbiBleGNlcHRpb24gbmVlZHMgdG8gYmUgdGhyb3duIGlmIHRoZSBzcGVjaWZpZWQga2V5IGlzIG5vdCBmb3VuZCBvbiB0aGlzIGJ1bmRsZVxyXG4gICAgICAgICAgICBpZiAodGhpcy5fbWlzc2luZ0tleUZvcm1hdC5pbmNsdWRlcygnJGV4Y2VwdGlvbicpKSB7XHJcbiAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihga2V5IDwke2tleX0+IG5vdCBmb3VuZCBvbiAke2J1bmRsZVBhdGh9YCk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHRoaXMuX2tleVZhbHVlc0NhY2hlW2NhY2hlS2V5XSA9IHRoaXMuX3JlcGxhY2UodGhpcy5fbWlzc2luZ0tleUZvcm1hdCwgJyRrZXknLCBrZXkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2tleVZhbHVlc0NhY2hlW2NhY2hlS2V5XTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEdldCB0aGUgdHJhbnNsYXRpb24gZm9yIHRoZSBnaXZlbiBrZXkgYW5kIGJ1bmRsZSBhcyBhIHN0cmluZyB3aXRoIGFsbCB3b3JkcyBmaXJzdCBjaGFyYWN0ZXIgY2FwaXRhbGl6ZWRcclxuICAgICAqIGFuZCBhbGwgdGhlIHJlc3Qgb2YgdGhlIHdvcmQgd2l0aCBsb3dlciBjYXNlXHJcbiAgICAgKlxyXG4gICAgICogQHNlZSB0KClcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBUaGUgbG9jYWxpemVkIGFuZCBjYXNlIGZvcm1hdHRlZCB0ZXh0XHJcbiAgICAgKi9cclxuICAgIHRTdGFydENhc2Uoa2V5OnN0cmluZywgYnVuZGxlUGF0aDpzdHJpbmcsIHJlcGxhY2VXaWxkY2FyZHM6c3RyaW5nW10gPSBbXSkge1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy50KGtleSwgYnVuZGxlUGF0aCwgcmVwbGFjZVdpbGRjYXJkcykuc3BsaXQoJyAnKVxyXG4gICAgICAgICAgICAgICAgLm1hcCgod29yZDphbnkpID0+IHdvcmQgPyB3b3JkWzBdLnRvVXBwZXJDYXNlKCkgKyB3b3JkLnNsaWNlKDEpLnRvTG93ZXJDYXNlKCkgOiAnJykuam9pbignICcpO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogR2V0IHRoZSB0cmFuc2xhdGlvbiBmb3IgdGhlIGdpdmVuIGtleSBhbmQgYnVuZGxlIGFzIGFuIGFsbCB1cHBlciBjYXNlIHN0cmluZ1xyXG4gICAgICpcclxuICAgICAqIEBzZWUgdCgpXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgVGhlIGxvY2FsaXplZCBhbmQgY2FzZSBmb3JtYXR0ZWQgdGV4dFxyXG4gICAgICovXHJcbiAgICB0QWxsVXBwZXJDYXNlKGtleTpzdHJpbmcsIGJ1bmRsZVBhdGg6c3RyaW5nLCByZXBsYWNlV2lsZGNhcmRzOnN0cmluZ1tdID0gW10pIHtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMudChrZXksIGJ1bmRsZVBhdGgsIHJlcGxhY2VXaWxkY2FyZHMpLnRvVXBwZXJDYXNlKCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBHZXQgdGhlIHRyYW5zbGF0aW9uIGZvciB0aGUgZ2l2ZW4ga2V5IGFuZCBidW5kbGUgYXMgYW4gYWxsIGxvd2VyIGNhc2Ugc3RyaW5nXHJcbiAgICAgKlxyXG4gICAgICogQHNlZSB0KClcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBUaGUgbG9jYWxpemVkIGFuZCBjYXNlIGZvcm1hdHRlZCB0ZXh0XHJcbiAgICAgKi9cclxuICAgIHRBbGxMb3dlckNhc2Uoa2V5OnN0cmluZywgYnVuZGxlUGF0aDpzdHJpbmcsIHJlcGxhY2VXaWxkY2FyZHM6c3RyaW5nW10gPSBbXSkge1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy50KGtleSwgYnVuZGxlUGF0aCwgcmVwbGFjZVdpbGRjYXJkcykudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEdldCB0aGUgdHJhbnNsYXRpb24gZm9yIHRoZSBnaXZlbiBrZXkgYW5kIGJ1bmRsZSBhcyBhIHN0cmluZyB3aXRoIHRoZSBmaXJzdCBjaGFyYWN0ZXIgYXMgVXBwZXIgY2FzZVxyXG4gICAgICogYW5kIGFsbCB0aGUgcmVzdCBhcyBsb3dlciBjYXNlXHJcbiAgICAgKlxyXG4gICAgICogQHNlZSB0KClcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyBUaGUgbG9jYWxpemVkIGFuZCBjYXNlIGZvcm1hdHRlZCB0ZXh0XHJcbiAgICAgKi9cclxuICAgIHRGaXJzdFVwcGVyUmVzdExvd2VyKGtleTpzdHJpbmcsIGJ1bmRsZVBhdGg6c3RyaW5nLCByZXBsYWNlV2lsZGNhcmRzOnN0cmluZ1tdID0gW10pe1xyXG5cclxuICAgICAgICBjb25zdCBzdHJpbmcgPSB0aGlzLnQoa2V5LCBidW5kbGVQYXRoLCByZXBsYWNlV2lsZGNhcmRzKTtcclxuICAgICAgICBcclxuICAgICAgICByZXR1cm4gc3RyaW5nLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpICsgc3RyaW5nLnNsaWNlKDEpLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBBIGxpc3Qgb2Ygc3RyaW5ncyBjb250YWluaW5nIHRoZSBsb2NhbGVzIHRoYXQgYXJlIHVzZWQgYnkgdGhpcyBjbGFzcyB0byB0cmFuc2xhdGUgdGhlIGdpdmVuIGtleXMsIHNvcnRlZCBieSBwcmVmZXJlbmNlLlxyXG4gICAgICogRWFjaCBzdHJpbmcgaXMgZm9ybWF0dGVkIGFzIGEgc3RhbmRhcmQgbG9jYWxlIGNvZGUgd2l0aCBsYW5ndWFnZSBhbmQgY291bnRyeSBqb2luZWQgYnkgYW4gdW5kZXJzY29yZSwgbGlrZTogZW5fVVMsIGZyX0ZSXHJcbiAgICAgKlxyXG4gICAgICogV2hlbiBhIGtleSBhbmQgYnVuZGxlIGFyZSByZXF1ZXN0ZWQgZm9yIHRyYW5zbGF0aW9uLCB0aGUgY2xhc3Mgd2lsbCBjaGVjayBvbiB0aGUgZmlyc3QgbGFuZ3VhZ2Ugb2YgdGhpc1xyXG4gICAgICogbGlzdCBmb3IgYSB0cmFuc2xhdGVkIHRleHQuIElmIG1pc3NpbmcsIHRoZSBuZXh0IG9uZSB3aWxsIGJlIHVzZWQsIGFuZCBzby4gVGhpcyBsaXN0IGlzIGNvbnN0cnVjdGVkIGFmdGVyIGluaXRpYWxpemVcclxuICAgICAqIG1ldGhvZHMgaXMgY2FsbGVkLlxyXG4gICAgICpcclxuICAgICAqIEBleGFtcGxlOiBBZnRlciBsb2FkaW5nIHRoZSBmb2xsb3dpbmcgbGlzdCBvZiBsb2NhbGVzIFsnZW5fVVMnLCAnZXNfRVMnLCAnZnJfRlInXSBpZiB3ZSBjYWxsIExvY2FsZXNNYW5hZ2VyLnQoJ0hFTExPJywgJ2xpYjEvZ3JlZXRpbmdzJylcclxuICAgICAqIHRoZSBsb2NhbGl6YXRpb24gbWFuYWdlciB3aWxsIHRyeSB0byBsb2NhdGUgdGhlIGVuX1VTIHZhbHVlIGZvciB0aGUgSEVMTE8gdGFnIG9uIHRoZSBncmVldGluZ3MgYnVuZGxlIGZvciB0aGUgbGlicmFyeSBsaWIxLlxyXG4gICAgICogSWYgdGhlIHRhZyBpcyBub3QgZm91bmQgZm9yIHRoZSBzcGVjaWZpZWQgbG9jYWxlIGFuZCBidW5kbGUsIHRoZSBzYW1lIHNlYXJjaCB3aWxsIGJlIHBlcmZvcm1lZCBmb3IgdGhlIGVzX0VTIGxvY2FsZSwgYW5kIHNvLCB0aWxsIGFcclxuICAgICAqIHZhbHVlIGlzIGZvdW5kIG9yIG5vIG1vcmUgbG9jYWxlcyBhcmUgZGVmaW5lZC5cclxuICAgICAqL1xyXG4gICAgZ2V0TG9jYWxlcygpe1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbG9jYWxlcztcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEEgbGlzdCBvZiBzdHJpbmdzIGNvbnRhaW5pbmcgdGhlIGxhbmd1YWdlcyB0aGF0IGFyZSB1c2VkIGJ5IHRoaXMgY2xhc3MgdG8gdHJhbnNsYXRlIHRoZSBnaXZlbiBrZXlzLCBzb3J0ZWQgYnkgcHJlZmVyZW5jZS5cclxuICAgICAqIEVhY2ggc3RyaW5nIGlzIGZvcm1hdHRlZCBhcyBhIDIgZGlnaXQgbGFuZ3VhZ2UgY29kZSwgbGlrZTogZW4sIGZyXHJcbiAgICAgKlxyXG4gICAgICogVGhpcyBsaXN0IGlzIHRoZSBzYW1lIGFzIHRoZSBsb2NhbGVzKCkgb25lLCBidXQgY29udGFpbmluZyBvbmx5IHRoZSBsYW5ndWFnZSBwYXJ0IG9mIGVhY2ggbG9jYWxlICh0aGUgZmlyc3QgdHdvIGRpZ2l0cylcclxuICAgICAqXHJcbiAgICAgKiBAc2VlIGdldExvY2FsZXMoKVxyXG4gICAgICovXHJcbiAgICBnZXRMYW5ndWFnZXMoKXtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xhbmd1YWdlcztcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIEdldCB0aGUgZmlyc3QgbG9jYWxlIGZyb20gdGhlIGxpc3Qgb2YgbG9hZGVkIGxvY2FsZXMsIHdoaWNoIGlzIHRoZSBjdXJyZW50bHkgdXNlZCB0byBzZWFyY2ggZm9yIHRyYW5zbGF0ZWQgdGV4dHMuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgVGhlIGxvY2FsZSB0aGF0IGlzIGRlZmluZWQgYXMgdGhlIHByaW1hcnkgb25lLiBGb3IgZXhhbXBsZTogZW5fVVMsIGVzX0VTLCAuLlxyXG4gICAgICovXHJcbiAgICBnZXRQcmltYXJ5TG9jYWxlKCl7XHJcblxyXG4gICAgICAgIHRoaXMuX3ZhbGlkYXRlSW5pdGlhbGl6ZWQoKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvY2FsZXNbMF07XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBHZXQgdGhlIGZpcnN0IGxhbmd1YWdlIGZyb20gdGhlIGxpc3Qgb2YgbG9hZGVkIGxvY2FsZXMsIHdoaWNoIGlzIHRoZSBjdXJyZW50bHkgdXNlZCB0byBzZWFyY2ggZm9yIHRyYW5zbGF0ZWQgdGV4dHMuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgVGhlIDIgZGlnaXQgbGFuZ3VhZ2UgY29kZSB0aGF0IGlzIGRlZmluZWQgYXMgdGhlIHByaW1hcnkgb25lLiBGb3IgZXhhbXBsZTogZW4sIGVzLCAuLlxyXG4gICAgICovXHJcbiAgICBnZXRQcmltYXJ5TGFuZ3VhZ2UoKXtcclxuXHJcbiAgICAgICAgdGhpcy5fdmFsaWRhdGVJbml0aWFsaXplZCgpO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbGFuZ3VhZ2VzWzBdO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogRGVmaW5lIHRoZSBsb2NhbGUgdGhhdCB3aWxsIGJlIHBsYWNlZCBhdCB0aGUgZnJvbnQgb2YgdGhlIGN1cnJlbnRseSBsb2FkZWQgbG9jYWxlcyBsaXN0IChtb3ZpbmcgYWxsIHRoZSBvdGhlcnMgb25lIHBvc2l0aW9uIHRvIHRoZSByaWdodCkuXHJcbiAgICAgKlxyXG4gICAgICogVGhpcyB3aWxsIGJlIHRoZSBmaXJzdCBsb2NhbGUgdG8gdXNlIHdoZW4gdHJ5aW5nIHRvIGdldCBhIHRyYW5zbGF0aW9uLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBsb2NhbGUgQSBjdXJyZW50bHkgbG9hZGVkIGxvY2FsZSB0aGF0IHdpbGwgYmUgbW92ZWQgdG8gdGhlIGZpcnN0IHBvc2l0aW9uIG9mIHRoZSBsb2FkZWQgbG9jYWxlcyBsaXN0LiBJZiB0aGUgc3BlY2lmaWVkIGxvY2FsZVxyXG4gICAgICogICAgICAgIGlzIG5vdCBjdXJyZW50bHkgbG9hZGVkLCBhbiBleGNlcHRpb24gd2lsbCBoYXBwZW4uXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgdm9pZFxyXG4gICAgICovXHJcbiAgICBzZXRQcmltYXJ5TG9jYWxlKGxvY2FsZTpzdHJpbmcpe1xyXG5cclxuICAgICAgICB0aGlzLl92YWxpZGF0ZUluaXRpYWxpemVkKCk7XHJcblxyXG4gICAgICAgIGlmKCF0aGlzLmlzTG9jYWxlTG9hZGVkKGxvY2FsZSkpe1xyXG5cclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGxvY2FsZSArICcgbm90IGxvYWRlZCcpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGV0IHJlc3VsdCA9IFtsb2NhbGVdO1xyXG5cclxuICAgICAgICBmb3IgKGxldCBsIG9mIHRoaXMuX2xvY2FsZXMpe1xyXG4gICAgICAgIFxyXG4gICAgICAgICAgICBpZihsICE9PSBsb2NhbGUpe1xyXG5cclxuICAgICAgICAgICAgICAgIHJlc3VsdC5wdXNoKGwpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB0aGlzLl9sb2NhbGVzID0gcmVzdWx0O1xyXG4gICAgICAgIHRoaXMuX2xhbmd1YWdlcyA9IHRoaXMuX2xvY2FsZXMubWFwKChsOiBzdHJpbmcpID0+IGwuc3Vic3RyaW5nKDAsIDIpKTtcclxuICAgICAgICB0aGlzLl9jYWNoZUhhc2hCYXNlU3RyaW5nID0gdGhpcy5fd2lsZENhcmRzRm9ybWF0ICsgdGhpcy5fbWlzc2luZ0tleUZvcm1hdCArIHRoaXMuX2xvY2FsZXNbMF07XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBNb3ZlcyB0aGUgc3BlY2lmaWVkIGxvY2FsZXMgdG8gdGhlIGJlZ2lubmluZyBvZiB0aGUgbG9jYWxlcyBsaXN0LiBUaGlzIGFsc28gYWx0ZXJzIHRoZSB0cmFuc2xhdGlvbiBwcmlvcml0eSBieSBzZXR0aW5nIHRoZSBmaXJzdFxyXG4gICAgICogcHJvdmlkZWQgbG9jYWxlIGFzIHRoZSBtb3N0IHByaW9yaXRhcnksIHRoZSBzZWNvbmQgYXMgdGhlIG5leHQgb25lIGFuZCBzby5cclxuICAgICAqXHJcbiAgICAgKiBUaGlzIG1ldGhvZCBiYXNpY2FsbHkgd29ya3MgZXhhY3RseSB0aGUgc2FtZSB3YXkgYXMgc2V0UHJpbWFyeUxvY2FsZSBidXQgbGV0dGluZyB1cyBhZGQgbWFueSBsb2NhbGVzIGF0IG9uY2UuXHJcbiAgICAgKlxyXG4gICAgICogQHNlZSBzZXRQcmltYXJ5TG9jYWxlKClcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gbG9jYWxlcyBBIGxpc3Qgb2YgbG9jYWxlcyB0byBiZSBtb3ZlZCB0byB0aGUgYmVnaW5uaW5nIG9mIHRoZSB0cmFuc2xhdGlvbiBwcmlvcml0eS4gRmlyc3QgbG9jYWxlcyBpdGVtIHdpbGwgYmUgdGhlIHByZWZlcmVkXHJcbiAgICAgKiAgICAgICAgbG9jYWxlIGZvciB0cmFuc2xhdGlvbiwgc2Vjb25kIHdpbGwgYmUgdGhlIG5leHQgb25lIGluIGNhc2Ugc29tZSBrZXkgaXMgbm90IHRyYW5zbGF0ZWQgZm9yIHRoZSBmaXJzdCBvbmUgYW5kIHNvLiBJZiBhbnkgb2YgdGhlXHJcbiAgICAgKiAgICAgICAgc3BlY2lmaWVkIGxvY2FsZXMgaXMgbm90IGN1cnJlbnRseSBsb2FkZWQsIGFuIGV4Y2VwdGlvbiB3aWxsIGhhcHBlbi5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB2b2lkXHJcbiAgICAgKi9cclxuICAgIHNldFByaW1hcnlMb2NhbGVzKGxvY2FsZXM6c3RyaW5nW10pe1xyXG5cclxuICAgICAgICBpZighQXJyYXkuaXNBcnJheShsb2NhbGVzKSB8fFxyXG4gICAgICAgICAgICAobmV3IFNldChsb2NhbGVzKS5zaXplICE9PSBsb2NhbGVzLmxlbmd0aCkgfHxcclxuICAgICAgICAgICAgbG9jYWxlcy5sZW5ndGggPT09IDApe1xyXG5cclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbG9jYWxlcyBtdXN0IGJlIG5vbiBlbXB0eSBzdHJpbmcgYXJyYXkgd2l0aCBubyBkdXBsaWNhdGUgZWxlbWVudHMnKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGZvciAobGV0IGkgPSBsb2NhbGVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblxyXG4gICAgICAgICAgICB0aGlzLnNldFByaW1hcnlMb2NhbGUobG9jYWxlc1tpXSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogRGVmaW5lIHRoZSAyIGRpZ2l0IGxhbmd1YWdlIHRoYXQgd2lsbCBiZSBwbGFjZWQgYXQgdGhlIGZyb250IG9mIHRoZSBjdXJyZW50bHkgbG9hZGVkIGxvY2FsZXMgbGlzdCAobW92aW5nIGFsbCB0aGUgb3RoZXJzIG9uZSBwb3NpdGlvbiB0byB0aGUgcmlnaHQpLlxyXG4gICAgICpcclxuICAgICAqIFRoaXMgd2lsbCBiZSB0aGUgZmlyc3QgbGFuZ3VhZ2UgdG8gdXNlIHdoZW4gdHJ5aW5nIHRvIGdldCBhIHRyYW5zbGF0aW9uLlxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSBsYW5ndWFnZSBBIDIgZGlnaXQgbGFuZ3VhZ2UgY29kZSB0aGF0IG1hdGNoZXMgd2l0aCBhbnkgb2YgdGhlIGN1cnJlbnRseSBsb2FkZWQgbG9jYWxlcywgd2hpY2ggd2lsbFxyXG4gICAgICogICAgICAgIGJlIG1vdmVkIHRvIHRoZSBmaXJzdCBwb3NpdGlvbiBvZiB0aGUgbG9hZGVkIGxvY2FsZXMgbGlzdC4gSWYgdGhlIHNwZWNpZmllZCBsYW5ndWFnZSBkb2VzIG5vdCBtYXRjaCB3aXRoXHJcbiAgICAgKiAgICAgICAgYSBsb2NhbGUgdGhhdCBpcyBjdXJyZW50bHkgbG9hZGVkLCBhbiBleGNlcHRpb24gd2lsbCBoYXBwZW4uXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMgdm9pZFxyXG4gICAgICovXHJcbiAgICBzZXRQcmltYXJ5TGFuZ3VhZ2UobGFuZ3VhZ2U6c3RyaW5nKXtcclxuXHJcbiAgICAgICAgZm9yKGxldCBsb2NhbGUgb2YgdGhpcy5fbG9jYWxlcyl7XHJcbiAgICAgICAgXHJcbiAgICAgICAgICAgIGlmKGxvY2FsZS5zdWJzdHJpbmcoMCwgMikgPT09IGxhbmd1YWdlKXtcclxuXHJcbiAgICAgICAgICAgICAgICB0aGlzLnNldFByaW1hcnlMb2NhbGUobG9jYWxlKTtcclxuXHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihsYW5ndWFnZSArICcgbm90IGxvYWRlZCcpO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBcclxuICAgIC8qKlxyXG4gICAgICogTW92ZXMgdGhlIGxvY2FsZXMgdGhhdCBtYXRjaCB0aGUgc3BlY2lmaWVkIGxhbmd1YWdlcyB0byB0aGUgYmVnaW5uaW5nIG9mIHRoZSBsb2NhbGVzIGxpc3QuXHJcbiAgICAgKiBXb3JrcyB0aGUgc2FtZSBhcyBzZXRQcmltYXJ5TG9jYWxlcygpIGJ1dCB3aXRoIGEgbGlzdCBvZiB0aGUgMiBkaWdpdCBsYW5ndWFnZSBjb2RlcyB0aGF0IG1hdGNoIHRoZSByZXNwZWN0aXZlIGxvY2FsZXMuXHJcbiAgICAgKlxyXG4gICAgICogQHNlZSBzZXRQcmltYXJ5TG9jYWxlKClcclxuICAgICAqIEBzZWUgc2V0UHJpbWFyeUxhbmd1YWdlKClcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gbGFuZ3VhZ2VzIEEgbGlzdCBvZiAyIGRpZ2l0IGxhbmd1YWdlIGNvZGVzIHRvIGJlIG1vdmVkIHRvIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHRyYW5zbGF0aW9uIHByaW9yaXR5LiBJZiBhbnkgb2YgdGhlXHJcbiAgICAgKiAgICAgICAgc3BlY2lmaWVkIGxhbmd1YWdlcyBkb2VzIG5vdCBtYXRjaCB3aXRoIGEgbG9jYWxlIHRoYXQgaXMgY3VycmVudGx5IGxvYWRlZCwgYW4gZXhjZXB0aW9uIHdpbGwgaGFwcGVuLlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHZvaWRcclxuICAgICAqL1xyXG4gICAgc2V0UHJpbWFyeUxhbmd1YWdlcyhsYW5ndWFnZXM6c3RyaW5nW10pe1xyXG5cclxuICAgICAgICBpZighQXJyYXkuaXNBcnJheShsYW5ndWFnZXMpIHx8XHJcbiAgICAgICAgICAgKG5ldyBTZXQobGFuZ3VhZ2VzKS5zaXplICE9PSBsYW5ndWFnZXMubGVuZ3RoKSB8fFxyXG4gICAgICAgICAgICBsYW5ndWFnZXMubGVuZ3RoID09PSAwKXtcclxuXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbGFuZ3VhZ2VzIG11c3QgYmUgbm9uIGVtcHR5IHN0cmluZyBhcnJheSB3aXRoIG5vIGR1cGxpY2F0ZSBlbGVtZW50cycpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yIChsZXQgaSA9IGxhbmd1YWdlcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cclxuICAgICAgICAgICAgdGhpcy5zZXRQcmltYXJ5TGFuZ3VhZ2UobGFuZ3VhZ2VzW2ldKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBDaGFuZ2UgdGhlIGxvYWRlZCBsb2NhbGVzIHRyYW5zbGF0aW9uIHByZWZlcmVuY2Ugb3JkZXIuIFRoZSBzYW1lIGxvY2FsZXMgdGhhdCBhcmUgY3VycmVudGx5IGxvYWRlZCBtdXN0IGJlIHBhc3NlZFxyXG4gICAgICogYnV0IHdpdGggYSBkaWZmZXJlbnQgb3JkZXIgdG8gY2hhbmdlIHRoZSB0cmFuc2xhdGlvbiBwcmlvcml0eS5cclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0gbG9jYWxlcyBBIGxpc3Qgd2l0aCB0aGUgbmV3IGxvY2FsZXMgdHJhbnNsYXRpb24gcHJpb3JpdHlcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB2b2lkXHJcbiAgICAgKi9cclxuICAgIHNldExvY2FsZXNPcmRlcihsb2NhbGVzOnN0cmluZ1tdKXtcclxuXHJcbiAgICAgICAgaWYobG9jYWxlcy5sZW5ndGggIT09IHRoaXMuX2xvY2FsZXMubGVuZ3RoKXtcclxuXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignbG9jYWxlcyBtdXN0IGNvbnRhaW4gYWxsIHRoZSBjdXJyZW50bHkgbG9hZGVkIGxvY2FsZXMnKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuX3ZhbGlkYXRlSW5pdGlhbGl6ZWQoKTtcclxuXHJcbiAgICAgICAgZm9yKGxldCBsb2NhbGUgb2YgbG9jYWxlcyl7XHJcbiAgICAgICAgXHJcbiAgICAgICAgICAgIGlmKCF0aGlzLmlzTG9jYWxlTG9hZGVkKGxvY2FsZSkpe1xyXG5cclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihsb2NhbGUgKyAnIG5vdCBsb2FkZWQnKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fbG9jYWxlcyA9IGxvY2FsZXM7XHJcbiAgICAgICAgdGhpcy5fbGFuZ3VhZ2VzID0gdGhpcy5fbG9jYWxlcy5tYXAoKGw6IHN0cmluZykgPT4gbC5zdWJzdHJpbmcoMCwgMikpO1xyXG4gICAgICAgIHRoaXMuX2NhY2hlSGFzaEJhc2VTdHJpbmcgPSB0aGlzLl93aWxkQ2FyZHNGb3JtYXQgKyB0aGlzLl9taXNzaW5nS2V5Rm9ybWF0ICsgdGhpcy5fbG9jYWxlc1swXTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIFRoaXMgaXMgYW4gYXV4IG1ldGhvZCB0byBpbXBsZW1lbnQgdGhlIFR1cmJvQ29tbW9ucyBTdHJpbmdVdGlscyByZXBsYWNlIG1ldGhvZC5cclxuICAgICAqIEl0IGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIG9uZSBvbiB0aGUgbGlicmFyeSwgYnV0IHdlIGltcGxlbWVudCBpdCBoZXJlIHRvIGF2b2lkIGhhdmluZyBhIGRlcGVuZGVuY3kgd2l0aCBUdXJib0NvbW1vbnNcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfcmVwbGFjZShzdHJpbmc6IHN0cmluZywgc2VhcmNoOnN0cmluZywgcmVwbGFjZW1lbnQ6IHN0cmluZykge1xyXG4gICAgICAgIFxyXG4gICAgICAgIGNvbnN0IGVzY2FwZWRTZWFyY2ggPSBzZWFyY2gucmVwbGFjZSgvWy4qKz9eJHt9KCl8W1xcXVxcXFxdL2csICdcXFxcJCYnKTtcclxuICAgICAgICAgICBcclxuICAgICAgICByZXR1cm4gc3RyaW5nLnJlcGxhY2UobmV3IFJlZ0V4cChlc2NhcGVkU2VhcmNoLCAnZycpLCByZXBsYWNlbWVudCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIFxyXG4gICAgLyoqXHJcbiAgICAgKiBUaGlzIGlzIGFuIGF1eCBtZXRob2QgdG8gaW1wbGVtZW50IHRoZSBUdXJib0NvbW1vbnMgU3RyaW5nVXRpbHMgaXNFbXB0eSBtZXRob2QuXHJcbiAgICAgKiBJdCBpcyBleGFjdGx5IHRoZSBzYW1lIGFzIHRoZSBvbmUgb24gdGhlIGxpYnJhcnksIGJ1dCB3ZSBpbXBsZW1lbnQgaXQgaGVyZSB0byBhdm9pZCBoYXZpbmcgYSBkZXBlbmRlbmN5IHdpdGggVHVyYm9Db21tb25zXHJcbiAgICAgKi9cclxuICAgIHByaXZhdGUgX2lzRW1wdHkoc3RyaW5nOnN0cmluZykge1xyXG4gICAgICAgIFxyXG4gICAgICAgIGxldCBpc1N0cmluZyA9ICh0eXBlb2Ygc3RyaW5nID09PSAnc3RyaW5nJyB8fCAoc3RyaW5nIGFzIGFueSkgaW5zdGFuY2VvZiBTdHJpbmcpO1xyXG4gICAgICAgICAgICAgICAgXHJcbiAgICAgICAgLy8gVGhyb3cgZXhjZXB0aW9uIGlmIG5vbiBzdHJpbmcgdmFsdWUgd2FzIHJlY2VpdmVkXHJcbiAgICAgICAgaWYoIWlzU3RyaW5nKXtcclxuXHJcbiAgICAgICAgICAgIC8vIEVtcHR5IG9yIG51bGwgdmFsdWUgaXMgY29uc2lkZXJlZCBlbXB0eVxyXG4gICAgICAgICAgICBpZihzdHJpbmcgPT0gbnVsbCB8fCBzdHJpbmcgPT0gJycpe1xyXG4gICAgXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwidmFsdWUgaXMgbm90IGEgc3RyaW5nXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZy5yZXBsYWNlKC9bIFxcblxcclxcdF0vZywgJycpID09PSAnJztcclxuICAgIH1cclxuICAgIFxyXG4gICAgXHJcbiAgICAvKipcclxuICAgICAqIFRoaXMgaXMgYW4gYXV4IG1ldGhvZCB0byBpbXBsZW1lbnQgdGhlIFR1cmJvQ29tbW9ucyBTdHJpbmdVdGlscyBmb3JjZU5vbkVtcHR5U3RyaW5nIG1ldGhvZC5cclxuICAgICAqIEl0IGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIG9uZSBvbiB0aGUgbGlicmFyeSwgYnV0IHdlIGltcGxlbWVudCBpdCBoZXJlIHRvIGF2b2lkIGhhdmluZyBhIGRlcGVuZGVuY3kgd2l0aCBUdXJib0NvbW1vbnNcclxuICAgICAqL1xyXG4gICAgcHJpdmF0ZSBfZm9yY2VOb25FbXB0eVN0cmluZyh2YWx1ZTphbnksIHZhbHVlTmFtZSA9ICcnLCBlcnJvck1lc3NhZ2UgPSAnbXVzdCBiZSBhIG5vbiBlbXB0eSBzdHJpbmcnKXtcclxuXHJcbiAgICAgICAgbGV0IGlzU3RyaW5nID0gKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgfHwgdmFsdWUgaW5zdGFuY2VvZiBTdHJpbmcpO1xyXG4gICAgICAgIFxyXG4gICAgICAgIGlmKCFpc1N0cmluZyB8fCB0aGlzLl9pc0VtcHR5KHZhbHVlKSl7XHJcblxyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IodmFsdWVOYW1lICsgJyAnICsgZXJyb3JNZXNzYWdlKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuIl19
|