@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
120 lines • 43 kB
JavaScript
import { Component, Input } from '@angular/core';
import { TenantLoginOptionType } from '@c8y/client';
import { ControlContainer, NgForm } from '@angular/forms';
import { defaults, cloneDeep, isFinite } from 'lodash-es';
import { gettext, TenantUiService } from '@c8y/ngx-components';
import { TranslateService } from '@ngx-translate/core';
import { isOauthInternal } from './basic-settings.model';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/ngx-components";
import * as i2 from "@ngx-translate/core";
import * as i3 from "@angular/common";
import * as i4 from "@angular/forms";
import * as i5 from "ngx-bootstrap/popover";
export class SessionConfigurationComponent {
constructor(tenantUiService, translateService) {
this.tenantUiService = tenantUiService;
this.translateService = translateService;
this.tenantLoginOptionTypeEnum = TenantLoginOptionType;
this.ABSOLUTE_TIMEOUT_VALIDATION_MESSAGE = gettext('The value must be greater than "Token lifespan" and not less than {{ minAbsoluteTimeout }}.');
this.RENEWAL_TIMEOUT_VALIDATION_MESSAGE = gettext('The value must be less than "Token lifespan".');
this.MAX_TOKEN_LIFESPAN_VALIDATION_MESSAGE = gettext('The value must be less than "Session absolute timeout".');
this.MIN_TOKEN_LIFESPAN_VALIDATION_MESSAGE = gettext('The value must be greater than "Session renewal timeout".');
this.USER_AGENT_VALIDATION_REQUIRED_POPOVER = gettext('If selected, then every request needs to use the same "User-Agent" header as the first request which initiated the session.');
this.MIN_ABSOLUTE_TIMEOUT = 15 * 60;
this.ABSOLUTE_TIMEOUT_VALIDATION_MESSAGE = this.translateService.instant(this.ABSOLUTE_TIMEOUT_VALIDATION_MESSAGE, { minAbsoluteTimeout: this.MIN_ABSOLUTE_TIMEOUT });
}
ngOnChanges(changes) {
if (changes.authConfiguration && changes.authConfiguration.currentValue) {
const oauthInternal = changes.authConfiguration.currentValue.loginOptions.find(isOauthInternal) || {};
this.originalSessionConfiguration = cloneDeep(oauthInternal.sessionConfiguration);
this.sessionConfiguration = oauthInternal.sessionConfiguration;
this.previousTokenLifespan =
this.authConfiguration.tenantOptions['oauth.internal']['basic-token.lifespan.seconds'];
}
}
get renewalTimeoutSeconds() {
const sessionConfiguration = this.sessionConfiguration;
return this.convertToSeconds(sessionConfiguration.renewalTimeoutMillis);
}
set renewalTimeoutSeconds(value) {
this.sessionConfiguration.renewalTimeoutMillis = this.convertToMillis(value);
}
get absoluteTimeoutSeconds() {
const sessionConfiguration = this.sessionConfiguration;
return this.convertToSeconds(sessionConfiguration.absoluteTimeoutMillis);
}
set absoluteTimeoutSeconds(value) {
this.sessionConfiguration.absoluteTimeoutMillis = this.convertToMillis(value);
}
get maximumNumberOfParallelSessions() {
return this.sessionConfiguration.maximumNumberOfParallelSessions;
}
set maximumNumberOfParallelSessions(value) {
this.sessionConfiguration.maximumNumberOfParallelSessions = value;
}
get userAgentValidationRequired() {
return this.sessionConfiguration.userAgentValidationRequired;
}
set userAgentValidationRequired(value) {
this.sessionConfiguration.userAgentValidationRequired = value;
}
get basicTokenLifespan() {
return this.authConfiguration.tenantOptions['oauth.internal']['basic-token.lifespan.seconds'];
}
set basicTokenLifespan(value) {
this.authConfiguration.tenantOptions['oauth.internal']['basic-token.lifespan.seconds'] = value;
}
get useSessionConfiguration() {
return !!this.sessionConfiguration;
}
set useSessionConfiguration(value) {
this.sessionConfiguration = value
? defaults({}, this.originalSessionConfiguration, {
absoluteTimeoutMillis: 1209600000, // 14 days
renewalTimeoutMillis: 86400000, // 1 day
maximumNumberOfParallelSessions: 5,
userAgentValidationRequired: false
})
: null;
this.basicTokenLifespan = this.previousTokenLifespan || 172800; // 2 days
}
get absoluteTimeoutConstraints() {
return {
min: Math.max(this.MIN_ABSOLUTE_TIMEOUT, this.basicTokenLifespan + 1)
};
}
get renewalTimeoutConstraints() {
return {
min: this.MIN_ABSOLUTE_TIMEOUT / 2,
max: this.basicTokenLifespan ? this.basicTokenLifespan - 1 : null
};
}
get basicTokenLifespanConstraints() {
return {
min: this.renewalTimeoutSeconds ? this.renewalTimeoutSeconds + 1 : null,
max: this.absoluteTimeoutSeconds ? this.absoluteTimeoutSeconds - 1 : null
};
}
get sessionConfiguration() {
return this.authConfiguration.loginOptions.find(isOauthInternal).sessionConfiguration;
}
set sessionConfiguration(value) {
this.authConfiguration.loginOptions.find(isOauthInternal).sessionConfiguration = value;
}
convertToMillis(seconds) {
return isFinite(seconds) ? seconds * 1000 : null;
}
convertToSeconds(milliseconds) {
return isFinite(milliseconds) ? Math.ceil(milliseconds / 1000) : null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SessionConfigurationComponent, deps: [{ token: i1.TenantUiService }, { token: i2.TranslateService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SessionConfigurationComponent, selector: "c8y-session-configuration", inputs: { authConfiguration: "authConfiguration" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"card-block separator-top overflow-auto\"\n *ngIf=\"authConfiguration.preferredLoginOptionType === tenantLoginOptionTypeEnum.OAUTH2_INTERNAL\"\n>\n <div class=\"col-sm-2\">\n <div class=\"h4 text-normal text-right text-left-xs\">\n {{ 'OAI-Secure session configuration' | translate }}\n </div>\n </div>\n\n <div class=\"col-sm-9\">\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label class=\"c8y-switch\" title=\"{{ 'Use session configuration' | translate }}\">\n <input\n type=\"checkbox\"\n name=\"useSessionConfiguration\"\n [(ngModel)]=\"useSessionConfiguration\"\n />\n <span></span>\n <span>{{ 'Use session configuration' | translate }}</span>\n </label>\n </c8y-form-group>\n </div>\n </div>\n\n <fieldset *ngIf=\"sessionConfiguration\">\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label class=\"c8y-switch\" title=\"{{ 'User agent validation required' | translate }}\">\n <input\n type=\"checkbox\"\n name=\"userAgentValidationRequired\"\n [(ngModel)]=\"userAgentValidationRequired\"\n />\n <span></span>\n <span>{{ 'User agent validation required' | translate }}</span>\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ USER_AGENT_VALIDATION_REQUIRED_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Session absolute timeout' | translate }}\">\n {{ 'Session absolute timeout' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"absoluteTimeoutSeconds\"\n class=\"form-control text-right\"\n [(ngModel)]=\"absoluteTimeoutSeconds\"\n [required]=\"useSessionConfiguration\"\n [min]=\"absoluteTimeoutConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"min\"\n text=\"{{ ABSOLUTE_TIMEOUT_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Session renewal timeout' | translate }}\">\n {{ 'Session renewal timeout' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"renewalTimeoutSeconds\"\n class=\"form-control text-right\"\n [(ngModel)]=\"renewalTimeoutSeconds\"\n [required]=\"useSessionConfiguration\"\n [max]=\"renewalTimeoutConstraints.max\"\n [min]=\"renewalTimeoutConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"max\"\n text=\"{{ RENEWAL_TIMEOUT_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Maximum parallel sessions per user' | translate }}\">\n {{ 'Maximum parallel sessions per user' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"maximumNumberOfParallelSessions\"\n class=\"form-control text-right\"\n [(ngModel)]=\"maximumNumberOfParallelSessions\"\n [required]=\"useSessionConfiguration\"\n [min]=\"1\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>sessions</span>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Token lifespan' | translate }}\">\n {{ 'Token lifespan' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"basicTokenLifespan\"\n class=\"form-control text-right\"\n [(ngModel)]=\"basicTokenLifespan\"\n [required]=\"useSessionConfiguration\"\n [max]=\"basicTokenLifespanConstraints.max\"\n [min]=\"basicTokenLifespanConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"max\"\n text=\"{{ MAX_TOKEN_LIFESPAN_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"min\"\n text=\"{{ MIN_TOKEN_LIFESPAN_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.MinValidationDirective, selector: "[min]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidationDirective, selector: "[max]", inputs: ["max"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i4.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i4.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i5.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SessionConfigurationComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-session-configuration', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], template: "<div\n class=\"card-block separator-top overflow-auto\"\n *ngIf=\"authConfiguration.preferredLoginOptionType === tenantLoginOptionTypeEnum.OAUTH2_INTERNAL\"\n>\n <div class=\"col-sm-2\">\n <div class=\"h4 text-normal text-right text-left-xs\">\n {{ 'OAI-Secure session configuration' | translate }}\n </div>\n </div>\n\n <div class=\"col-sm-9\">\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label class=\"c8y-switch\" title=\"{{ 'Use session configuration' | translate }}\">\n <input\n type=\"checkbox\"\n name=\"useSessionConfiguration\"\n [(ngModel)]=\"useSessionConfiguration\"\n />\n <span></span>\n <span>{{ 'Use session configuration' | translate }}</span>\n </label>\n </c8y-form-group>\n </div>\n </div>\n\n <fieldset *ngIf=\"sessionConfiguration\">\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label class=\"c8y-switch\" title=\"{{ 'User agent validation required' | translate }}\">\n <input\n type=\"checkbox\"\n name=\"userAgentValidationRequired\"\n [(ngModel)]=\"userAgentValidationRequired\"\n />\n <span></span>\n <span>{{ 'User agent validation required' | translate }}</span>\n <button\n class=\"btn-help btn-help--sm\"\n type=\"button\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ USER_AGENT_VALIDATION_REQUIRED_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Session absolute timeout' | translate }}\">\n {{ 'Session absolute timeout' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"absoluteTimeoutSeconds\"\n class=\"form-control text-right\"\n [(ngModel)]=\"absoluteTimeoutSeconds\"\n [required]=\"useSessionConfiguration\"\n [min]=\"absoluteTimeoutConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"min\"\n text=\"{{ ABSOLUTE_TIMEOUT_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Session renewal timeout' | translate }}\">\n {{ 'Session renewal timeout' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"renewalTimeoutSeconds\"\n class=\"form-control text-right\"\n [(ngModel)]=\"renewalTimeoutSeconds\"\n [required]=\"useSessionConfiguration\"\n [max]=\"renewalTimeoutConstraints.max\"\n [min]=\"renewalTimeoutConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"max\"\n text=\"{{ RENEWAL_TIMEOUT_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Maximum parallel sessions per user' | translate }}\">\n {{ 'Maximum parallel sessions per user' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"maximumNumberOfParallelSessions\"\n class=\"form-control text-right\"\n [(ngModel)]=\"maximumNumberOfParallelSessions\"\n [required]=\"useSessionConfiguration\"\n [min]=\"1\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>sessions</span>\n </div>\n </c8y-form-group>\n </div>\n <div class=\"col-sm-6\">\n <c8y-form-group>\n <label title=\"{{ 'Token lifespan' | translate }}\">\n {{ 'Token lifespan' | translate }}\n </label>\n <div class=\"input-group\">\n <input\n type=\"number\"\n name=\"basicTokenLifespan\"\n class=\"form-control text-right\"\n [(ngModel)]=\"basicTokenLifespan\"\n [required]=\"useSessionConfiguration\"\n [max]=\"basicTokenLifespanConstraints.max\"\n [min]=\"basicTokenLifespanConstraints.min\"\n step=\"1\"\n />\n <span class=\"input-group-addon\" translate>seconds</span>\n </div>\n <c8y-messages>\n <c8y-message\n name=\"max\"\n text=\"{{ MAX_TOKEN_LIFESPAN_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n <c8y-message\n name=\"min\"\n text=\"{{ MIN_TOKEN_LIFESPAN_VALIDATION_MESSAGE | translate }}\"\n ></c8y-message>\n </c8y-messages>\n </c8y-form-group>\n </div>\n </div>\n </fieldset>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.TenantUiService }, { type: i2.TranslateService }], propDecorators: { authConfiguration: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2F1dGgtY29uZmlndXJhdGlvbi9iYXNpYy1zZXR0aW5ncy9zZXNzaW9uLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vYXV0aC1jb25maWd1cmF0aW9uL2Jhc2ljLXNldHRpbmdzL3Nlc3Npb24tY29uZmlndXJhdGlvbi5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBaUIsTUFBTSxlQUFlLENBQUM7QUFDaEUsT0FBTyxFQUF5QixxQkFBcUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzFELE9BQU8sRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFL0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHdCQUF3QixDQUFDOzs7Ozs7O0FBT3pELE1BQU0sT0FBTyw2QkFBNkI7SUF1QnhDLFlBQ1UsZUFBZ0MsRUFDaEMsZ0JBQWtDO1FBRGxDLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUNoQyxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBdEI1Qyw4QkFBeUIsR0FBRyxxQkFBcUIsQ0FBQztRQUNsRCx3Q0FBbUMsR0FBRyxPQUFPLENBQzNDLDZGQUE2RixDQUM5RixDQUFDO1FBQ0YsdUNBQWtDLEdBQUcsT0FBTyxDQUFDLCtDQUErQyxDQUFDLENBQUM7UUFDOUYsMENBQXFDLEdBQUcsT0FBTyxDQUM3Qyx5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QyxDQUFDLENBQUMsSUFBSTtTQUMxRSxDQUFDO0lBQ0osQ0FBQztJQUVELElBQUksb0JBQW9CO1FBQ3RCLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsb0JBQW9CLENBQUM7SUFDeEYsQ0FBQztJQUVELElBQUksb0JBQW9CLENBQUMsS0FBNEI7UUFDbkQsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsb0JBQW9CLEdBQUcsS0FBSyxDQUFDO0lBQ3pGLENBQUM7SUFFTyxlQUFlLENBQUMsT0FBZTtRQUNyQyxPQUFPLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ25ELENBQUM7SUFFTyxnQkFBZ0IsQ0FBQyxZQUFvQjtRQUMzQyxPQUFPLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUN4RSxDQUFDOytHQXhJVSw2QkFBNkI7bUdBQTdCLDZCQUE2QiwwSUNkMUMsMjVMQWdLQSxzd0ZEcEppQixDQUFDLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsQ0FBQzs7NEZBRXhELDZCQUE2QjtrQkFMekMsU0FBUzsrQkFDRSwyQkFBMkIsaUJBRXRCLENBQUMsRUFBRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxDQUFDO21IQUluRSxpQkFBaUI7c0JBRGhCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBTaW1wbGVDaGFuZ2VzIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJU2Vzc2lvbkNvbmZpZ3VyYXRpb24sIFRlbmFudExvZ2luT3B0aW9uVHlwZSB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IENvbnRyb2xDb250YWluZXIsIE5nRm9ybSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IGRlZmF1bHRzLCBjbG9uZURlZXAsIGlzRmluaXRlIH0gZnJvbSAnbG9kYXNoLWVzJztcbmltcG9ydCB7IGdldHRleHQsIFRlbmFudFVpU2VydmljZSB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMnO1xuaW1wb3J0IHsgQXV0aENvbmZpZ3VyYXRpb24gfSBmcm9tICcuL2F1dGgtY29uZmlndXJhdGlvbi5tb2RlbCc7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBpc09hdXRoSW50ZXJuYWwgfSBmcm9tICcuL2Jhc2ljLXNldHRpbmdzLm1vZGVsJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LXNlc3Npb24tY29uZmlndXJhdGlvbicsXG4gIHRlbXBsYXRlVXJsOiAnLi9zZXNzaW9uLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmh0bWwnLFxuICB2aWV3UHJvdmlkZXJzOiBbeyBwcm92aWRlOiBDb250cm9sQ29udGFpbmVyLCB1c2VFeGlzdGluZzogTmdGb3JtIH1dXG59KVxuZXhwb3J0IGNsYXNzIFNlc3Npb25Db25maWd1cmF0aW9uQ29tcG9uZW50IHtcbiAgQElucHV0KClcbiAgYXV0aENvbmZpZ3VyYXRpb246IEF1dGhDb25maWd1cmF0aW9uO1xuICB0ZW5hbnRMb2dpbk9wdGlvblR5cGVFbnVtID0gVGVuYW50TG9naW5PcHRpb25UeXBlO1xuICBBQlNPTFVURV9USU1FT1VUX1ZBTElEQVRJT05fTUVTU0FHRSA9IGdldHRleHQoXG4gICAgJ1RoZSB2YWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiBcIlRva2VuIGxpZmVzcGFuXCIgYW5kIG5vdCBsZXNzIHRoYW4ge3sgbWluQWJzb2x1dGVUaW1lb3V0IH19LidcbiAgKTtcbiAgUkVORVdBTF9USU1FT1VUX1ZBTElEQVRJT05fTUVTU0FHRSA9IGdldHRleHQoJ1RoZSB2YWx1ZSBtdXN0IGJlIGxlc3MgdGhhbiBcIlRva2VuIGxpZmVzcGFuXCIuJyk7XG4gIE1BWF9UT0tFTl9MSUZFU1BBTl9WQUxJREFUSU9OX01FU1NBR0UgPSBnZXR0ZXh0KFxuICAgICdUaGUgdmFsdWUgbXVzdCBiZSBsZXNzIHRoYW4gXCJTZXNzaW9uIGFic29sdXRlIHRpbWVvdXRcIi4nXG4gICk7XG4gIE1JTl9UT0tFTl9MSUZFU1BBTl9WQUxJREFUSU9OX01FU1NBR0UgPSBnZXR0ZXh0KFxuICAgICdUaGUgdmFsdWUgbXVzdCBiZSBncmVhdGVyIHRoYW4gXCJTZXNzaW9uIHJlbmV3YWwgdGltZW91dFwiLidcbiAgKTtcblxuICBVU0VSX0FHRU5UX1ZBTElEQVRJT05fUkVRVUlSRURfUE9QT1ZFUiA9IGdldHRleHQoXG4gICAgJ0lmIHNlbGVjdGVkLCB0aGVuIGV2ZXJ5IHJlcXVlc3QgbmVlZHMgdG8gdXNlIHRoZSBzYW1lIFwiVXNlci1BZ2VudFwiIGhlYWRlciBhcyB0aGUgZmlyc3QgcmVxdWVzdCB3aGljaCBpbml0aWF0ZWQgdGhlIHNlc3Npb24uJ1xuICApO1xuXG4gIHByaXZhdGUgTUlOX0FCU09MVVRFX1RJTUVPVVQ6IG51bWJlciA9IDE1ICogNjA7XG4gIHByaXZhdGUgb3JpZ2luYWxTZXNzaW9uQ29uZmlndXJhdGlvbjtcbiAgcHJpdmF0ZSBwcmV2aW91c1Rva2VuTGlmZXNwYW47XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSB0ZW5hbnRVaVNlcnZpY2U6IFRlbmFudFVpU2VydmljZSxcbiAgICBwcml2YXRlIHRyYW5zbGF0ZVNlcnZpY2U6IFRyYW5zbGF0ZVNlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5BQlNPTFVURV9USU1FT1VUX1ZBTElEQVRJT05fTUVTU0FHRSA9IHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KFxuICAgICAgdGhpcy5BQlNPTFVURV9USU1FT1VUX1ZBTElEQVRJT05fTUVTU0FHRSxcbiAgICAgIHsgbWluQWJzb2x1dGVUaW1lb3V0OiB0aGlzLk1JTl9BQlNPTFVURV9USU1FT1VUIH1cbiAgICApO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzLmF1dGhDb25maWd1cmF0aW9uICYmIGNoYW5nZXMuYXV0aENvbmZpZ3VyYXRpb24uY3VycmVudFZhbHVlKSB7XG4gICAgICBjb25zdCBvYXV0aEludGVybmFsID1cbiAgICAgICAgY2hhbmdlcy5hdXRoQ29uZmlndXJhdGlvbi5jdXJyZW50VmFsdWUubG9naW5PcHRpb25zLmZpbmQoaXNPYXV0aEludGVybmFsKSB8fCB7fTtcbiAgICAgIHRoaXMub3JpZ2luYWxTZXNzaW9uQ29uZmlndXJhdGlvbiA9IGNsb25lRGVlcChvYXV0aEludGVybmFsLnNlc3Npb25Db25maWd1cmF0aW9uKTtcbiAgICAgIHRoaXMuc2Vzc2lvbkNvbmZpZ3VyYXRpb24gPSBvYXV0aEludGVybmFsLnNlc3Npb25Db25maWd1cmF0aW9uO1xuICAgICAgdGhpcy5wcmV2aW91c1Rva2VuTGlmZXNwYW4gPVxuICAgICAgICB0aGlzLmF1dGhDb25maWd1cmF0aW9uLnRlbmFudE9wdGlvbnNbJ29hdXRoLmludGVybmFsJ11bJ2Jhc2ljLXRva2VuLmxpZmVzcGFuLnNlY29uZHMnXTtcbiAgICB9XG4gIH1cblxuICBnZXQgcmVuZXdhbFRpbWVvdXRTZWNvbmRzKCk6IG51bWJlciB7XG4gICAgY29uc3Qgc2Vzc2lvbkNvbmZpZ3VyYXRpb24gPSB0aGlzLnNlc3Npb25Db25maWd1cmF0aW9uO1xuICAgIHJldHVybiB0aGlzLmNvbnZlcnRUb1NlY29uZHMoc2Vzc2lvbkNvbmZpZ3VyYXRpb24ucmVuZXdhbFRpbWVvdXRNaWxsaXMpO1xuICB9XG5cbiAgc2V0IHJlbmV3YWxUaW1lb3V0U2Vjb25kcyh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5zZXNzaW9uQ29uZmlndXJhdGlvbi5yZW5ld2FsVGltZW91dE1pbGxpcyA9IHRoaXMuY29udmVydFRvTWlsbGlzKHZhbHVlKTtcbiAgfVxuXG4gIGdldCBhYnNvbHV0ZVRpbWVvdXRTZWNvbmRzKCk6IG51bWJlciB7XG4gICAgY29uc3Qgc2Vzc2lvbkNvbmZpZ3VyYXRpb24gPSB0aGlzLnNlc3Npb25Db25maWd1cmF0aW9uO1xuICAgIHJldHVybiB0aGlzLmNvbnZlcnRUb1NlY29uZHMoc2Vzc2lvbkNvbmZpZ3VyYXRpb24uYWJzb2x1dGVUaW1lb3V0TWlsbGlzKTtcbiAgfVxuXG4gIHNldCBhYnNvbHV0ZVRpbWVvdXRTZWNvbmRzKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLnNlc3Npb25Db25maWd1cmF0aW9uLmFic29sdXRlVGltZW91dE1pbGxpcyA9IHRoaXMuY29udmVydFRvTWlsbGlzKHZhbHVlKTtcbiAgfVxuXG4gIGdldCBtYXhpbXVtTnVtYmVyT2ZQYXJhbGxlbFNlc3Npb25zKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc2Vzc2lvbkNvbmZpZ3VyYXRpb24ubWF4aW11bU51bWJlck9mUGFyYWxsZWxTZXNzaW9ucztcbiAgfVxuXG4gIHNldCBtYXhpbXVtTnVtYmVyT2ZQYXJhbGxlbFNlc3Npb25zKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLnNlc3Npb25Db25maWd1cmF0aW9uLm1heGltdW1OdW1iZXJPZlBhcmFsbGVsU2Vzc2lvbnMgPSB2YWx1ZTtcbiAgfVxuXG4gIGdldCB1c2VyQWdlbnRWYWxpZGF0aW9uUmVxdWlyZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2Vzc2lvbkNvbmZpZ3VyYXRpb24udXNlckFnZW50VmFsaWRhdGlvblJlcXVpcmVkO1xuICB9XG5cbiAgc2V0IHVzZXJBZ2VudFZhbGlkYXRpb25SZXF1aXJlZCh2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuc2Vzc2lvbkNvbmZpZ3VyYXRpb24udXNlckFnZW50VmFsaWRhdGlvblJlcXVpcmVkID0gdmFsdWU7XG4gIH1cblxuICBnZXQgYmFzaWNUb2tlbkxpZmVzcGFuKCkge1xuICAgIHJldHVybiB0aGlzLmF1dGhDb25maWd1cmF0aW9uLnRlbmFudE9wdGlvbnNbJ29hdXRoLmludGVybmFsJ11bJ2Jhc2ljLXRva2VuLmxpZmVzcGFuLnNlY29uZHMnXTtcbiAgfVxuXG4gIHNldCBiYXNpY1Rva2VuTGlmZXNwYW4odmFsdWUpIHtcbiAgICB0aGlzLmF1dGhDb25maWd1cmF0aW9uLnRlbmFudE9wdGlvbnNbJ29hdXRoLmludGVybmFsJ11bJ2Jhc2ljLXRva2VuLmxpZmVzcGFuLnNlY29uZHMnXSA9IHZhbHVlO1xuICB9XG5cbiAgZ2V0IHVzZVNlc3Npb25Db25maWd1cmF0aW9uKCkge1xuICAgIHJldHVybiAhIXRoaXMuc2Vzc2lvbkNvbmZpZ3VyYXRpb247XG4gIH1cblxuICBzZXQgdXNlU2Vzc2lvbkNvbmZpZ3VyYXRpb24odmFsdWUpIHtcbiAgICB0aGlzLnNlc3Npb25Db25maWd1cmF0aW9uID0gdmFsdWVcbiAgICAgID8gZGVmYXVsdHMoe30sIHRoaXMub3JpZ2luYWxTZXNzaW9uQ29uZmlndXJhdGlvbiwge1xuICAgICAgICAgIGFic29sdXRlVGltZW91dE1pbGxpczogMTIwOTYwMDAwMCwgLy8gMTQgZGF5c1xuICAgICAgICAgIHJlbmV3YWxUaW1lb3V0TWlsbGlzOiA4NjQwMDAwMCwgLy8gMSBkYXlcbiAgICAgICAgICBtYXhpbXVtTnVtYmVyT2ZQYXJhbGxlbFNlc3Npb25zOiA1LFxuICAgICAgICAgIHVzZXJBZ2VudFZhbGlkYXRpb25SZXF1aXJlZDogZmFsc2VcbiAgICAgICAgfSlcbiAgICAgIDogbnVsbDtcbiAgICB0aGlzLmJhc2ljVG9rZW5MaWZlc3BhbiA9IHRoaXMucHJldmlvdXNUb2tlbkxpZmVzcGFuIHx8IDE3MjgwMDsgLy8gMiBkYXlzXG4gIH1cblxuICBnZXQgYWJzb2x1dGVUaW1lb3V0Q29uc3RyYWludHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG1pbjogTWF0aC5tYXgodGhpcy5NSU5fQUJTT0xVVEVfVElNRU9VVCwgdGhpcy5iYXNpY1Rva2VuTGlmZXNwYW4gKyAxKVxuICAgIH07XG4gIH1cblxuICBnZXQgcmVuZXdhbFRpbWVvdXRDb25zdHJhaW50cygpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbWluOiB0aGlzLk1JTl9BQlNPTFVURV9USU1FT1VUIC8gMixcbiAgICAgIG1heDogdGhpcy5iYXNpY1Rva2VuTGlmZXNwYW4gPyB0aGlzLmJhc2ljVG9rZW5MaWZlc3BhbiAtIDEgOiBudWxsXG4gICAgfTtcbiAgfVxuXG4gIGdldCBiYXNpY1Rva2VuTGlmZXNwYW5Db25zdHJhaW50cygpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbWluOiB0aGlzLnJlbmV3YWxUaW1lb3V0U2Vjb25kcyA/IHRoaXMucmVuZXdhbFRpbWVvdXRTZWNvbmRzICsgMSA6IG51bGwsXG4gICAgICBtYXg6IHRoaXMuYWJzb2x1dGVUaW1lb3V0U2Vjb25kcyA/IHRoaXMuYWJzb2x1dGVUaW1lb3V0U2Vjb25kcyAtIDEgOiBudWxsXG4gICAgfTtcbiAgfVxuXG4gIGdldCBzZXNzaW9uQ29uZmlndXJhdGlvbigpOiBJU2Vzc2lvbkNvbmZpZ3VyYXRpb24ge1xuICAgIHJldHVybiB0aGlzLmF1dGhDb25maWd1cmF0aW9uLmxvZ2luT3B0aW9ucy5maW5kKGlzT2F1dGhJbnRlcm5hbCkuc2Vzc2lvbkNvbmZpZ3VyYXRpb247XG4gIH1cblxuICBzZXQgc2Vzc2lvbkNvbmZpZ3VyYXRpb24odmFsdWU6IElTZXNzaW9uQ29uZmlndXJhdGlvbikge1xuICAgIHRoaXMuYXV0aENvbmZpZ3VyYXRpb24ubG9naW5PcHRpb25zLmZpbmQoaXNPYXV0aEludGVybmFsKS5zZXNzaW9uQ29uZmlndXJhdGlvbiA9IHZhbHVlO1xuICB9XG5cbiAgcHJpdmF0ZSBjb252ZXJ0VG9NaWxsaXMoc2Vjb25kczogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gaXNGaW5pdGUoc2Vjb25kcykgPyBzZWNvbmRzICogMTAwMCA6IG51bGw7XG4gIH1cblxuICBwcml2YXRlIGNvbnZlcnRUb1NlY29uZHMobWlsbGlzZWNvbmRzOiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBpc0Zpbml0ZShtaWxsaXNlY29uZHMpID8gTWF0aC5jZWlsKG1pbGxpc2Vjb25kcyAvIDEwMDApIDogbnVsbDtcbiAgfVxufVxuIiwiPGRpdlxuICBjbGFzcz1cImNhcmQtYmxvY2sgc2VwYXJhdG9yLXRvcCBvdmVyZmxvdy1hdXRvXCJcbiAgKm5nSWY9XCJhdXRoQ29uZmlndXJhdGlvbi5wcmVmZXJyZWRMb2dpbk9wdGlvblR5cGUgPT09IHRlbmFudExvZ2luT3B0aW9uVHlwZUVudW0uT0FVVEgyX0lOVEVSTkFMXCJcbj5cbiAgPGRpdiBjbGFzcz1cImNvbC1zbS0yXCI+XG4gICAgPGRpdiBjbGFzcz1cImg0IHRleHQtbm9ybWFsIHRleHQtcmlnaHQgdGV4dC1sZWZ0LXhzXCI+XG4gICAgICB7eyAnT0FJLVNlY3VyZSBzZXNzaW9uIGNvbmZpZ3VyYXRpb24nIHwgdHJhbnNsYXRlIH19XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDxkaXYgY2xhc3M9XCJjb2wtc20tOVwiPlxuICAgIDxkaXYgY2xhc3M9XCJyb3dcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tNlwiPlxuICAgICAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgICAgPGxhYmVsIGNsYXNzPVwiYzh5LXN3aXRjaFwiIHRpdGxlPVwie3sgJ1VzZSBzZXNzaW9uIGNvbmZpZ3VyYXRpb24nIHwgdHJhbnNsYXRlIH19XCI+XG4gICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgdHlwZT1cImNoZWNrYm94XCJcbiAgICAgICAgICAgICAgbmFtZT1cInVzZVNlc3Npb25Db25maWd1cmF0aW9uXCJcbiAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJ1c2VTZXNzaW9uQ29uZmlndXJhdGlvblwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPHNwYW4+PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+e3sgJ1VzZSBzZXNzaW9uIGNvbmZpZ3VyYXRpb24nIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgICAgIDwvbGFiZWw+XG4gICAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxmaWVsZHNldCAqbmdJZj1cInNlc3Npb25Db25maWd1cmF0aW9uXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwicm93XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tNlwiPlxuICAgICAgICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cImM4eS1zd2l0Y2hcIiB0aXRsZT1cInt7ICdVc2VyIGFnZW50IHZhbGlkYXRpb24gcmVxdWlyZWQnIHwgdHJhbnNsYXRlIH19XCI+XG4gICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICAgICAgbmFtZT1cInVzZXJBZ2VudFZhbGlkYXRpb25SZXF1aXJlZFwiXG4gICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJ1c2VyQWdlbnRWYWxpZGF0aW9uUmVxdWlyZWRcIlxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgICAgICAgIDxzcGFuPnt7ICdVc2VyIGFnZW50IHZhbGlkYXRpb24gcmVxdWlyZWQnIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJidG4taGVscCBidG4taGVscC0tc21cIlxuICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ0hlbHAnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgICAgICBwb3BvdmVyPVwie3sgVVNFUl9BR0VOVF9WQUxJREFUSU9OX1JFUVVJUkVEX1BPUE9WRVIgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgICAgIHBsYWNlbWVudD1cInJpZ2h0XCJcbiAgICAgICAgICAgICAgICB0cmlnZ2Vycz1cImZvY3VzXCJcbiAgICAgICAgICAgICAgICBjb250YWluZXI9XCJib2R5XCJcbiAgICAgICAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgICAgPGRpdiBjbGFzcz1cInJvd1wiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTZcIj5cbiAgICAgICAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgICAgICA8bGFiZWwgdGl0bGU9XCJ7eyAnU2Vzc2lvbiBhYnNvbHV0ZSB0aW1lb3V0JyB8IHRyYW5zbGF0ZSB9fVwiPlxuICAgICAgICAgICAgICB7eyAnU2Vzc2lvbiBhYnNvbHV0ZSB0aW1lb3V0JyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cFwiPlxuICAgICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgICB0eXBlPVwibnVtYmVyXCJcbiAgICAgICAgICAgICAgICBuYW1lPVwiYWJzb2x1dGVUaW1lb3V0U2Vjb25kc1wiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2wgdGV4dC1yaWdodFwiXG4gICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJhYnNvbHV0ZVRpbWVvdXRTZWNvbmRzXCJcbiAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwidXNlU2Vzc2lvbkNvbmZpZ3VyYXRpb25cIlxuICAgICAgICAgICAgICAgIFttaW5dPVwiYWJzb2x1dGVUaW1lb3V0Q29uc3RyYWludHMubWluXCJcbiAgICAgICAgICAgICAgICBzdGVwPVwiMVwiXG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiaW5wdXQtZ3JvdXAtYWRkb25cIiB0cmFuc2xhdGU+c2Vjb25kczwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGM4eS1tZXNzYWdlcz5cbiAgICAgICAgICAgICAgPGM4eS1tZXNzYWdlXG4gICAgICAgICAgICAgICAgbmFtZT1cIm1pblwiXG4gICAgICAgICAgICAgICAgdGV4dD1cInt7IEFCU09MVVRFX1RJTUVPVVRfVkFMSURBVElPTl9NRVNTQUdFIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgICAgPjwvYzh5LW1lc3NhZ2U+XG4gICAgICAgICAgICA8L2M4eS1tZXNzYWdlcz5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbC1zbS02XCI+XG4gICAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgICAgPGxhYmVsIHRpdGxlPVwie3sgJ1Nlc3Npb24gcmVuZXdhbCB0aW1lb3V0JyB8IHRyYW5zbGF0ZSB9fVwiPlxuICAgICAgICAgICAgICB7eyAnU2Vzc2lvbiByZW5ld2FsIHRpbWVvdXQnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImlucHV0LWdyb3VwXCI+XG4gICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICAgICAgICAgIG5hbWU9XCJyZW5ld2FsVGltZW91dFNlY29uZHNcIlxuICAgICAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sIHRleHQtcmlnaHRcIlxuICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwicmVuZXdhbFRpbWVvdXRTZWNvbmRzXCJcbiAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwidXNlU2Vzc2lvbkNvbmZpZ3VyYXRpb25cIlxuICAgICAgICAgICAgICAgIFttYXhdPVwicmVuZXdhbFRpbWVvdXRDb25zdHJhaW50cy5tYXhcIlxuICAgICAgICAgICAgICAgIFttaW5dPVwicmVuZXdhbFRpbWVvdXRDb25zdHJhaW50cy5taW5cIlxuICAgICAgICAgICAgICAgIHN0ZXA9XCIxXCJcbiAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJpbnB1dC1ncm91cC1hZGRvblwiIHRyYW5zbGF0ZT5zZWNvbmRzPC9zcGFuPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8Yzh5LW1lc3NhZ2VzPlxuICAgICAgICAgICAgICA8Yzh5LW1lc3NhZ2VcbiAgICAgICAgICAgICAgICBuYW1lPVwibWF4XCJcbiAgICAgICAgICAgICAgICB0ZXh0PVwie3sgUkVORVdBTF9USU1FT1VUX1ZBTElEQVRJT05fTUVTU0FHRSB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgID48L2M4eS1tZXNzYWdlPlxuICAgICAgICAgICAgPC9jOHktbWVzc2FnZXM+XG4gICAgICAgICAgPC9jOHktZm9ybS1ncm91cD5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdiBjbGFzcz1cInJvd1wiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTZcIj5cbiAgICAgICAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICAgICAgICA8bGFiZWwgdGl0bGU9XCJ7eyAnTWF4aW11bSBwYXJhbGxlbCBzZXNzaW9ucyBwZXIgdXNlcicgfCB0cmFuc2xhdGUgfX1cIj5cbiAgICAgICAgICAgICAge3sgJ01heGltdW0gcGFyYWxsZWwgc2Vzc2lvbnMgcGVyIHVzZXInIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImlucHV0LWdyb3VwXCI+XG4gICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICAgICAgICAgIG5hbWU9XCJtYXhpbXVtTnVtYmVyT2ZQYXJhbGxlbFNlc3Npb25zXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbCB0ZXh0LXJpZ2h0XCJcbiAgICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cIm1heGltdW1OdW1iZXJPZlBhcmFsbGVsU2Vzc2lvbnNcIlxuICAgICAgICAgICAgICAgIFtyZXF1aXJlZF09XCJ1c2VTZXNzaW9uQ29uZmlndXJhdGlvblwiXG4gICAgICAgICAgICAgICAgW21pbl09XCIxXCJcbiAgICAgICAgICAgICAgICBzdGVwPVwiMVwiXG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiaW5wdXQtZ3JvdXAtYWRkb25cIiB0cmFuc2xhdGU+c2Vzc2lvbnM8L3NwYW4+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbC1zbS02XCI+XG4gICAgICAgICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgICAgICAgPGxhYmVsIHRpdGxlPVwie3sgJ1Rva2VuIGxpZmVzcGFuJyB8IHRyYW5zbGF0ZSB9fVwiPlxuICAgICAgICAgICAgICB7eyAnVG9rZW4gbGlmZXNwYW4nIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2xhYmVsPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImlucHV0LWdyb3VwXCI+XG4gICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICAgICAgICAgIG5hbWU9XCJiYXNpY1Rva2VuTGlmZXNwYW5cIlxuICAgICAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sIHRleHQtcmlnaHRcIlxuICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwiYmFzaWNUb2tlbkxpZmVzcGFuXCJcbiAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwidXNlU2Vzc2lvbkNvbmZpZ3VyYXRpb25cIlxuICAgICAgICAgICAgICAgIFttYXhdPVwiYmFzaWNUb2tlbkxpZmVzcGFuQ29uc3RyYWludHMubWF4XCJcbiAgICAgICAgICAgICAgICBbbWluXT1cImJhc2ljVG9rZW5MaWZlc3BhbkNvbnN0cmFpbnRzLm1pblwiXG4gICAgICAgICAgICAgICAgc3RlcD1cIjFcIlxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImlucHV0LWdyb3VwLWFkZG9uXCIgdHJhbnNsYXRlPnNlY29uZHM8L3NwYW4+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDxjOHktbWVzc2FnZXM+XG4gICAgICAgICAgICAgIDxjOHktbWVzc2FnZVxuICAgICAgICAgICAgICAgIG5hbWU9XCJtYXhcIlxuICAgICAgICAgICAgICAgIHRleHQ9XCJ7eyBNQVhfVE9LRU5fTElGRVNQQU5fVkFMSURBVElPTl9NRVNTQUdFIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgICAgPjwvYzh5LW1lc3NhZ2U+XG4gICAgICAgICAgICAgIDxjOHktbWVzc2FnZVxuICAgICAgICAgICAgICAgIG5hbWU9XCJtaW5cIlxuICAgICAgICAgICAgICAgIHRleHQ9XCJ7eyBNSU5fVE9LRU5fTElGRVNQQU5fVkFMSURBVElPTl9NRVNTQUdFIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAgICAgPjwvYzh5LW1lc3NhZ2U+XG4gICAgICAgICAgICA8L2M4eS1tZXNzYWdlcz5cbiAgICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZmllbGRzZXQ+XG4gIDwvZGl2PlxuPC9kaXY+XG4iXX0=