UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

394 lines 55.6 kB
import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { camelCase, flatMap } from 'lodash-es'; import { BehaviorSubject, Subject, combineLatest, fromEvent } from 'rxjs'; import { delay, filter, map, take } from 'rxjs/operators'; import { CookieBannerService } from '../bootstrap/cookie-banner/cookie-banner.service'; import { OptionsService } from '../common/options.service'; import { AppStateService } from '../common/ui-state.service'; import { UserPreferencesService } from '../common/user-preferences/user-preferences.service'; import * as i0 from "@angular/core"; import * as i1 from "../common/ui-state.service"; import * as i2 from "../common/options.service"; import * as i3 from "../bootstrap/cookie-banner/cookie-banner.service"; import * as i4 from "../common/user-preferences/user-preferences.service"; import * as i5 from "@ngx-translate/core"; /** * A service to manage the Gainsight integration. It allows to load the * tag and */ export class GainsightService { constructor(appState, options, cookieBannerService, userPreferencesService, translateService) { this.appState = appState; this.options = options; this.cookieBannerService = cookieBannerService; this.userPreferencesService = userPreferencesService; this.translateService = translateService; /** * A subject that emits the tag function as soon as a new tag is set. */ this.tagFunction$ = new BehaviorSubject(null); this.trackingLoaded$ = new Subject(); /** * Gainsight is activated only when the cookie banner is present. If functional cookies are enabled, both personally identifiable information (PII) and required data are sent. * Otherwise, only the required data is transmitted during the identity step execution. */ this.USER_PREFERENCES_GAINSIGHT_KEY = 'gainsightEnabled'; /** * The name of the key remained unchanged, but applies to all engagements. */ this.USER_PREFERENCES_GAINSIGHT_ENGAGEMENTS_KEY = 'gainsightBotEnabled'; this.HIDE_GAINSIGHT_BOT_STYLE_ID = 'hide-gs-bot'; this.GAINSIGHT_URL = 'web-sdk.aptrinsic.com/api/aptrinsic.js?a='; this.GAINSIGHT_GLOBAL_SCOPE = 'aptrinsic'; this.SCRIPT_EXECUTION_WAIT_TIME = 500; this.OPTIONS_KEY_CATEGORY = 'gainsight'; this.OPTIONS_KEY_NAME = 'api.key'; this.isScriptLoaded = false; } /** * Checks if the specified Gainsight preference is disabled in user preferences. * @param preferenceName - Name of the Gainsight preference. * @returns A promise that resolves to `true` if the preference is disabled, otherwise `false`. */ async isGainsightPreferenceDisabledInUserPreferences(preferenceName) { const userGainsightPref = await this.userPreferencesService.get(preferenceName).toPromise(); return userGainsightPref === false; } /** * Sets the state of the functional cookie. * @param value - A boolean value to indicate whether the functional cookie should be enabled (`true`) or disabled (`false`). */ setFunctionalCookie(value) { const cookies = this.cookieBannerService.getUserCookiePreferences(); if (cookies) { Object.keys(cookies).forEach(cookieName => { if (cookieName === 'functional') { cookies[cookieName] = value; return; } }); localStorage.setItem('acceptCookieNotice', JSON.stringify(cookies)); } } async getGainsightKey() { this.gainsightKey = this.options.gainsightKey || (await this.options.getSystemOption(this.OPTIONS_KEY_CATEGORY, this.OPTIONS_KEY_NAME)); return this.gainsightKey; } /** * Returns the tag global function which can be used to identify user * or add special events. */ get tagFunction() { return window[this.GAINSIGHT_GLOBAL_SCOPE]; } /** * Load the script tag and calls the identify function to start the tracking. * @param currentTenant The current tenant. * @param sendPiiData Flag for sending personally identifiable information (PII) during identification in Gainsight. */ async loadTag(currentTenant, sendPiiData) { const scriptTag = document.createElement('script'); const key = await this.getGainsightKey(); if (key && !this.isScriptLoaded) { this.loadScriptTag(scriptTag, key); const currentUserStream = this.appState.currentUser; const scriptLoadStream = fromEvent(scriptTag, 'load'); const versionStream = this.appState.state$.pipe(filter(({ versions }) => versions.backend), map(({ versions }) => versions), take(1)); const sourceStreams = sendPiiData ? [currentUserStream, scriptLoadStream, versionStream] : [currentUserStream, scriptLoadStream]; combineLatest(sourceStreams) .pipe(delay(this.SCRIPT_EXECUTION_WAIT_TIME), filter(([user, scriptEvent]) => !!(scriptEvent && user))) .subscribe(args => { const [user, , versions] = args; this.setGlobalContext(); const instanceId = this.getInstanceIdFromUrl(); if (sendPiiData) { const versionUI = versions.ui.ngx; const versionBE = versions.backend; const extendedIdentifyData = { user, currentTenant, instanceId, versionUI, versionBE }; this.identify(sendPiiData, extendedIdentifyData); } else { const requiredIdentifyData = { user, currentTenant, instanceId }; this.identify(sendPiiData, requiredIdentifyData); } this.isScriptLoaded = true; this.tagFunction$.next(this.tagFunction); this.trackingLoaded$.next(true); }); } } /** * Identifies the user/account at Gainsight. * @param sendPiiData Flag for sending personally identifiable information. * @param identifyData Object containing identification data. */ identify(sendPiiData, identifyData) { const windowRef = window; const { id: userId, email, roles } = identifyData.user; const { name: tenantID, customProperties, domainName } = identifyData.currentTenant; const { instanceId, versionUI, versionBE } = identifyData; /** * Passing ID is a minimum required data to make an identify call to Gainsight. * isUserCreatedAfterAnonymizationWasActivated parameter is passed to later distinguish between users created before and after data anonymization done by Gainsight. * tenantID Used to distinguish between tenants when same email is used for different tenants. * * Due to GS limitations (GS does not allow clearing user attr/preferences via the GS tag!), * we always need to initialize fields related to PII to prevent leaking this data to GS when the user has disabled functional cookies. */ const requiredIdentify = { /** * Email was not mandatory form field until 10.14 */ id: email ? email : `${userId}_${tenantID}_${instanceId}`, isUserCreatedAfterAnonymizationWasActivated: true, tenantID: tenantID, email: '--', userName: '--', firstName: '--', lastName: '--', domainName, versionUI, versionBE, userLanguage: this.translateService.currentLang, browserLanguage: this.translateService.getBrowserLang(), instanceId, externalReference: customProperties?.externalReference, userRoles: this.transformUserRolesToStr(roles?.references), customBranding: this.isCustomBranding(), fullTracking: sendPiiData }; if (sendPiiData) { const { userName, firstName, lastName } = identifyData.user; const extendedIdentify = { ...requiredIdentify, email, userName, firstName, lastName }; windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', extendedIdentify, { id: `${tenantID}_${instanceId}`, instanceId }); return; } windowRef[this.GAINSIGHT_GLOBAL_SCOPE]('identify', requiredIdentify); } /** * Triggers an event to be recorded by Gainsight PX. * This method calls the Gainsight PX's tracking mechanism to log a specific event * along with its associated properties. * @param eventName - Name of the event to be triggered. * @param props - Optional properties associated with the event. */ triggerEvent(eventName, props) { if (this.tagFunction && eventName) { eventName = this.prepareEventName(eventName); this.tagFunction('track', eventName, props); } } translateToEnglish(textToTranslate) { const { currentLang } = this.translateService; if (currentLang === 'en') { return textToTranslate; } if (currentLang && currentLang !== this.cachedLanguage) { this.cachedRevertedTranslations = undefined; } if (!this.cachedRevertedTranslations) { this.cachedLanguage = currentLang; this.cachedRevertedTranslations = this.getRevertedTranslations(currentLang); } return this.getEnTranslation(textToTranslate, this.cachedRevertedTranslations); } /** * Determines whether personally identifiable information (PII) should be sent while loading a tag. * The decision to activate Gainsight and send PII relies on whether the cookiePreferences option is defined in the application settings, * if the functional cookie is enabled, and if the user grants permission. */ async shouldSendPiiData() { return (this.cookieBannerService.isConfigCookiePreferencesDefined() && this.cookieBannerService.isFunctionalCookieEnabled() && !(await this.isGainsightPreferenceDisabledInUserPreferences(this.USER_PREFERENCES_GAINSIGHT_KEY))); } /** * Updates a specific user attribute in the Gainsight global scope. * This method interfaces with the Gainsight global object to set a user's specific attribute with a provided value. * @param name - Name of the user attribute to be updated. * @param value - Value to set for the specified user attribute. */ updateUserAttribute(name, value) { window[this.GAINSIGHT_GLOBAL_SCOPE]?.('set', 'user', { [name]: value }); } /** * Determines if the current user has the capability to modify Gainsight PX settings. * * This method checks multiple conditions: * 1. Whether tracking has been disabled globally via application options. * 2. Whether Gainsight is disabled at the tenant level through custom properties. * 3. Whether a Gainsight key is available, either currently loaded or fetched asynchronously. * 4. Whether cookie preferences are defined and available for the user. * * @returns Promise that resolves to a boolean. True indicates the user can edit product experience settings, and false otherwise. */ async canEditProductExperienceSettings() { const currentTenant = this.appState.currentTenant.value; const { customProperties } = currentTenant; if (this.isTrackingDisabled() || this.isGainsightDisabledAtTenantCustomProperties(customProperties)) { return false; } const gainsightKey = !!this.gainsightKey || !!(await this.getGainsightKey()); return (gainsightKey && this.cookieBannerService.isConfigCookiePreferencesDefined() && !!this.cookieBannerService.getUserCookiePreferences()); } async hashGroupName(groupName) { if (!groupName) { return; } // Convert the group name to an ArrayBuffer const encoder = new TextEncoder(); const data = encoder.encode(groupName); // Use the SHA-256 algorithm to hash the data const hashBuffer = await crypto.subtle.digest('SHA-256', data); // Convert the hash to a hexadecimal string const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashedName = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join(''); return hashedName; } /** * Sets the global context for Gainsight with the current application name. * The global context can be utilized by Gainsight for various purposes, such as segmenting users. */ setGlobalContext() { const currentAppState = this.appState.state$.value; const currentAppName = currentAppState.app.name; window[this.GAINSIGHT_GLOBAL_SCOPE]?.('set', 'globalContext', { projectName: currentAppName }); } transformUserRolesToStr(userRoles) { if (!userRoles) { return ''; } return flatMap(userRoles, (userRole) => userRole.role.name).join(); } /** * Checks if Gainsight is disabled based on tenant custom properties. * * @param customProperties - The custom properties of the tenant. * @returns {boolean} - True if Gainsight is disabled, false otherwise. */ isGainsightDisabledAtTenantCustomProperties(customProperties) { const gainsightEnabled = customProperties && customProperties.gainsightEnabled; return gainsightEnabled === false; } /** * Determines if custom branding is enabled based on the presence of a brand logo. * * @returns {boolean} - True if custom branding is applied, false otherwise. */ isCustomBranding() { const brandingCssVars = this.options.get('brandingCssVars') || {}; return !!brandingCssVars['brand-logo-img']; } /** * Determines if tracking is disabled based on the application options. * @returns `true` if tracking is disabled, otherwise `false`. */ isTrackingDisabled() { return this.options.disableTracking === true; } prepareEventName(baseEventName) { return baseEventName .split(':') .map(eventNamePart => camelCase(removeTranslationContext(eventNamePart))) .join(':'); function removeTranslationContext(eventNamePart) { return eventNamePart.replace(/`[\w\W]*`/g, ''); } } loadScriptTag(scriptTag, key) { try { const windowRef = window; const firstTag = document.getElementsByTagName('script')[0]; const protocol = location.protocol; const gainsightGlobalScope = this.GAINSIGHT_GLOBAL_SCOPE; scriptTag.src = `${protocol}//${this.GAINSIGHT_URL}${key}`; (windowRef[this.GAINSIGHT_GLOBAL_SCOPE] = windowRef[this.GAINSIGHT_GLOBAL_SCOPE] || // tslint:disable-next-line:only-arrow-functions function (...args) { (windowRef[gainsightGlobalScope].q = windowRef[gainsightGlobalScope].q || []).push(args); }), (windowRef[gainsightGlobalScope].p = key); scriptTag.async = true; firstTag.parentNode.insertBefore(scriptTag, firstTag); } catch (ex) { console.warn('Failed to load Gainsight PX', ex); } } getInstanceIdFromUrl() { const hostName = location.hostname; return hostName.substring(hostName.indexOf('.') + 1); } /** * Reverses the translation object. * * **Example** * { Add widget: "Widget hinzufügen" } * * will be changed to: * * { Widget hinzufügen: "Add widget" } * * @param currentLang Language whose translated values are to be placed in the object key. * @returns Returns an inverted object where the keys have been swapped with the values. */ getRevertedTranslations(currentLang) { const translations = this.translateService.store.translations[currentLang]; if (!translations) { return {}; } const swappedKeysWithValues = {}; Object.keys(translations).forEach(key => { swappedKeysWithValues[translations[key]] = key; }); return swappedKeysWithValues; } /**Translates string back into English. * If the current application language is set to English, the string passed as an argument is returned. * @param textToTranslate string to translate. * @returns Returns the string translated into English. */ getEnTranslation(textToTranslate, translations) { let enTranslation = translations[textToTranslate]; if (!enTranslation) { return textToTranslate; } /** remove translation context */ const regex = /\`(.*?)\`/; enTranslation = enTranslation.replace(regex, ''); return enTranslation; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, deps: [{ token: i1.AppStateService }, { token: i2.OptionsService }, { token: i3.CookieBannerService }, { token: i4.UserPreferencesService }, { token: i5.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GainsightService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.AppStateService }, { type: i2.OptionsService }, { type: i3.CookieBannerService }, { type: i4.UserPreferencesService }, { type: i5.TranslateService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FpbnNpZ2h0LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9jb3JlL3Byb2R1Y3QtZXhwZXJpZW5jZS9nYWluc2lnaHQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTNDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDMUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDN0QsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0scURBQXFELENBQUM7Ozs7Ozs7QUF1QjdGOzs7R0FHRztBQUlILE1BQU0sT0FBTyxnQkFBZ0I7SUEyQjNCLFlBQ1UsUUFBeUIsRUFDekIsT0FBdUIsRUFDdkIsbUJBQXdDLEVBQ3hDLHNCQUE4QyxFQUM5QyxnQkFBa0M7UUFKbEMsYUFBUSxHQUFSLFFBQVEsQ0FBaUI7UUFDekIsWUFBTyxHQUFQLE9BQU8sQ0FBZ0I7UUFDdkIsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFxQjtRQUN4QywyQkFBc0IsR0FBdEIsc0JBQXNCLENBQXdCO1FBQzlDLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUEvQjVDOztXQUVHO1FBQ0gsaUJBQVksR0FBRyxJ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Q0FBQztZQUNuQixPQUFPLGVBQWUsQ0FBQztRQUN6QixDQUFDO1FBQ0QsaUNBQWlDO1FBQ2pDLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQztRQUMxQixhQUFhLEdBQUcsYUFBYSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakQsT0FBTyxhQUFhLENBQUM7SUFDdkIsQ0FBQzsrR0E5YVUsZ0JBQWdCO21IQUFoQixnQkFBZ0IsY0FGZixNQUFNOzs0RkFFUCxnQkFBZ0I7a0JBSDVCLFVBQVU7bUJBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSUN1cnJlbnRUZW5hbnQsIElDdXN0b21Qcm9wZXJ0aWVzLCBJSWRlbnRpZmllZCwgSVVzZXIgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBjYW1lbENhc2UsIGZsYXRNYXAgfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJqZWN0LCBjb21iaW5lTGF0ZXN0LCBmcm9tRXZlbnQgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGRlbGF5LCBmaWx0ZXIsIG1hcCwgdGFrZSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IENvb2tpZUJhbm5lclNlcnZpY2UgfSBmcm9tICcuLi9ib290c3RyYXAvY29va2llLWJhbm5lci9jb29raWUtYmFubmVyLnNlcnZpY2UnO1xuaW1wb3J0IHsgT3B0aW9uc1NlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vb3B0aW9ucy5zZXJ2aWNlJztcbmltcG9ydCB7IEFwcFN0YXRlU2VydmljZSB9IGZyb20gJy4uL2NvbW1vbi91aS1zdGF0ZS5zZXJ2aWNlJztcbmltcG9ydCB7IFVzZXJQcmVmZXJlbmNlc1NlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vdXNlci1wcmVmZXJlbmNlcy91c2VyLXByZWZlcmVuY2VzLnNlcnZpY2UnO1xuaW1wb3J0IHsgUHhFdmVudERhdGEgfSBmcm9tICcuL3Byb2R1Y3QtZXhwZXJpZW5jZS5tb2RlbCc7XG5cbmludGVyZmFjZSBVc2VyUm9sZSB7XG4gIGlkOiBzdHJpbmc7XG4gIG5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBAcHJvcGVydHkgdXNlciBUaGUgdXNlciB3aGljaCBpcyBnaXZlbiB0byBHYWluc2lnaHQuXG4gKiBAcHJvcGVydHkgdGVuYW50IFRoZSB0ZW5hbnQgd2hpY2ggaXMgZ2l2ZW4gdG8gR2FpbnNpZ2h0LlxuICogQHByb3BlcnR5IGluc3RhbmNlSWQgRXh0cmFjdGVkIGRvbWFpbiBwYXJ0IG9mIGFuIFVSTC5cbiAqIEBwcm9wZXJ0eSB2ZXJzaW9uVUkgVGhlIFVJIHZlcnNpb24gdXNlZC5cbiAqIEBwcm9wZXJ0eSB2ZXJzaW9uQkUgVGhlIEJFIHZlcnNpb24gdXNlZC5cbiAqL1xuaW50ZXJmYWNlIElkZW50aWZ5RGF0YSB7XG4gIHVzZXI6IElVc2VyO1xuICBjdXJyZW50VGVuYW50OiBJQ3VycmVudFRlbmFudDtcbiAgaW5zdGFuY2VJZDogc3RyaW5nO1xuICB2ZXJzaW9uVUk/OiBzdHJpbmc7XG4gIHZlcnNpb25CRT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBBIHNlcnZpY2UgdG8gbWFuYWdlIHRoZSBHYWluc2lnaHQgaW50ZWdyYXRpb24uIEl0IGFsbG93cyB0byBsb2FkIHRoZVxuICogdGFnIGFuZFxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBHYWluc2lnaHRTZXJ2aWNlIHtcbiAgLyoqXG4gICAqIEEgc3ViamVjdCB0aGF0IGVtaXRzIHRoZSB0YWcgZnVuY3Rpb24gYXMgc29vbiBhcyBhIG5ldyB0YWcgaXMgc2V0LlxuICAgKi9cbiAgdGFnRnVuY3Rpb24kID0gbmV3IEJlaGF2aW9yU3ViamVjdChudWxsKTtcbiAgdHJhY2tpbmdMb2FkZWQkOiBTdWJqZWN0PGJvb2xlYW4+ID0gbmV3IFN1YmplY3QoKTtcblxuICAvKipcbiAgICogR2FpbnNpZ2h0IGlzIGFjdGl2YXRlZCBvbmx5IHdoZW4gdGhlIGNvb2tpZSBiYW5uZXIgaXMgcHJlc2VudC4gSWYgZnVuY3Rpb25hbCBjb29raWVzIGFyZSBlbmFibGVkLCBib3RoIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uIChQSUkpIGFuZCByZXF1aXJlZCBkYXRhIGFyZSBzZW50LlxuICAgKiBPdGhlcndpc2UsIG9ubHkgdGhlIHJlcXVpcmVkIGRhdGEgaXMgdHJhbnNtaXR0ZWQgZHVyaW5nIHRoZSBpZGVudGl0eSBzdGVwIGV4ZWN1dGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IFVTRVJfUFJFRkVSRU5DRVNfR0FJTlNJR0hUX0tFWSA9ICdnYWluc2lnaHRFbmFibGVkJztcbiAgLyoqXG4gICAqIFRoZSBuYW1lIG9mIHRoZSBrZXkgcmVtYWluZWQgdW5jaGFuZ2VkLCBidXQgYXBwbGllcyB0byBhbGwgZW5nYWdlbWVudHMuXG4gICAqL1xuICByZWFkb25seSBVU0VSX1BSRUZFUkVOQ0VTX0dBSU5TSUdIVF9FTkdBR0VNRU5UU19LRVkgPSAnZ2FpbnNpZ2h0Qm90RW5hYmxlZCc7XG4gIHJlYWRvbmx5IEhJREVfR0FJTlNJR0hUX0JPVF9TVFlMRV9JRCA9ICdoaWRlLWdzLWJvdCc7XG4gIHByaXZhdGUgcmVhZG9ubHkgR0FJTlNJR0hUX1VSTCA9ICd3ZWItc2RrLmFwdHJpbnNpYy5jb20vYXBpL2FwdHJpbnNpYy5qcz9hPSc7XG4gIHByaXZhdGUgcmVhZG9ubHkgR0FJTlNJR0hUX0dMT0JBTF9TQ09QRSA9ICdhcHRyaW5zaWMnO1xuICBwcml2YXRlIHJlYWRvbmx5IFNDUklQVF9FWEVDVVRJT05fV0FJVF9USU1FID0gNTAwO1xuICBwcml2YXRlIHJlYWRvbmx5IE9QVElPTlNfS0VZX0NBVEVHT1JZID0gJ2dhaW5zaWdodCc7XG4gIHByaXZhdGUgcmVhZG9ubHkgT1BUSU9OU19LRVlfTkFNRSA9ICdhcGkua2V5JztcbiAgcHJpdmF0ZSBpc1NjcmlwdExvYWRlZCA9IGZhbHNlO1xuICBwcml2YXRlIGdhaW5zaWdodEtleTogc3RyaW5nO1xuICBwcml2YXRlIGNhY2hlZFJldmVydGVkVHJhbnNsYXRpb25zOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9O1xuICBwcml2YXRlIGNhY2hlZExhbmd1YWdlOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBhcHBTdGF0ZTogQXBwU3RhdGVTZXJ2aWNlLFxuICAgIHByaXZhdGUgb3B0aW9uczogT3B0aW9uc1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBjb29raWVCYW5uZXJTZXJ2aWNlOiBDb29raWVCYW5uZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgdXNlclByZWZlcmVuY2VzU2VydmljZTogVXNlclByZWZlcmVuY2VzU2VydmljZSxcbiAgICBwcml2YXRlIHRyYW5zbGF0ZVNlcnZpY2U6IFRyYW5zbGF0ZVNlcnZpY2VcbiAgKSB7fVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIHNwZWNpZmllZCBHYWluc2lnaHQgcHJlZmVyZW5jZSBpcyBkaXNhYmxlZCBpbiB1c2VyIHByZWZlcmVuY2VzLlxuICAgKiBAcGFyYW0gcHJlZmVyZW5jZU5hbWUgLSBOYW1lIG9mIHRoZSBHYWluc2lnaHQgcHJlZmVyZW5jZS5cbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYHRydWVgIGlmIHRoZSBwcmVmZXJlbmNlIGlzIGRpc2FibGVkLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICovXG4gIGFzeW5jIGlzR2FpbnNpZ2h0UHJlZmVyZW5jZURpc2FibGVkSW5Vc2VyUHJlZmVyZW5jZXMocHJlZmVyZW5jZU5hbWU6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IHVzZXJHYWluc2lnaHRQcmVmID0gYXdhaXQgdGhpcy51c2VyUHJlZmVyZW5jZXNTZXJ2aWNlLmdldChwcmVmZXJlbmNlTmFtZSkudG9Qcm9taXNlKCk7XG5cbiAgICByZXR1cm4gdXNlckdhaW5zaWdodFByZWYgPT09IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIHN0YXRlIG9mIHRoZSBmdW5jdGlvbmFsIGNvb2tpZS5cbiAgICogQHBhcmFtIHZhbHVlIC0gQSBib29sZWFuIHZhbHVlIHRvIGluZGljYXRlIHdoZXRoZXIgdGhlIGZ1bmN0aW9uYWwgY29va2llIHNob3VsZCBiZSBlbmFibGVkIChgdHJ1ZWApIG9yIGRpc2FibGVkIChgZmFsc2VgKS5cbiAgICovXG4gIHNldEZ1bmN0aW9uYWxDb29raWUodmFsdWU6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBjb29raWVzID0gdGhpcy5jb29raWVCYW5uZXJTZXJ2aWNlLmdldFVzZXJDb29raWVQcmVmZXJlbmNlcygpO1xuICAgIGlmIChjb29raWVzKSB7XG4gICAgICBPYmplY3Qua2V5cyhjb29raWVzKS5mb3JFYWNoKGNvb2tpZU5hbWUgPT4ge1xuICAgICAgICBpZiAoY29va2llTmFtZSA9PT0gJ2Z1bmN0aW9uYWwnKSB7XG4gICAgICAgICAgY29va2llc1tjb29raWVOYW1lXSA9IHZhbHVlO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSgnYWNjZXB0Q29va2llTm90aWNlJywgSlNPTi5zdHJpbmdpZnkoY29va2llcykpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGdldEdhaW5zaWdodEtleSgpIHtcbiAgICB0aGlzLmdhaW5zaWdodEtleSA9XG4gICAgICB0aGlzLm9wdGlvbnMuZ2FpbnNpZ2h0S2V5IHx8XG4gICAgICAoYXdhaXQgdGhpcy5vcHRpb25zLmdldFN5c3RlbU9wdGlvbih0aGlzLk9QVElPTlNfS0VZX0NBVEVHT1JZLCB0aGlzLk9QVElPTlNfS0VZX05BTUUpKTtcbiAgICByZXR1cm4gdGhpcy5nYWluc2lnaHRLZXk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGFnIGdsb2JhbCBmdW5jdGlvbiB3aGljaCBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSB1c2VyXG4gICAqIG9yIGFkZCBzcGVjaWFsIGV2ZW50cy5cbiAgICovXG4gIGdldCB0YWdGdW5jdGlvbigpIHtcbiAgICByZXR1cm4gKHdpbmRvdyBhcyBhbnkpW3RoaXMuR0FJTlNJR0hUX0dMT0JBTF9TQ09QRV07XG4gIH1cblxuICAvKipcbiAgICogTG9hZCB0aGUgc2NyaXB0IHRhZyBhbmQgY2FsbHMgdGhlIGlkZW50aWZ5IGZ1bmN0aW9uIHRvIHN0YXJ0IHRoZSB0cmFja2luZy5cbiAgICogQHBhcmFtIGN1cnJlbnRUZW5hbnQgVGhlIGN1cnJlbnQgdGVuYW50LlxuICAgKiBAcGFyYW0gc2VuZFBpaURhdGEgRmxhZyBmb3Igc2VuZGluZyBwZXJzb25hbGx5IGlkZW50aWZpYWJsZSBpbmZvcm1hdGlvbiAoUElJKSBkdXJpbmcgaWRlbnRpZmljYXRpb24gaW4gR2FpbnNpZ2h0LlxuICAgKi9cbiAgYXN5bmMgbG9hZFRhZyhjdXJyZW50VGVuYW50OiBJQ3VycmVudFRlbmFudCwgc2VuZFBpaURhdGE6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBzY3JpcHRUYWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTtcbiAgICBjb25zdCBrZXkgPSBhd2FpdCB0aGlzLmdldEdhaW5zaWdodEtleSgpO1xuXG4gICAgaWYgKGtleSAmJiAhdGhpcy5pc1NjcmlwdExvYWRlZCkge1xuICAgICAgdGhpcy5sb2FkU2NyaXB0VGFnKHNjcmlwdFRhZywga2V5KTtcblxuICAgICAgY29uc3QgY3VycmVudFVzZXJTdHJlYW0gPSB0aGlzLmFwcFN0YXRlLmN1cnJlbnRVc2VyO1xuICAgICAgY29uc3Qgc2NyaXB0TG9hZFN0cmVhbSA9IGZyb21FdmVudChzY3JpcHRUYWcsICdsb2FkJyk7XG4gICAgICBjb25zdCB2ZXJzaW9uU3RyZWFtID0gdGhpcy5hcHBTdGF0ZS5zdGF0ZSQucGlwZShcbiAgICAgICAgZmlsdGVyKCh7IHZlcnNpb25zIH0pID0+IHZlcnNpb25zLmJhY2tlbmQpLFxuICAgICAgICBtYXAoKHsgdmVyc2lvbnMgfSkgPT4gdmVyc2lvbnMpLFxuICAgICAgICB0YWtlKDEpXG4gICAgICApO1xuXG4gICAgICBjb25zdCBzb3VyY2VTdHJlYW1zID0gc2VuZFBpaURhdGFcbiAgICAgICAgPyBbY3VycmVudFVzZXJTdHJlYW0sIHNjcmlwdExvYWRTdHJlYW0sIHZlcnNpb25TdHJlYW1dXG4gICAgICAgIDogW2N1cnJlbnRVc2VyU3RyZWFtLCBzY3JpcHRMb2FkU3RyZWFtXTtcblxuICAgICAgY29tYmluZUxhdGVzdChzb3VyY2VTdHJlYW1zKVxuICAgICAgICAucGlwZShcbiAgICAgICAgICBkZWxheSh0aGlzLlNDUklQVF9FWEVDVVRJT05fV0FJVF9USU1FKSxcbiAgICAgICAgICBmaWx0ZXIoKFt1c2VyLCBzY3JpcHRFdmVudF0pID0+ICEhKHNjcmlwdEV2ZW50ICYmIHVzZXIpKVxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoYXJncyA9PiB7XG4gICAgICAgICAgY29uc3QgW3VzZXIsICwgdmVyc2lvbnNdID0gYXJncztcbiAgICAgICAgICB0aGlzLnNldEdsb2JhbENvbnRleHQoKTtcblxuICAgICAgICAgIGNvbnN0IGluc3RhbmNlSWQgPSB0aGlzLmdldEluc3RhbmNlSWRGcm9tVXJsKCk7XG4gICAgICAgICAgaWYgKHNlbmRQaWlEYXRhKSB7XG4gICAgICAgICAgICBjb25zdCB2ZXJzaW9uVUk6IHN0cmluZyA9IHZlcnNpb25zLnVpLm5neDtcbiAgICAgICAgICAgIGNvbnN0IHZlcnNpb25CRTogc3RyaW5nID0gdmVyc2lvbnMuYmFja2VuZDtcbiAgICAgICAgICAgIGNvbnN0IGV4dGVuZGVkSWRlbnRpZnlEYXRhOiBJZGVudGlmeURhdGEgPSB7XG4gICAgICAgICAgICAgIHVzZXIsXG4gICAgICAgICAgICAgIGN1cnJlbnRUZW5hbnQsXG4gICAgICAgICAgICAgIGluc3RhbmNlSWQsXG4gICAgICAgICAgICAgIHZlcnNpb25VSSxcbiAgICAgICAgICAgICAgdmVyc2lvbkJFXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5pZGVudGlmeShzZW5kUGlpRGF0YSwgZXh0ZW5kZWRJZGVudGlmeURhdGEpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCByZXF1aXJlZElkZW50aWZ5RGF0YTogSWRlbnRpZnlEYXRhID0geyB1c2VyLCBjdXJyZW50VGVuYW50LCBpbnN0YW5jZUlkIH07XG4gICAgICAgICAgICB0aGlzLmlkZW50aWZ5KHNlbmRQaWlEYXRhLCByZXF1aXJlZElkZW50aWZ5RGF0YSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMuaXNTY3JpcHRMb2FkZWQgPSB0cnVlO1xuICAgICAgICAgIHRoaXMudGFnRnVuY3Rpb24kLm5leHQodGhpcy50YWdGdW5jdGlvbik7XG4gICAgICAgICAgdGhpcy50cmFja2luZ0xvYWRlZCQubmV4dCh0cnVlKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIElkZW50aWZpZXMgdGhlIHVzZXIvYWNjb3VudCBhdCBHYWluc2lnaHQuXG4gICAqIEBwYXJhbSBzZW5kUGlpRGF0YSBGbGFnIGZvciBzZW5kaW5nIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uLlxuICAgKiBAcGFyYW0gaWRlbnRpZnlEYXRhIE9iamVjdCBjb250YWluaW5nIGlkZW50aWZpY2F0aW9uIGRhdGEuXG4gICAqL1xuICBpZGVudGlmeShzZW5kUGlpRGF0YTogYm9vbGVhbiwgaWRlbnRpZnlEYXRhOiBJZGVudGlmeURhdGEpIHtcbiAgICBjb25zdCB3aW5kb3dSZWYgPSB3aW5kb3cgYXMgYW55O1xuICAgIGNvbnN0IHsgaWQ6IHVzZXJJZCwgZW1haWwsIHJvbGVzIH0gPSBpZGVudGlmeURhdGEudXNlcjtcbiAgICBjb25zdCB7IG5hbWU6IHRlbmFudElELCBjdXN0b21Qcm9wZXJ0aWVzLCBkb21haW5OYW1lIH0gPSBpZGVudGlmeURhdGEuY3VycmVudFRlbmFudDtcbiAgICBjb25zdCB7IGluc3RhbmNlSWQsIHZlcnNpb25VSSwgdmVyc2lvbkJFIH0gPSBpZGVudGlmeURhdGE7XG5cbiAgICAvKipcbiAgICAgKiBQYXNzaW5nIElEIGlzIGEgbWluaW11bSByZXF1aXJlZCBkYXRhIHRvIG1ha2UgYW4gaWRlbnRpZnkgY2FsbCB0byBHYWluc2lnaHQuXG4gICAgICogaXNVc2VyQ3JlYXRlZEFmdGVyQW5vbnltaXphdGlvbldhc0FjdGl2YXRlZCBwYXJhbWV0ZXIgaXMgcGFzc2VkIHRvIGxhdGVyIGRpc3Rpbmd1aXNoIGJldHdlZW4gdXNlcnMgY3JlYXRlZCBiZWZvcmUgYW5kIGFmdGVyIGRhdGEgYW5vbnltaXphdGlvbiBkb25lIGJ5IEdhaW5zaWdodC5cbiAgICAgKiB0ZW5hbnRJRCBVc2VkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdGVuYW50cyB3aGVuIHNhbWUgZW1haWwgaXMgdXNlZCBmb3IgZGlmZmVyZW50IHRlbmFudHMuXG4gICAgICpcbiAgICAgKiBEdWUgdG8gR1MgbGltaXRhdGlvbnMgKEdTIGRvZXMgbm90IGFsbG93IGNsZWFyaW5nIHVzZXIgYXR0ci9wcmVmZXJlbmNlcyB2aWEgdGhlIEdTIHRhZyEpLFxuICAgICAqIHdlIGFsd2F5cyBuZWVkIHRvIGluaXRpYWxpemUgZmllbGRzIHJlbGF0ZWQgdG8gUElJIHRvIHByZXZlbnQgbGVha2luZyB0aGlzIGRhdGEgdG8gR1Mgd2hlbiB0aGUgdXNlciBoYXMgZGlzYWJsZWQgZnVuY3Rpb25hbCBjb29raWVzLlxuICAgICAqL1xuICAgIGNvbnN0IHJlcXVpcmVkSWRlbnRpZnkgPSB7XG4gICAgICAvKipcbiAgICAgICAqIEVtYWlsIHdhcyBub3QgbWFuZGF0b3J5IGZvcm0gZmllbGQgdW50aWwgMTAuMTRcbiAgICAgICAqL1xuICAgICAgaWQ6IGVtYWlsID8gZW1haWwgOiBgJHt1c2VySWR9XyR7dGVuYW50SUR9XyR7aW5zdGFuY2VJZH1gLFxuICAgICAgaXNVc2VyQ3JlYXRlZEFmdGVyQW5vbnltaXphdGlvbldhc0FjdGl2YXRlZDogdHJ1ZSxcbiAgICAgIHRlbmFudElEOiB0ZW5hbnRJRCxcbiAgICAgIGVtYWlsOiAnLS0nLFxuICAgICAgdXNlck5hbWU6ICctLScsXG4gICAgICBmaXJzdE5hbWU6ICctLScsXG4gICAgICBsYXN0TmFtZTogJy0tJyxcbiAgICAgIGRvbWFpbk5hbWUsXG4gICAgICB2ZXJzaW9uVUksXG4gICAgICB2ZXJzaW9uQkUsXG4gICAgICB1c2VyTGFuZ3VhZ2U6IHRoaXMudHJhbnNsYXRlU2VydmljZS5jdXJyZW50TGFuZyxcbiAgICAgIGJyb3dzZXJMYW5ndWFnZTogdGhpcy50cmFuc2xhdGVTZXJ2aWNlLmdldEJyb3dzZXJMYW5nKCksXG4gICAgICBpbnN0YW5jZUlkLFxuICAgICAgZXh0ZXJuYWxSZWZlcmVuY2U6IGN1c3RvbVByb3BlcnRpZXM/LmV4dGVybmFsUmVmZXJlbmNlLFxuICAgICAgdXNlclJvbGVzOiB0aGlzLnRyYW5zZm9ybVVzZXJSb2xlc1RvU3RyKHJvbGVzPy5yZWZlcmVuY2VzKSxcbiAgICAgIGN1c3RvbUJyYW5kaW5nOiB0aGlzLmlzQ3VzdG9tQnJhbmRpbmcoKSxcbiAgICAgIGZ1bGxUcmFja2luZzogc2VuZFBpaURhdGFcbiAgICB9O1xuXG4gICAgaWYgKHNlbmRQaWlEYXRhKSB7XG4gICAgICBjb25zdCB7IHVzZXJOYW1lLCBmaXJzdE5hbWUsIGxhc3ROYW1lIH0gPSBpZGVudGlmeURhdGEudXNlcjtcblxuICAgICAgY29uc3QgZXh0ZW5kZWRJZGVudGlmeSA9IHtcbiAgICAgICAgLi4ucmVxdWlyZWRJZGVudGlmeSxcbiAgICAgICAgZW1haWwsXG4gICAgICAgIHVzZXJOYW1lLFxuICAgICAgICBmaXJzdE5hbWUsXG4gICAgICAgIGxhc3ROYW1lXG4gICAgICB9O1xuXG4gICAgICB3aW5kb3dSZWZbdGhpcy5HQUlOU0lHSFRfR0xPQkFMX1NDT1BFXSgnaWRlbnRpZnknLCBleHRlbmRlZElkZW50aWZ5LCB7XG4gICAgICAgIGlkOiBgJHt0ZW5hbnRJRH1fJHtpbnN0YW5jZUlkfWAsXG4gICAgICAgIGluc3RhbmNlSWRcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHdpbmRvd1JlZlt0aGlzLkdBSU5TSUdIVF9HTE9CQUxfU0NPUEVdKCdpZGVudGlmeScsIHJlcXVpcmVkSWRlbnRpZnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJzIGFuIGV2ZW50IHRvIGJlIHJlY29yZGVkIGJ5IEdhaW5zaWdodCBQWC5cbiAgICogVGhpcyBtZXRob2QgY2FsbHMgdGhlIEdhaW5zaWdodCBQWCdzIHRyYWNraW5nIG1lY2hhbmlzbSB0byBsb2cgYSBzcGVjaWZpYyBldmVudFxuICAgKiBhbG9uZyB3aXRoIGl0cyBhc3NvY2lhdGVkIHByb3BlcnRpZXMuXG4gICAqIEBwYXJhbSBldmVudE5hbWUgLSBOYW1lIG9mIHRoZSBldmVudCB0byBiZSB0cmlnZ2VyZWQuXG4gICAqIEBwYXJhbSBwcm9wcyAtIE9wdGlvbmFsIHByb3BlcnRpZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBldmVudC5cbiAgICovXG4gIHRyaWdnZXJFdmVudChldmVudE5hbWU6IHN0cmluZywgcHJvcHM/OiBQeEV2ZW50RGF0YSkge1xuICAgIGlmICh0aGlzLnRhZ0Z1bmN0aW9uICYmIGV2ZW50TmFtZSkge1xuICAgICAgZXZlbnROYW1lID0gdGhpcy5wcmVwYXJlRXZlbnROYW1lKGV2ZW50TmFtZSk7XG4gICAgICB0aGlzLnRhZ0Z1bmN0aW9uKCd0cmFjaycsIGV2ZW50TmFtZSwgcHJvcHMpO1xuICAgIH1cbiAgfVxuXG4gIHRyYW5zbGF0ZVRvRW5nbGlzaCh0ZXh0VG9UcmFuc2xhdGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgeyBjdXJyZW50TGFuZyB9ID0gdGhpcy50cmFuc2xhdGVTZXJ2aWNlO1xuXG4gICAgaWYgKGN1cnJlbnRMYW5nID09PSAnZW4nKSB7XG4gICAgICByZXR1cm4gdGV4dFRvVHJhbnNsYXRlO1xuICAgIH1cblxuICAgIGlmIChjdXJyZW50TGFuZyAmJiBjdXJyZW50TGFuZyAhPT0gdGhpcy5jYWNoZWRMYW5ndWFnZSkge1xuICAgICAgdGhpcy5jYWNoZWRSZXZlcnRlZFRyYW5zbGF0aW9ucyA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuY2FjaGVkUmV2ZXJ0ZWRUcmFuc2xhdGlvbnMpIHtcbiAgICAgIHRoaXMuY2FjaGVkTGFuZ3VhZ2UgPSBjdXJyZW50TGFuZztcbiAgICAgIHRoaXMuY2FjaGVkUmV2ZXJ0ZWRUcmFuc2xhdGlvbnMgPSB0aGlzLmdldFJldmVydGVkVHJhbnNsYXRpb25zKGN1cnJlbnRMYW5nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5nZXRFblRyYW5zbGF0aW9uKHRleHRUb1RyYW5zbGF0ZSwgdGhpcy5jYWNoZWRSZXZlcnRlZFRyYW5zbGF0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGV0aGVyIHBlcnNvbmFsbHkgaWRlbnRpZmlhYmxlIGluZm9ybWF0aW9uIChQSUkpIHNob3VsZCBiZSBzZW50IHdoaWxlIGxvYWRpbmcgYSB0YWcuXG4gICAqIFRoZSBkZWNpc2lvbiB0byBhY3RpdmF0ZSBHYWluc2lnaHQgYW5kIHNlbmQgUElJIHJlbGllcyBvbiB3aGV0aGVyIHRoZSBjb29raWVQcmVmZXJlbmNlcyBvcHRpb24gaXMgZGVmaW5lZCBpbiB0aGUgYXBwbGljYXRpb24gc2V0dGluZ3MsXG4gICAqIGlmIHRoZSBmdW5jdGlvbmFsIGNvb2tpZSBpcyBlbmFibGVkLCBhbmQgaWYgdGhlIHVzZXIgZ3JhbnRzIHBlcm1pc3Npb24uXG4gICAqL1xuICBhc3luYyBzaG91bGRTZW5kUGlpRGF0YSgpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5jb29raWVCYW5uZXJTZXJ2aWNlLmlzQ29uZmlnQ29va2llUHJlZmVyZW5jZXNEZWZpbmVkKCkgJiZcbiAgICAgIHRoaXMuY29va2llQmFubmVyU2VydmljZS5pc0Z1bmN0aW9uYWxDb29raWVFbmFibGVkKCkgJiZcbiAgICAgICEoYXdhaXQgdGhpcy5pc0dhaW5zaWdodFByZWZlcmVuY2VEaXNhYmxlZEluVXNlclByZWZlcmVuY2VzKFxuICAgICAgICB0aGlzLlVTRVJfUFJFRkVSRU5DRVNfR0FJTlNJR0hUX0tFWVxuICAgICAgKSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgYSBzcGVjaWZpYyB1c2VyIGF0dHJpYnV0ZSBpbiB0aGUgR2FpbnNpZ2h0IGdsb2JhbCBzY29wZS5cbiAgICogVGhpcyBtZXRob2QgaW50ZXJmYWNlcyB3aXRoIHRoZSBHYWluc2lnaHQgZ2xvYmFsIG9iamVjdCB0byBzZXQgYSB1c2VyJ3Mgc3BlY2lmaWMgYXR0cmlidXRlIHdpdGggYSBwcm92aWRlZCB2YWx1ZS5cbiAgICogQHBhcmFtIG5hbWUgLSBOYW1lIG9mIHRoZSB1c2VyIGF0dHJpYnV0ZSB0byBiZSB1cGRhdGVkLlxuICAgKiBAcGFyYW0gdmFsdWUgLSBWYWx1ZSB0byBzZXQgZm9yIHRoZSBzcGVjaWZpZWQgdXNlciBhdHRyaWJ1dGUuXG4gICAqL1xuICB1cGRhdGVVc2VyQXR0cmlidXRlKG5hbWU6IHN0cmluZywgdmFsdWU6IHN0cmluZyB8IERhdGUgfCBudW1iZXIgfCBib29sZWFuKTogdm9pZCB7XG4gICAgd2luZG93W3RoaXMuR0FJTlNJR0hUX0dMT0JBTF9TQ09QRV0/Lignc2V0JywgJ3VzZXInLCB7IFtuYW1lXTogdmFsdWUgfSk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiB0aGUgY3VycmVudCB1c2VyIGhhcyB0aGUgY2FwYWJpbGl0eSB0byBtb2RpZnkgR2FpbnNpZ2h0IFBYIHNldHRpbmdzLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBjaGVja3MgbXVsdGlwbGUgY29uZGl0aW9uczpcbiAgICogMS4gV2hldGhlciB0cmFja2luZyBoYXMgYmVlbiBkaXNhYmxlZCBnbG9iYWxseSB2aWEgYXBwbGljYXRpb24gb3B0aW9ucy5cbiAgICogMi4gV2hldGhlciBHYWluc2lnaHQgaXMgZGlzYWJsZWQgYXQgdGhlIHRlbmFudCBsZXZlbCB0aHJvdWdoIGN1c3RvbSBwcm9wZXJ0aWVzLlxuICAgKiAzLiBXaGV0aGVyIGEgR2FpbnNpZ2h0IGtleSBpcyBhdmFpbGFibGUsIGVpdGhlciBjdXJyZW50bHkgbG9hZGVkIG9yIGZldGNoZWQgYXN5bmNocm9ub3VzbHkuXG4gICAqIDQuIFdoZXRoZXIgY29va2llIHByZWZlcmVuY2VzIGFyZSBkZWZpbmVkIGFuZCBhdmFpbGFibGUgZm9yIHRoZSB1c2VyLlxuICAgKlxuICAgKiBAcmV0dXJucyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSBib29sZWFuLiBUcnVlIGluZGljYXRlcyB0aGUgdXNlciBjYW4gZWRpdCBwcm9kdWN0IGV4cGVyaWVuY2Ugc2V0dGluZ3MsIGFuZCBmYWxzZSBvdGhlcndpc2UuXG4gICAqL1xuICBhc3luYyBjYW5FZGl0UHJvZHVjdEV4cGVyaWVuY2VTZXR0aW5ncygpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBjdXJyZW50VGVuYW50ID0gdGhpcy5hcHBTdGF0ZS5jdXJyZW50VGVuYW50LnZhbHVlO1xuICAgIGNvbnN0IHsgY3VzdG9tUHJvcGVydGllcyB9ID0gY3VycmVudFRlbmFudDtcbiAgICBpZiAoXG4gICAgICB0aGlzLmlzVHJhY2tpbmdEaXNhYmxlZCgpIHx8XG4gICAgICB0aGlzLmlzR2FpbnNpZ2h0RGlzYWJsZWRBdFRlbmFudEN1c3RvbVByb3BlcnRpZXMoY3VzdG9tUHJvcGVydGllcylcbiAgICApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3QgZ2FpbnNpZ2h0S2V5ID0gISF0aGlzLmdhaW5zaWdodEtleSB8fCAhIShhd2FpdCB0aGlzLmdldEdhaW5zaWdodEtleSgpKTtcbiAgICByZXR1cm4gKFxuICAgICAgZ2FpbnNpZ2h0S2V5ICYmXG4gICAgICB0aGlzLmNvb2tpZUJhbm5lclNlcnZpY2UuaXNDb25maWdDb29raWVQcmVmZXJlbmNlc0RlZmluZWQoKSAmJlxuICAgICAgISF0aGlzLmNvb2tpZUJhbm5lclNlcnZpY2UuZ2V0VXNlckNvb2tpZVByZWZlcmVuY2VzKClcbiAgICApO1xuICB9XG5cbiAgYXN5bmMgaGFzaEdyb3VwTmFtZShncm91cE5hbWU6IHN0cmluZykge1xuICAgIGlmICghZ3JvdXBOYW1lKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIENvbnZlcnQgdGhlIGdyb3VwIG5hbWUgdG8gYW4gQXJyYXlCdWZmZXJcbiAgICBjb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG4gICAgY29uc3QgZGF0YSA9IGVuY29kZXIuZW5jb2RlKGdyb3VwTmFtZSk7XG5cbiAgICAvLyBVc2UgdGhlIFNIQS0yNTYgYWxnb3JpdGhtIHRvIGhhc2ggdGhlIGRhdGFcbiAgICBjb25zdCBoYXNoQnVmZmVyID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5kaWdlc3QoJ1NIQS0yNTYnLCBkYXRhKTtcblxuICAgIC8vIENvbnZlcnQgdGhlIGhhc2ggdG8gYSBoZXhhZGVjaW1hbCBzdHJpbmdcbiAgICBjb25zdCBoYXNoQXJyYXkgPSBBcnJheS5mcm9tKG5ldyBVaW50OEFycmF5KGhhc2hCdWZmZXIpKTtcbiAgICBjb25zdCBoYXNoZWROYW1lID0gaGFzaEFycmF5Lm1hcChieXRlID0+IGJ5dGUudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJykpLmpvaW4oJycpO1xuXG4gICAgcmV0dXJuIGhhc2hlZE5hbWU7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB0aGUgZ2xvYmFsIGNvbnRleHQgZm9yIEdhaW5zaWdodCB3aXRoIHRoZSBjdXJyZW50IGFwcGxpY2F0aW9uIG5hbWUuXG4gICAqIFRoZSBnbG9iYWwgY29udGV4dCBjYW4gYmUgdXRpbGl6ZWQgYnkgR2FpbnNpZ2h0IGZvciB2YXJpb3VzIHB1cnBvc2VzLCBzdWNoIGFzIHNlZ21lbnRpbmcgdXNlcnMuXG4gICAqL1xuICBzZXRHbG9iYWxDb250ZXh0KCk6IHZvaWQge1xuICAgIGNvbnN0IGN1cnJlbnRBcHBTdGF0ZSA9IHRoaXMuYXBwU3RhdGUuc3RhdGUkLnZhbHVlO1xuICAgIGNvbnN0IGN1cnJlbnRBcHBOYW1lID0gY3VycmVudEFwcFN0YXRlLmFwcC5uYW1lO1xuXG4gICAgd2luZG93W3RoaXMuR0FJTlNJR0hUX0