@ngx-translate/core
Version:
The internationalization (i18n) library for Angular
1,278 lines (1,265 loc) • 124 kB
JavaScript
import { Injectable, EventEmitter, Inject, InjectionToken, ChangeDetectorRef, Directive, ElementRef, Input, Pipe, NgModule } from '@angular/core';
import { of, concat, merge, Observable } from 'rxjs';
import { map, share, switchMap, take, toArray } from 'rxjs/operators';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @abstract
*/
class TranslateLoader {
}
/**
* This loader is just a placeholder that does nothing, in case you don't need a loader at all
*/
class TranslateFakeLoader extends TranslateLoader {
/**
* @param {?} lang
* @return {?}
*/
getTranslation(lang) {
return of({});
}
}
TranslateFakeLoader.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @abstract
*/
class MissingTranslationHandler {
}
/**
* This handler is just a placeholder that does nothing, in case you don't need a missing translation handler at all
*/
class FakeMissingTranslationHandler {
/**
* @param {?} params
* @return {?}
*/
handle(params) {
return params.key;
}
}
FakeMissingTranslationHandler.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @abstract
*/
class TranslateCompiler {
}
/**
* This compiler is just a placeholder that does nothing, in case you don't need a compiler at all
*/
class TranslateFakeCompiler extends TranslateCompiler {
/**
* @param {?} value
* @param {?} lang
* @return {?}
*/
compile(value, lang) {
return value;
}
/**
* @param {?} translations
* @param {?} lang
* @return {?}
*/
compileTranslations(translations, lang) {
return translations;
}
}
TranslateFakeCompiler.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/* tslint:disable */
/**
* Determines if two objects or two values are equivalent.
*
* Two objects or values are considered equivalent if at least one of the following is true:
*
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties are equal by
* comparing them with `equals`.
*
* @param {?} o1 Object or value to compare.
* @param {?} o2 Object or value to compare.
* @return {?} true if arguments are equal.
*/
function equals(o1, o2) {
if (o1 === o2)
return true;
if (o1 === null || o2 === null)
return false;
if (o1 !== o1 && o2 !== o2)
return true; // NaN === NaN
// NaN === NaN
/** @type {?} */
let t1 = typeof o1;
/** @type {?} */
let t2 = typeof o2;
/** @type {?} */
let length;
/** @type {?} */
let key;
/** @type {?} */
let keySet;
if (t1 == t2 && t1 == 'object') {
if (Array.isArray(o1)) {
if (!Array.isArray(o2))
return false;
if ((length = o1.length) == o2.length) {
for (key = 0; key < length; key++) {
if (!equals(o1[key], o2[key]))
return false;
}
return true;
}
}
else {
if (Array.isArray(o2)) {
return false;
}
keySet = Object.create(null);
for (key in o1) {
if (!equals(o1[key], o2[key])) {
return false;
}
keySet[key] = true;
}
for (key in o2) {
if (!(key in keySet) && typeof o2[key] !== 'undefined') {
return false;
}
}
return true;
}
}
return false;
}
/* tslint:enable */
/**
* @param {?} value
* @return {?}
*/
function isDefined(value) {
return typeof value !== 'undefined' && value !== null;
}
/**
* @param {?} item
* @return {?}
*/
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
/**
* @param {?} target
* @param {?} source
* @return {?}
*/
function mergeDeep(target, source) {
/** @type {?} */
let output = Object.assign({}, target);
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach((key) => {
if (isObject(source[key])) {
if (!(key in target)) {
Object.assign(output, { [key]: source[key] });
}
else {
output[key] = mergeDeep(target[key], source[key]);
}
}
else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @abstract
*/
class TranslateParser {
}
class TranslateDefaultParser extends TranslateParser {
constructor() {
super(...arguments);
this.templateMatcher = /{{\s?([^{}\s]*)\s?}}/g;
}
/**
* @param {?} expr
* @param {?=} params
* @return {?}
*/
interpolate(expr, params) {
/** @type {?} */
let result;
if (typeof expr === 'string') {
result = this.interpolateString(expr, params);
}
else if (typeof expr === 'function') {
result = this.interpolateFunction(expr, params);
}
else {
// this should not happen, but an unrelated TranslateService test depends on it
result = (/** @type {?} */ (expr));
}
return result;
}
/**
* @param {?} target
* @param {?} key
* @return {?}
*/
getValue(target, key) {
/** @type {?} */
let keys = key.split('.');
key = '';
do {
key += keys.shift();
if (isDefined(target) && isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) {
target = target[key];
key = '';
}
else if (!keys.length) {
target = undefined;
}
else {
key += '.';
}
} while (keys.length);
return target;
}
/**
* @param {?} fn
* @param {?=} params
* @return {?}
*/
interpolateFunction(fn, params) {
return fn(params);
}
/**
* @param {?} expr
* @param {?=} params
* @return {?}
*/
interpolateString(expr, params) {
if (!params) {
return expr;
}
return expr.replace(this.templateMatcher, (substring, b) => {
/** @type {?} */
let r = this.getValue(params, b);
return isDefined(r) ? r : substring;
});
}
}
TranslateDefaultParser.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TranslateStore {
constructor() {
/**
* The lang currently used
*/
this.currentLang = this.defaultLang;
/**
* a list of translations per lang
*/
this.translations = {};
/**
* an array of langs
*/
this.langs = [];
/**
* An EventEmitter to listen to translation change events
* onTranslationChange.subscribe((params: TranslationChangeEvent) => {
* // do something
* });
*/
this.onTranslationChange = new EventEmitter();
/**
* An EventEmitter to listen to lang change events
* onLangChange.subscribe((params: LangChangeEvent) => {
* // do something
* });
*/
this.onLangChange = new EventEmitter();
/**
* An EventEmitter to listen to default lang change events
* onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
* // do something
* });
*/
this.onDefaultLangChange = new EventEmitter();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/** @type {?} */
const USE_STORE = new InjectionToken('USE_STORE');
/** @type {?} */
const USE_DEFAULT_LANG = new InjectionToken('USE_DEFAULT_LANG');
class TranslateService {
/**
*
* @param {?} store an instance of the store (that is supposed to be unique)
* @param {?} currentLoader An instance of the loader currently used
* @param {?} compiler An instance of the compiler currently used
* @param {?} parser An instance of the parser currently used
* @param {?} missingTranslationHandler A handler for missing translations.
* @param {?=} useDefaultLang whether we should use default language translation when current language translation is missing.
* @param {?=} isolate whether this service should use the store or not
*/
constructor(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang = true, isolate = false) {
this.store = store;
this.currentLoader = currentLoader;
this.compiler = compiler;
this.parser = parser;
this.missingTranslationHandler = missingTranslationHandler;
this.useDefaultLang = useDefaultLang;
this.isolate = isolate;
this.pending = false;
this._onTranslationChange = new EventEmitter();
this._onLangChange = new EventEmitter();
this._onDefaultLangChange = new EventEmitter();
this._langs = [];
this._translations = {};
this._translationRequests = {};
}
/**
* An EventEmitter to listen to translation change events
* onTranslationChange.subscribe((params: TranslationChangeEvent) => {
* // do something
* });
* @return {?}
*/
get onTranslationChange() {
return this.isolate ? this._onTranslationChange : this.store.onTranslationChange;
}
/**
* An EventEmitter to listen to lang change events
* onLangChange.subscribe((params: LangChangeEvent) => {
* // do something
* });
* @return {?}
*/
get onLangChange() {
return this.isolate ? this._onLangChange : this.store.onLangChange;
}
/**
* An EventEmitter to listen to default lang change events
* onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
* // do something
* });
* @return {?}
*/
get onDefaultLangChange() {
return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange;
}
/**
* The default lang to fallback when translations are missing on the current lang
* @return {?}
*/
get defaultLang() {
return this.isolate ? this._defaultLang : this.store.defaultLang;
}
/**
* @param {?} defaultLang
* @return {?}
*/
set defaultLang(defaultLang) {
if (this.isolate) {
this._defaultLang = defaultLang;
}
else {
this.store.defaultLang = defaultLang;
}
}
/**
* The lang currently used
* @return {?}
*/
get currentLang() {
return this.isolate ? this._currentLang : this.store.currentLang;
}
/**
* @param {?} currentLang
* @return {?}
*/
set currentLang(currentLang) {
if (this.isolate) {
this._currentLang = currentLang;
}
else {
this.store.currentLang = currentLang;
}
}
/**
* an array of langs
* @return {?}
*/
get langs() {
return this.isolate ? this._langs : this.store.langs;
}
/**
* @param {?} langs
* @return {?}
*/
set langs(langs) {
if (this.isolate) {
this._langs = langs;
}
else {
this.store.langs = langs;
}
}
/**
* a list of translations per lang
* @return {?}
*/
get translations() {
return this.isolate ? this._translations : this.store.translations;
}
/**
* @param {?} translations
* @return {?}
*/
set translations(translations) {
if (this.isolate) {
this._translations = translations;
}
else {
this.store.translations = translations;
}
}
/**
* Sets the default language to use as a fallback
* @param {?} lang
* @return {?}
*/
setDefaultLang(lang) {
if (lang === this.defaultLang) {
return;
}
/** @type {?} */
let pending = this.retrieveTranslations(lang);
if (typeof pending !== "undefined") {
// on init set the defaultLang immediately
if (!this.defaultLang) {
this.defaultLang = lang;
}
pending.pipe(take(1))
.subscribe((res) => {
this.changeDefaultLang(lang);
});
}
else { // we already have this language
this.changeDefaultLang(lang);
}
}
/**
* Gets the default language used
* @return {?}
*/
getDefaultLang() {
return this.defaultLang;
}
/**
* Changes the lang currently used
* @param {?} lang
* @return {?}
*/
use(lang) {
// don't change the language if the language given is already selected
if (lang === this.currentLang) {
return of(this.translations[lang]);
}
/** @type {?} */
let pending = this.retrieveTranslations(lang);
if (typeof pending !== "undefined") {
// on init set the currentLang immediately
if (!this.currentLang) {
this.currentLang = lang;
}
pending.pipe(take(1))
.subscribe((res) => {
this.changeLang(lang);
});
return pending;
}
else { // we have this language, return an Observable
this.changeLang(lang);
return of(this.translations[lang]);
}
}
/**
* Retrieves the given translations
* @param {?} lang
* @return {?}
*/
retrieveTranslations(lang) {
/** @type {?} */
let pending;
// if this language is unavailable, ask for it
if (typeof this.translations[lang] === "undefined") {
this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang);
pending = this._translationRequests[lang];
}
return pending;
}
/**
* Gets an object of translations for a given language with the current loader
* and passes it through the compiler
* @param {?} lang
* @return {?}
*/
getTranslation(lang) {
this.pending = true;
/** @type {?} */
const loadingTranslations = this.currentLoader.getTranslation(lang).pipe(share());
this.loadingTranslations = loadingTranslations.pipe(take(1), map((res) => this.compiler.compileTranslations(res, lang)), share());
this.loadingTranslations
.subscribe((res) => {
this.translations[lang] = res;
this.updateLangs();
this.pending = false;
}, (err) => {
this.pending = false;
});
return loadingTranslations;
}
/**
* Manually sets an object of translations for a given language
* after passing it through the compiler
* @param {?} lang
* @param {?} translations
* @param {?=} shouldMerge
* @return {?}
*/
setTranslation(lang, translations, shouldMerge = false) {
translations = this.compiler.compileTranslations(translations, lang);
if (shouldMerge && this.translations[lang]) {
this.translations[lang] = mergeDeep(this.translations[lang], translations);
}
else {
this.translations[lang] = translations;
}
this.updateLangs();
this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
}
/**
* Returns an array of currently available langs
* @return {?}
*/
getLangs() {
return this.langs;
}
/**
* Add available langs
* @param {?} langs
* @return {?}
*/
addLangs(langs) {
langs.forEach((lang) => {
if (this.langs.indexOf(lang) === -1) {
this.langs.push(lang);
}
});
}
/**
* Update the list of available langs
* @return {?}
*/
updateLangs() {
this.addLangs(Object.keys(this.translations));
}
/**
* Returns the parsed result of the translations
* @param {?} translations
* @param {?} key
* @param {?=} interpolateParams
* @return {?}
*/
getParsedResult(translations, key, interpolateParams) {
/** @type {?} */
let res;
if (key instanceof Array) {
/** @type {?} */
let result = {};
/** @type {?} */
let observables = false;
for (let k of key) {
result[k] = this.getParsedResult(translations, k, interpolateParams);
if (typeof result[k].subscribe === "function") {
observables = true;
}
}
if (observables) {
/** @type {?} */
let mergedObs;
for (let k of key) {
/** @type {?} */
let obs = typeof result[k].subscribe === "function" ? result[k] : of((/** @type {?} */ (result[k])));
if (typeof mergedObs === "undefined") {
mergedObs = obs;
}
else {
mergedObs = merge(mergedObs, obs);
}
}
return mergedObs.pipe(toArray(), map((arr) => {
/** @type {?} */
let obj = {};
arr.forEach((value, index) => {
obj[key[index]] = value;
});
return obj;
}));
}
return result;
}
if (translations) {
res = this.parser.interpolate(this.parser.getValue(translations, key), interpolateParams);
}
if (typeof res === "undefined" && this.defaultLang && this.defaultLang !== this.currentLang && this.useDefaultLang) {
res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams);
}
if (typeof res === "undefined") {
/** @type {?} */
let params = { key, translateService: this };
if (typeof interpolateParams !== 'undefined') {
params.interpolateParams = interpolateParams;
}
res = this.missingTranslationHandler.handle(params);
}
return typeof res !== "undefined" ? res : key;
}
/**
* Gets the translated value of a key (or an array of keys)
* @param {?} key
* @param {?=} interpolateParams
* @return {?} the translated key, or an object of translated keys
*/
get(key, interpolateParams) {
if (!isDefined(key) || !key.length) {
throw new Error(`Parameter "key" required`);
}
// check if we are loading a new translation to use
if (this.pending) {
return Observable.create((observer) => {
/** @type {?} */
let onComplete = (res) => {
observer.next(res);
observer.complete();
};
/** @type {?} */
let onError = (err) => {
observer.error(err);
};
this.loadingTranslations.subscribe((res) => {
res = this.getParsedResult(res, key, interpolateParams);
if (typeof res.subscribe === "function") {
res.subscribe(onComplete, onError);
}
else {
onComplete(res);
}
}, onError);
});
}
else {
/** @type {?} */
let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
if (typeof res.subscribe === "function") {
return res;
}
else {
return of(res);
}
}
}
/**
* Returns a stream of translated values of a key (or an array of keys) which updates
* whenever the language changes.
* @param {?} key
* @param {?=} interpolateParams
* @return {?} A stream of the translated key, or an object of translated keys
*/
stream(key, interpolateParams) {
if (!isDefined(key) || !key.length) {
throw new Error(`Parameter "key" required`);
}
return concat(this.get(key, interpolateParams), this.onLangChange.pipe(switchMap((event) => {
/** @type {?} */
const res = this.getParsedResult(event.translations, key, interpolateParams);
if (typeof res.subscribe === "function") {
return res;
}
else {
return of(res);
}
})));
}
/**
* Returns a translation instantly from the internal state of loaded translation.
* All rules regarding the current language, the preferred language of even fallback languages will be used except any promise handling.
* @param {?} key
* @param {?=} interpolateParams
* @return {?}
*/
instant(key, interpolateParams) {
if (!isDefined(key) || !key.length) {
throw new Error(`Parameter "key" required`);
}
/** @type {?} */
let res = this.getParsedResult(this.translations[this.currentLang], key, interpolateParams);
if (typeof res.subscribe !== "undefined") {
if (key instanceof Array) {
/** @type {?} */
let obj = {};
key.forEach((value, index) => {
obj[key[index]] = key[index];
});
return obj;
}
return key;
}
else {
return res;
}
}
/**
* Sets the translated value of a key, after compiling it
* @param {?} key
* @param {?} value
* @param {?=} lang
* @return {?}
*/
set(key, value, lang = this.currentLang) {
this.translations[lang][key] = this.compiler.compile(value, lang);
this.updateLangs();
this.onTranslationChange.emit({ lang: lang, translations: this.translations[lang] });
}
/**
* Changes the current lang
* @param {?} lang
* @return {?}
*/
changeLang(lang) {
this.currentLang = lang;
this.onLangChange.emit({ lang: lang, translations: this.translations[lang] });
// if there is no default lang, use the one that we just set
if (!this.defaultLang) {
this.changeDefaultLang(lang);
}
}
/**
* Changes the default lang
* @param {?} lang
* @return {?}
*/
changeDefaultLang(lang) {
this.defaultLang = lang;
this.onDefaultLangChange.emit({ lang: lang, translations: this.translations[lang] });
}
/**
* Allows to reload the lang file from the file
* @param {?} lang
* @return {?}
*/
reloadLang(lang) {
this.resetLang(lang);
return this.getTranslation(lang);
}
/**
* Deletes inner translation
* @param {?} lang
* @return {?}
*/
resetLang(lang) {
this._translationRequests[lang] = undefined;
this.translations[lang] = undefined;
}
/**
* Returns the language code name from the browser, e.g. "de"
* @return {?}
*/
getBrowserLang() {
if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
return undefined;
}
/** @type {?} */
let browserLang = window.navigator.languages ? window.navigator.languages[0] : null;
browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
if (browserLang.indexOf('-') !== -1) {
browserLang = browserLang.split('-')[0];
}
if (browserLang.indexOf('_') !== -1) {
browserLang = browserLang.split('_')[0];
}
return browserLang;
}
/**
* Returns the culture language code name from the browser, e.g. "de-DE"
* @return {?}
*/
getBrowserCultureLang() {
if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
return undefined;
}
/** @type {?} */
let browserCultureLang = window.navigator.languages ? window.navigator.languages[0] : null;
browserCultureLang = browserCultureLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
return browserCultureLang;
}
}
TranslateService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
TranslateService.ctorParameters = () => [
{ type: TranslateStore },
{ type: TranslateLoader },
{ type: TranslateCompiler },
{ type: TranslateParser },
{ type: MissingTranslationHandler },
{ type: Boolean, decorators: [{ type: Inject, args: [USE_DEFAULT_LANG,] }] },
{ type: Boolean, decorators: [{ type: Inject, args: [USE_STORE,] }] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TranslateDirective {
/**
* @param {?} translateService
* @param {?} element
* @param {?} _ref
*/
constructor(translateService, element, _ref) {
this.translateService = translateService;
this.element = element;
this._ref = _ref;
// subscribe to onTranslationChange event, in case the translations of the current lang change
if (!this.onTranslationChangeSub) {
this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event) => {
if (event.lang === this.translateService.currentLang) {
this.checkNodes(true, event.translations);
}
});
}
// subscribe to onLangChange event, in case the language changes
if (!this.onLangChangeSub) {
this.onLangChangeSub = this.translateService.onLangChange.subscribe((event) => {
this.checkNodes(true, event.translations);
});
}
// subscribe to onDefaultLangChange event, in case the default language changes
if (!this.onDefaultLangChangeSub) {
this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event) => {
this.checkNodes(true);
});
}
}
/**
* @param {?} key
* @return {?}
*/
set translate(key) {
if (key) {
this.key = key;
this.checkNodes();
}
}
/**
* @param {?} params
* @return {?}
*/
set translateParams(params) {
if (!equals(this.currentParams, params)) {
this.currentParams = params;
this.checkNodes(true);
}
}
/**
* @return {?}
*/
ngAfterViewChecked() {
this.checkNodes();
}
/**
* @param {?=} forceUpdate
* @param {?=} translations
* @return {?}
*/
checkNodes(forceUpdate = false, translations) {
/** @type {?} */
let nodes = this.element.nativeElement.childNodes;
// if the element is empty
if (!nodes.length) {
// we add the key as content
this.setContent(this.element.nativeElement, this.key);
nodes = this.element.nativeElement.childNodes;
}
for (let i = 0; i < nodes.length; ++i) {
/** @type {?} */
let node = nodes[i];
if (node.nodeType === 3) { // node type 3 is a text node
// node type 3 is a text node
/** @type {?} */
let key;
if (this.key) {
key = this.key;
if (forceUpdate) {
node.lastKey = null;
}
}
else {
/** @type {?} */
let content = this.getContent(node);
/** @type {?} */
let trimmedContent = content.trim();
if (trimmedContent.length) {
// we want to use the content as a key, not the translation value
if (content !== node.currentValue) {
key = trimmedContent;
// the content was changed from the user, we'll use it as a reference if needed
node.originalContent = this.getContent(node);
}
else if (node.originalContent && forceUpdate) { // the content seems ok, but the lang has changed
node.lastKey = null;
// the current content is the translation, not the key, use the last real content as key
key = node.originalContent.trim();
}
}
}
this.updateValue(key, node, translations);
}
}
}
/**
* @param {?} key
* @param {?} node
* @param {?} translations
* @return {?}
*/
updateValue(key, node, translations) {
if (key) {
if (node.lastKey === key && this.lastParams === this.currentParams) {
return;
}
this.lastParams = this.currentParams;
/** @type {?} */
let onTranslation = (res) => {
if (res !== key) {
node.lastKey = key;
}
if (!node.originalContent) {
node.originalContent = this.getContent(node);
}
node.currentValue = isDefined(res) ? res : (node.originalContent || key);
// we replace in the original content to preserve spaces that we might have trimmed
this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue));
this._ref.markForCheck();
};
if (isDefined(translations)) {
/** @type {?} */
let res = this.translateService.getParsedResult(translations, key, this.currentParams);
if (typeof res.subscribe === "function") {
res.subscribe(onTranslation);
}
else {
onTranslation(res);
}
}
else {
this.translateService.get(key, this.currentParams).subscribe(onTranslation);
}
}
}
/**
* @param {?} node
* @return {?}
*/
getContent(node) {
return isDefined(node.textContent) ? node.textContent : node.data;
}
/**
* @param {?} node
* @param {?} content
* @return {?}
*/
setContent(node, content) {
if (isDefined(node.textContent)) {
node.textContent = content;
}
else {
node.data = content;
}
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this.onLangChangeSub) {
this.onLangChangeSub.unsubscribe();
}
if (this.onDefaultLangChangeSub) {
this.onDefaultLangChangeSub.unsubscribe();
}
if (this.onTranslationChangeSub) {
this.onTranslationChangeSub.unsubscribe();
}
}
}
TranslateDirective.decorators = [
{ type: Directive, args: [{
selector: '[translate],[ngx-translate]'
},] }
];
/** @nocollapse */
TranslateDirective.ctorParameters = () => [
{ type: TranslateService },
{ type: ElementRef },
{ type: ChangeDetectorRef }
];
TranslateDirective.propDecorators = {
translate: [{ type: Input }],
translateParams: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TranslatePipe {
/**
* @param {?} translate
* @param {?} _ref
*/
constructor(translate, _ref) {
this.translate = translate;
this._ref = _ref;
this.value = '';
}
/**
* @param {?} key
* @param {?=} interpolateParams
* @param {?=} translations
* @return {?}
*/
updateValue(key, interpolateParams, translations) {
/** @type {?} */
let onTranslation = (res) => {
this.value = res !== undefined ? res : key;
this.lastKey = key;
this._ref.markForCheck();
};
if (translations) {
/** @type {?} */
let res = this.translate.getParsedResult(translations, key, interpolateParams);
if (typeof res.subscribe === 'function') {
res.subscribe(onTranslation);
}
else {
onTranslation(res);
}
}
this.translate.get(key, interpolateParams).subscribe(onTranslation);
}
/**
* @param {?} query
* @param {...?} args
* @return {?}
*/
transform(query, ...args) {
if (!query || query.length === 0) {
return query;
}
// if we ask another time for the same key, return the last value
if (equals(query, this.lastKey) && equals(args, this.lastParams)) {
return this.value;
}
/** @type {?} */
let interpolateParams;
if (isDefined(args[0]) && args.length) {
if (typeof args[0] === 'string' && args[0].length) {
// we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'}
// which is why we might need to change it to real JSON objects such as {"n":1} or {"n":"v"}
/** @type {?} */
let validArgs = args[0]
.replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":')
.replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"');
try {
interpolateParams = JSON.parse(validArgs);
}
catch (e) {
throw new SyntaxError(`Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`);
}
}
else if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
interpolateParams = args[0];
}
}
// store the query, in case it changes
this.lastKey = query;
// store the params, in case they change
this.lastParams = args;
// set the value
this.updateValue(query, interpolateParams);
// if there is a subscription to onLangChange, clean it
this._dispose();
// subscribe to onTranslationChange event, in case the translations change
if (!this.onTranslationChange) {
this.onTranslationChange = this.translate.onTranslationChange.subscribe((event) => {
if (this.lastKey && event.lang === this.translate.currentLang) {
this.lastKey = null;
this.updateValue(query, interpolateParams, event.translations);
}
});
}
// subscribe to onLangChange event, in case the language changes
if (!this.onLangChange) {
this.onLangChange = this.translate.onLangChange.subscribe((event) => {
if (this.lastKey) {
this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
this.updateValue(query, interpolateParams, event.translations);
}
});
}
// subscribe to onDefaultLangChange event, in case the default language changes
if (!this.onDefaultLangChange) {
this.onDefaultLangChange = this.translate.onDefaultLangChange.subscribe(() => {
if (this.lastKey) {
this.lastKey = null; // we want to make sure it doesn't return the same value until it's been updated
this.updateValue(query, interpolateParams);
}
});
}
return this.value;
}
/**
* Clean any existing subscription to change events
* @return {?}
*/
_dispose() {
if (typeof this.onTranslationChange !== 'undefined') {
this.onTranslationChange.unsubscribe();
this.onTranslationChange = undefined;
}
if (typeof this.onLangChange !== 'undefined') {
this.onLangChange.unsubscribe();
this.onLangChange = undefined;
}
if (typeof this.onDefaultLangChange !== 'undefined') {
this.onDefaultLangChange.unsubscribe();
this.onDefaultLangChange = undefined;
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this._dispose();
}
}
TranslatePipe.decorators = [
{ type: Injectable },
{ type: Pipe, args: [{
name: 'translate',
pure: false // required to update the value when the promise is resolved
},] }
];
/** @nocollapse */
TranslatePipe.ctorParameters = () => [
{ type: TranslateService },
{ type: ChangeDetectorRef }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TranslateModule {
/**
* Use this method in your root module to provide the TranslateService
* @param {?=} config
* @return {?}
*/
static forRoot(config = {}) {
return {
ngModule: TranslateModule,
providers: [
config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
TranslateStore,
{ provide: USE_STORE, useValue: config.isolate },
{ provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
TranslateService
]
};
}
/**
* Use this method in your other (non root) modules to import the directive/pipe
* @param {?=} config
* @return {?}
*/
static forChild(config = {}) {
return {
ngModule: TranslateModule,
providers: [
config.loader || { provide: TranslateLoader, useClass: TranslateFakeLoader },
config.compiler || { provide: TranslateCompiler, useClass: TranslateFakeCompiler },
config.parser || { provide: TranslateParser, useClass: TranslateDefaultParser },
config.missingTranslationHandler || { provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler },
{ provide: USE_STORE, useValue: config.isolate },
{ provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang },
TranslateService
]
};
}
}
TranslateModule.decorators = [
{ type: NgModule, args: [{
declarations: [
TranslatePipe,
TranslateDirective
],
exports: [
TranslatePipe,
TranslateDirective
]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
export { TranslateModule, TranslateLoader, TranslateFakeLoader, USE_STORE, USE_DEFAULT_LANG, TranslateService, MissingTranslationHandler, FakeMissingTranslationHandler, TranslateParser, TranslateDefaultParser, TranslateCompiler, TranslateFakeCompiler, TranslateDirective, TranslatePipe, TranslateStore };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LXRyYW5zbGF0ZS1jb3JlLmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi90cmFuc2xhdGUubG9hZGVyLnRzIiwibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi9taXNzaW5nLXRyYW5zbGF0aW9uLWhhbmRsZXIudHMiLCJuZzovL0BuZ3gtdHJhbnNsYXRlL2NvcmUvbGliL3RyYW5zbGF0ZS5jb21waWxlci50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdXRpbC50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnBhcnNlci50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnN0b3JlLnRzIiwibmc6Ly9Abmd4LXRyYW5zbGF0ZS9jb3JlL2xpYi90cmFuc2xhdGUuc2VydmljZS50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLmRpcmVjdGl2ZS50cyIsIm5nOi8vQG5neC10cmFuc2xhdGUvY29yZS9saWIvdHJhbnNsYXRlLnBpcGUudHMiLCJuZzovL0BuZ3gtdHJhbnNsYXRlL2NvcmUvcHVibGljX2FwaS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0luamVjdGFibGV9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQge09ic2VydmFibGUsIG9mfSBmcm9tIFwicnhqc1wiO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVHJhbnNsYXRlTG9hZGVyIHtcbiAgYWJzdHJhY3QgZ2V0VHJhbnNsYXRpb24obGFuZzogc3RyaW5nKTogT2JzZXJ2YWJsZTxhbnk+O1xufVxuXG4vKipcbiAqIFRoaXMgbG9hZGVyIGlzIGp1c3QgYSBwbGFjZWhvbGRlciB0aGF0IGRvZXMgbm90aGluZywgaW4gY2FzZSB5b3UgZG9uJ3QgbmVlZCBhIGxvYWRlciBhdCBhbGxcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFRyYW5zbGF0ZUZha2VMb2FkZXIgZXh0ZW5kcyBUcmFuc2xhdGVMb2FkZXIge1xuICBnZXRUcmFuc2xhdGlvbihsYW5nOiBzdHJpbmcpOiBPYnNlcnZhYmxlPGFueT4ge1xuICAgIHJldHVybiBvZih7fSk7XG4gIH1cbn1cbiIsImltcG9ydCB7SW5qZWN0YWJsZX0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7VHJhbnNsYXRlU2VydmljZX0gZnJvbSBcIi4vdHJhbnNsYXRlLnNlcnZpY2VcIjtcblxuZXhwb3J0IGludGVyZmFjZSBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zIHtcbiAgLyoqXG4gICAqIHRoZSBrZXkgdGhhdCdzIG1pc3NpbmcgaW4gdHJhbnNsYXRpb24gZmlsZXNcbiAgICovXG4gIGtleTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBhbiBpbnN0YW5jZSBvZiB0aGUgc2VydmljZSB0aGF0IHdhcyB1bmFibGUgdG8gdHJhbnNsYXRlIHRoZSBrZXkuXG4gICAqL1xuICB0cmFuc2xhdGVTZXJ2aWNlOiBUcmFuc2xhdGVTZXJ2aWNlO1xuXG4gIC8qKlxuICAgKiBpbnRlcnBvbGF0aW9uIHBhcmFtcyB0aGF0IHdlcmUgcGFzc2VkIGFsb25nIGZvciB0cmFuc2xhdGluZyB0aGUgZ2l2ZW4ga2V5LlxuICAgKi9cbiAgaW50ZXJwb2xhdGVQYXJhbXM/OiBPYmplY3Q7XG59XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyIHtcbiAgLyoqXG4gICAqIEEgZnVuY3Rpb24gdGhhdCBoYW5kbGVzIG1pc3NpbmcgdHJhbnNsYXRpb25zLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyYW1zIGNvbnRleHQgZm9yIHJlc29sdmluZyBhIG1pc3NpbmcgdHJhbnNsYXRpb25cbiAgICogQHJldHVybnMgYSB2YWx1ZSBvciBhbiBvYnNlcnZhYmxlXG4gICAqIElmIGl0IHJldHVybnMgYSB2YWx1ZSwgdGhlbiB0aGlzIHZhbHVlIGlzIHVzZWQuXG4gICAqIElmIGl0IHJldHVybiBhbiBvYnNlcnZhYmxlLCB0aGUgdmFsdWUgcmV0dXJuZWQgYnkgdGhpcyBvYnNlcnZhYmxlIHdpbGwgYmUgdXNlZCAoZXhjZXB0IGlmIHRoZSBtZXRob2Qgd2FzIFwiaW5zdGFudFwiKS5cbiAgICogSWYgaXQgZG9lc24ndCByZXR1cm4gdGhlbiB0aGUga2V5IHdpbGwgYmUgdXNlZCBhcyBhIHZhbHVlXG4gICAqL1xuICBhYnN0cmFjdCBoYW5kbGUocGFyYW1zOiBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zKTogYW55O1xufVxuXG4vKipcbiAqIFRoaXMgaGFuZGxlciBpcyBqdXN0IGEgcGxhY2Vob2xkZXIgdGhhdCBkb2VzIG5vdGhpbmcsIGluIGNhc2UgeW91IGRvbid0IG5lZWQgYSBtaXNzaW5nIHRyYW5zbGF0aW9uIGhhbmRsZXIgYXQgYWxsXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBGYWtlTWlzc2luZ1RyYW5zbGF0aW9uSGFuZGxlciBpbXBsZW1lbnRzIE1pc3NpbmdUcmFuc2xhdGlvbkhhbmRsZXIge1xuICBoYW5kbGUocGFyYW1zOiBNaXNzaW5nVHJhbnNsYXRpb25IYW5kbGVyUGFyYW1zKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcGFyYW1zLmtleTtcbiAgfVxufVxuIiwiaW1wb3J0IHtJbmplY3RhYmxlfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgVHJhbnNsYXRlQ29tcGlsZXIge1xuICBhYnN0cmFjdCBjb21waWxlKHZhbHVlOiBzdHJpbmcsIGxhbmc6IHN0cmluZyk6IHN0cmluZyB8IEZ1bmN0aW9uO1xuXG4gIGFic3RyYWN0IGNvbXBpbGVUcmFuc2xhdGlvbnModHJhbnNsYXRpb25zOiBhbnksIGxhbmc6IHN0cmluZyk6IGFueTtcbn1cblxuLyoqXG4gKiBUaGlzIGNvbXBpbGVyIGlzIGp1c3QgYSBwbGFjZWhvbGRlciB0aGF0IGRvZXMgbm90aGluZywgaW4gY2FzZSB5b3UgZG9uJ3QgbmVlZCBhIGNvbXBpbGVyIGF0IGFsbFxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgVHJhbnNsYXRlRmFrZUNvbXBpbGVyIGV4dGVuZHMgVHJhbnNsYXRlQ29tcGlsZXIge1xuICBjb21waWxlKHZhbHVlOiBzdHJpbmcsIGxhbmc6IHN0cmluZyk6IHN0cmluZyB8IEZ1bmN0aW9uIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICBjb21waWxlVHJhbnNsYXRpb25zKHRyYW5zbGF0aW9uczogYW55LCBsYW5nOiBzdHJpbmcpOiBhbnkge1xuICAgIHJldHVybiB0cmFuc2xhdGlvbnM7XG4gIH1cbn1cbiIsIi8qIHRzbGludDpkaXNhYmxlICovXG4vKipcbiAqIERldGVybWluZXMgaWYgdHdvIG9iamVjdHMgb3IgdHdvIHZhbHVlcyBhcmUgZXF1aXZhbGVudC5cbiAqXG4gKiBUd28gb2JqZWN0cyBvciB2YWx1ZXMgYXJlIGNvbnNpZGVyZWQgZXF1aXZhbGVudCBpZiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZyBpcyB0cnVlOlxuICpcbiAqICogQm90aCBvYmplY3RzIG9yIHZhbHVlcyBwYXNzIGA9PT1gIGNvbXBhcmlzb24uXG4gKiAqIEJvdGggb2JqZWN0cyBvciB2YWx1ZXMgYXJlIG9mIHRoZSBzYW1lIHR5cGUgYW5kIGFsbCBvZiB0aGVpciBwcm9wZXJ0aWVzIGFyZSBlcXVhbCBieVxuICogICBjb21wYXJpbmcgdGhlbSB3aXRoIGBlcXVhbHNgLlxuICpcbiAqIEBwYXJhbSBvMSBPYmplY3Qgb3IgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSBvMiBPYmplY3Qgb3IgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEByZXR1cm5zIHRydWUgaWYgYXJndW1lbnRzIGFyZSBlcXVhbC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVxdWFscyhvMTogYW55LCBvMjogYW55KTogYm9vbGVhbiB7XG4gIGlmIChvMSA9PT0gbzIpIHJldHVybiB0cnVlO1xuICBpZiAobzEgPT09IG51bGwgfHwgbzIgPT09IG51bGwpIHJldHVybiBmYWxzZTtcbiAgaWYgKG8xICE9PSBvMSAmJiBvMiAhPT0gbzIpIHJldHVybiB0cnVlOyAvLyBOYU4gPT09IE5hTlxuICBsZXQgdDEgPSB0eXBlb2YgbzEsIHQyID0gdHlwZW9mIG8yLCBsZW5ndGg6IG51bWJlciwga2V5OiBhbnksIGtleVNldDogYW55O1xuICBpZiAodDEgPT0gdDIgJiYgdDEgPT0gJ29iamVjdCcpIHtcbiAgICBpZiAoQXJyYXkuaXNBcnJheShvMSkpIHtcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShvMikpIHJldHVybiBmYWxzZTtcbiAgICAgIGlmICgobGVuZ3RoID0gbzEubGVuZ3RoKSA9PSBvMi5sZW5ndGgpIHtcbiAgICAgICAgZm9yIChrZXkgPSAwOyBrZXkgPCBsZW5ndGg7IGtleSsrKSB7XG4gICAgICAgICAgaWYgKCFlcXVhbHMobzFba2V5XSwgbzJba2V5XSkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkobzIpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGtleVNldCA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gICAgICBmb3IgKGtleSBpbiBvMSkge1xuICAgICAgICBpZiAoIWVxdWFscyhvMVtrZXldLCBvMltrZXldKSkge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBrZXlTZXRba2V5XSA9IHRydWU7XG4gICAgICB9XG4gICAgICBmb3IgKGtleSBpbiBvMikge1xuICAgICAgICBpZiAoIShrZXkgaW4ga2V5U2V0KSAmJiB0eXBlb2YgbzJba2V5XSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4vKiB0c2xpbnQ6ZW5hYmxlICovXG5cbmV4cG9ydCBmdW5jdGlvbiBpc0RlZmluZWQodmFsdWU6IGFueSk6IGJvb2xlYW4ge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSAhPT0gbnVsbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0KGl0ZW06IGFueSk6IGJvb2xlYW4ge1xuICByZXR1cm4gKGl0ZW0gJiYgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlRGVlcCh0YXJnZXQ6IGFueSwgc291cmNlOiBhbnkpOiBhbnkge1xuICBsZXQgb3V0cHV0ID0gT2JqZWN0LmFzc2lnbih7fSwgdGFyZ2V0KTtcbiAgaWYgKGlzT2JqZWN0KHRhcmdldCkgJiYgaXNPYmplY3Qoc291cmNlKSkge1xuICAgIE9iamVjdC5rZXlzKHNvdXJjZSkuZm9yRWFjaCgoa2V5OiBhbnkpID0+IHtcbiAgICAgIGlmIChpc09iamVjdChzb3VyY2Vba2V5XSkpIHtcbiAgICAgICAgaWYgKCEoa2V5IGluIHRhcmdldCkpIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKG91dHB1dCwge1trZXldOiBzb3VyY2Vba2V5XX0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG91dHB1dFtrZXldID0gbWVyZ2VEZWVwKHRhcmdldFtrZXldLCBzb3VyY2Vba2V5XSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIE9iamVjdC5hc3NpZ24ob3V0cHV0LCB7W2tleV06IHNvdXJjZVtrZXldfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIG91dHB1dDtcbn1cbiIsImltcG9ydCB7SW5qZWN0YWJsZX0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7aXNEZWZpbmVkfSBmcm9tIFwiLi91dGlsXCI7XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBUcmFuc2xhdGVQYXJzZXIge1xuICAvKipcbiAgICogSW50ZXJwb2xhdGVzIGEgc3RyaW5nIHRvIHJlcGxhY2UgcGFyYW1ldGVyc1xuICAgKiBcIlRoaXMgaXMgYSB7eyBrZXkgfX1cIiA9PT4gXCJUaGlzIGlzIGEgdmFsdWVcIiwgd2l0aCBwYXJhbXMgPSB7IGtleTogXCJ2YWx1ZVwiIH1cbiAgICogQHBhcmFtIGV4cHJcbiAgICogQHBhcmFtIHBhcmFtc1xuICAgKi9cbiAgYWJzdHJhY3QgaW50ZXJwb2xhdGUoZXhwcjogc3RyaW5nIHwgRnVuY3Rpb24sIHBhcmFtcz86IGFueSk6IHN0cmluZztcblxuICAvKipcbiAgICogR2V0cyBhIHZhbHVlIGZyb20gYW4gb2JqZWN0IGJ5IGNvbXBvc2VkIGtleVxuICAgKiBwYXJzZXIuZ2V0VmFsdWUoeyBrZXkxOiB7IGtleUE6ICd2YWx1ZUknIH19LCAna2V5MS5rZXlBJykgPT0+ICd2YWx1ZUknXG4gICAqIEBwYXJhbSB0YXJnZXRcbiAgICogQHBhcmFtIGtleVxuICAgKi9cbiAgYWJzdHJhY3QgZ2V0VmFsdWUodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nKTogYW55XG59XG5cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBUcmFuc2xhdGVEZWZhdWx0UGFyc2VyIGV4dGVuZHMgVHJhbnNsYXRlUGFyc2VyIHtcbiAgdGVtcGxhdGVNYXRjaGVyOiBSZWdFeHAgPSAve3tcXHM/KFtee31cXHNdKilcXHM/fX0vZztcblxuICBwdWJsaWMgaW50ZXJwb2xhdGUoZXhwcjogc3RyaW5nIHwgRnVuY3Rpb24sIHBhcmFtcz86IGFueSk6IHN0cmluZyB7XG4gICAgbGV0IHJlc3VsdDogc3RyaW5nO1xuXG4gICAgaWYgKHR5cGVvZiBleHByID09PSAnc3RyaW5nJykge1xuICAgICAgcmVzdWx0ID0gdGhpcy5pbnRlcnBvbGF0ZVN0cmluZyhleHByLCBwYXJhbXMpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGV4cHIgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHJlc3VsdCA9IHRoaXMuaW50ZXJwb2xhdGVGdW5jdGlvbihleHByLCBwYXJhbXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyB0aGlzIHNob3VsZCBub3QgaGFwcGVuLCBidXQgYW4gdW5yZWxhdGVkIFRyYW5zbGF0ZVNlcnZpY2UgdGVzdCBkZXBlbmRzIG9uIGl0XG4gICAgICByZXN1bHQgPSBleHByIGFzIHN0cmluZztcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZ2V0VmFsdWUodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nKTogYW55IHtcbiAgICBsZXQga2V5cyA9IGtleS5zcGxpdCgnLicpO1xuICAgIGtleSA9ICcnO1xuICAgIGRvIHtcbiAgICAgIGtleSArPSBrZXlzLnNoaWZ0KCk7XG4gICAgICBpZiAoaXNEZWZpbmVkKHRhcmdldCkgJiYgaXNEZWZpbmVkKHRhcmdldFtrZXldKSAmJiAodHlwZW9mIHRhcmdldFtrZXldID0