ngx-captcha-ssr
Version:
Dynamic captcha (Google reCaptcha) implementation for Angular
313 lines • 35.6 kB
JavaScript
import { isPlatformBrowser } from "@angular/common";
import { Directive, EventEmitter, Inject, InjectFlags, Injector, Input, NgZone, Output, PLATFORM_ID, Renderer2, } from "@angular/core";
import { NgControl, } from "@angular/forms";
import { ScriptService } from "../services/script.service";
import * as i0 from "@angular/core";
import * as i1 from "../services/script.service";
export class BaseReCaptchaComponentDirective {
constructor(renderer, zone, injector, scriptService, platformId) {
this.renderer = renderer;
this.zone = zone;
this.injector = injector;
this.scriptService = scriptService;
this.platformId = platformId;
/**
* Prefix of the captcha element
*/
this.captchaElemPrefix = "ngx_captcha_id_";
this.setupCaptcha = true;
/**
* Indicates if global domain 'recaptcha.net' should be used instead of default domain ('google.com')
*/
this.useGlobalDomain = false;
this.useEnterprise = false;
/**
* Type
*/
this.type = "image";
/**
* Tab index
*/
this.tabIndex = 0;
/**
* Called when captcha receives successful response.
* Captcha response token is passed to event.
*/
this.success = new EventEmitter();
/**
* Called when captcha is loaded. Event receives id of the captcha
*/
this.load = new EventEmitter();
/**
* Called when captcha is reset.
*/
this.reset = new EventEmitter();
/**
* Called when captcha is loaded & ready. I.e. when you need to execute captcha on component load.
*/
this.ready = new EventEmitter();
/**
* Error callback
*/
this.error = new EventEmitter();
/**
* Expired callback
*/
this.expire = new EventEmitter();
/**
* Indicates if captcha should be set on load
*/
this.setupAfterLoad = false;
/**
* If enabled, captcha will reset after receiving success response. This is useful
* when invisible captcha need to be resolved multiple times on same page
*/
this.resetCaptchaAfterSuccess = false;
/**
* Required by ControlValueAccessor
*/
this.onChange = (val) => { };
this.onTouched = (val) => { };
/**
* Indicates if captcha is loaded
*/
this.isLoaded = false;
}
ngAfterViewInit() {
this.control = this.injector.get(NgControl, undefined, InjectFlags.Optional)?.control;
}
ngAfterViewChecked() {
if (this.setupCaptcha) {
this.setupCaptcha = false;
this.setupComponent();
}
}
ngOnChanges(changes) {
// cleanup scripts if language changed because they need to be reloaded
if (changes && changes.hl) {
// cleanup scripts when language changes
if (!changes.hl.firstChange &&
changes.hl.currentValue !== changes.hl.previousValue) {
this.scriptService.cleanup();
}
}
if (changes && changes.useGlobalDomain) {
// cleanup scripts when domain changes
if (!changes.useGlobalDomain.firstChange &&
changes.useGlobalDomain.currentValue !==
changes.useGlobalDomain.previousValue) {
this.scriptService.cleanup();
}
}
this.setupCaptcha = true;
}
/**
* Gets captcha response as per reCaptcha docs
*/
getResponse() {
return this.reCaptchaApi.getResponse(this.captchaId);
}
/**
* Gets Id of captcha widget
*/
getCaptchaId() {
return this.captchaId;
}
/**
* Resets captcha
*/
resetCaptcha() {
this.zone.run(() => {
// reset captcha using Google js api
this.reCaptchaApi.reset();
// required due to forms
this.onChange(undefined);
this.onTouched(undefined);
// trigger reset event
this.reset.next();
});
}
/**
* Gets last submitted captcha response
*/
getCurrentResponse() {
return this.currentResponse;
}
/**
* Reload captcha. Useful when properties (i.e. theme) changed and captcha need to reflect them
*/
reloadCaptcha() {
this.setupComponent();
}
ensureCaptchaElem(captchaElemId) {
if (isPlatformBrowser(this.platformId)) {
const captchaElem = document.getElementById(captchaElemId);
if (!captchaElem) {
throw Error(`Captcha element with id '${captchaElemId}' was not found`);
}
// assign captcha alem
this.captchaElem = captchaElem;
}
}
/**
* Responsible for instantiating captcha element
*/
renderReCaptcha() {
// run outside angular zone due to timeout issues when testing
// details: https://github.com/Enngage/ngx-captcha/issues/26
this.zone.runOutsideAngular(() => {
// to fix reCAPTCHA placeholder element must be an element or id
// https://github.com/Enngage/ngx-captcha/issues/96
setTimeout(() => {
this.captchaId = this.reCaptchaApi.render(this.captchaElemId, this.getCaptchaProperties());
this.ready.next();
}, 0);
});
}
/**
* Called when captcha receives response
* @param callback Callback
*/
handleCallback(callback) {
this.currentResponse = callback;
this.success.next(callback);
this.zone.run(() => {
this.onChange(callback);
this.onTouched(callback);
});
if (this.resetCaptchaAfterSuccess) {
this.resetCaptcha();
}
}
getPseudoUniqueNumber() {
return new Date().getUTCMilliseconds() + Math.floor(Math.random() * 9999);
}
setupComponent() {
// captcha specific setup
this.captchaSpecificSetup();
// create captcha wrapper
this.createAndSetCaptchaElem();
this.scriptService.registerCaptchaScript({
useGlobalDomain: this.useGlobalDomain,
useEnterprise: this.useEnterprise,
}, "explicit", (grecaptcha) => {
this.onloadCallback(grecaptcha);
}, this.hl);
}
/**
* Called when google's recaptcha script is ready
*/
onloadCallback(grecapcha) {
// assign reference to reCaptcha Api once its loaded
this.reCaptchaApi = grecapcha;
if (!this.reCaptchaApi) {
throw Error(`ReCaptcha Api was not initialized correctly`);
}
// loaded flag
this.isLoaded = true;
// fire load event
this.load.next();
// render captcha
this.renderReCaptcha();
// setup component if it was flagged as such
if (this.setupAfterLoad) {
this.setupAfterLoad = false;
this.setupComponent();
}
}
generateNewElemId() {
return this.captchaElemPrefix + this.getPseudoUniqueNumber();
}
createAndSetCaptchaElem() {
// generate new captcha id
this.captchaElemId = this.generateNewElemId();
if (!this.captchaElemId) {
throw Error(`Captcha elem Id is not set`);
}
if (!this.captchaWrapperElem) {
throw Error(`Captcha DOM element is not initialized`);
}
// remove old html
this.captchaWrapperElem.nativeElement.innerHTML = "";
// create new wrapper for captcha
const newElem = this.renderer.createElement("div");
newElem.id = this.captchaElemId;
this.renderer.appendChild(this.captchaWrapperElem.nativeElement, newElem);
// when use captcha in cdk stepper then throwing error Captcha element with id 'ngx_captcha_id_XXXX' not found
// to fix it checking ensureCaptchaElem in timeout so that its check in next call and its able to find element
setTimeout(() => {
// update captcha elem
if (this.captchaElemId) {
this.ensureCaptchaElem(this.captchaElemId);
}
}, 0);
}
/**
* To be aligned with the ControlValueAccessor interface we need to implement this method
* However as we don't want to update the recaptcha, this doesn't need to be implemented
*/
writeValue(obj) { }
/**
* This method helps us tie together recaptcha and our formControl values
*/
registerOnChange(fn) {
this.onChange = fn;
}
/**
* At some point we might be interested whether the user has touched our component
*/
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* Handles error callback
*/
handleErrorCallback() {
this.zone.run(() => {
this.onChange(undefined);
this.onTouched(undefined);
});
this.error.next();
}
/**
* Handles expired callback
*/
handleExpireCallback() {
this.expire.next();
// reset captcha on expire callback
this.resetCaptcha();
}
}
/** @nocollapse */ BaseReCaptchaComponentDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: BaseReCaptchaComponentDirective, deps: [{ token: i0.Renderer2 }, { token: i0.NgZone }, { token: i0.Injector }, { token: i1.ScriptService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Directive });
/** @nocollapse */ BaseReCaptchaComponentDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.4", type: BaseReCaptchaComponentDirective, inputs: { siteKey: "siteKey", useGlobalDomain: "useGlobalDomain", useEnterprise: "useEnterprise", type: "type", hl: "hl", tabIndex: "tabIndex" }, outputs: { success: "success", load: "load", reset: "reset", ready: "ready", error: "error", expire: "expire" }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: BaseReCaptchaComponentDirective, decorators: [{
type: Directive
}], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.NgZone }, { type: i0.Injector }, { type: i1.ScriptService }, { type: Object, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; }, propDecorators: { siteKey: [{
type: Input
}], useGlobalDomain: [{
type: Input
}], useEnterprise: [{
type: Input
}], type: [{
type: Input
}], hl: [{
type: Input
}], tabIndex: [{
type: Input
}], success: [{
type: Output
}], load: [{
type: Output
}], reset: [{
type: Output
}], ready: [{
type: Output
}], error: [{
type: Output
}], expire: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZS1yZS1jYXB0Y2hhLWNvbXBvbmVudC5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvYmFzZS1yZS1jYXB0Y2hhLWNvbXBvbmVudC5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDcEQsT0FBTyxFQUdMLFNBQVMsRUFFVCxZQUFZLEVBQ1osTUFBTSxFQUNOLFdBQVcsRUFDWCxRQUFRLEVBQ1IsS0FBSyxFQUNMLE1BQU0sRUFFTixNQUFNLEVBQ04sV0FBVyxFQUNYLFNBQVMsR0FFVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBR0wsU0FBUyxHQUNWLE1BQU0sZ0JBQWdCLENBQUM7QUFHeEIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDOzs7QUFHM0QsTUFBTSxPQUFnQiwrQkFBK0I7SUFnSW5ELFlBQ1ksUUFBbUIsRUFDbkIsSUFBWSxFQUNaLFFBQWtCLEVBQ2xCLGFBQTRCLEVBQ1AsVUFBa0I7UUFKdkMsYUFBUSxHQUFSLFFBQVEsQ0FBVztRQUNuQixTQUFJLEdBQUosSUFBSSxDQUFRO1FBQ1osYUFBUSxHQUFSLFFBQVEsQ0FBVTtRQUNsQixrQkFBYSxHQUFiLGFBQWEsQ0FBZTtRQUNQLGVBQVUsR0FBVixVQUFVLENBQVE7UUFsSW5EOztXQUVHO1FBQ2dCLHNCQUFpQixHQUFHLGlCQUFpQixDQUFDO1FBRWpELGlCQUFZLEdBQVksSUFBSSxDQUFDO1FBUXJDOztXQUVHO1FBQ00sb0JBQWUsR0FBWSxLQUFLLENBQUM7UUFFakMsa0JBQWEsR0FBWSxLQUFLLENBQUM7UUFFeEM7O1dBRUc7UUFDTSxTQUFJLEdBQXNCLE9BQU8sQ0FBQztRQU8zQzs7V0FFRztRQUNNLGFBQVEsR0FBRyxDQUFDLENBQUM7UUFFdEI7OztXQUdHO1FBQ08sWUFBTyxHQUFH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QkFBZCxNQUFNO2dCQUtHLE1BQU07c0JBQWYsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGlzUGxhdGZvcm1Ccm93c2VyIH0gZnJvbSBcIkBhbmd1bGFyL2NvbW1vblwiO1xyXG5pbXBvcnQge1xyXG4gIEFmdGVyVmlld0NoZWNrZWQsXHJcbiAgQWZ0ZXJWaWV3SW5pdCxcclxuICBEaXJlY3RpdmUsXHJcbiAgRWxlbWVudFJlZixcclxuICBFdmVudEVtaXR0ZXIsXHJcbiAgSW5qZWN0LFxyXG4gIEluamVjdEZsYWdzLFxyXG4gIEluamVjdG9yLFxyXG4gIElucHV0LFxyXG4gIE5nWm9uZSxcclxuICBPbkNoYW5nZXMsXHJcbiAgT3V0cHV0LFxyXG4gIFBMQVRGT1JNX0lELFxyXG4gIFJlbmRlcmVyMixcclxuICBTaW1wbGVDaGFuZ2VzLFxyXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XHJcbmltcG9ydCB7XHJcbiAgQWJzdHJhY3RDb250cm9sLFxyXG4gIENvbnRyb2xWYWx1ZUFjY2Vzc29yLFxyXG4gIE5nQ29udHJvbCxcclxufSBmcm9tIFwiQGFuZ3VsYXIvZm9ybXNcIjtcclxuXHJcbmltcG9ydCB7IFJlQ2FwdGNoYVR5cGUgfSBmcm9tIFwiLi4vbW9kZWxzL3JlY2FwdGNoYS10eXBlLmVudW1cIjtcclxuaW1wb3J0IHsgU2NyaXB0U2VydmljZSB9IGZyb20gXCIuLi9zZXJ2aWNlcy9zY3JpcHQuc2VydmljZVwiO1xyXG5cclxuQERpcmVjdGl2ZSgpXHJcbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNlUmVDYXB0Y2hhQ29tcG9uZW50RGlyZWN0aXZlXHJcbiAgaW1wbGVtZW50cyBPbkNoYW5nZXMsIENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBBZnRlclZpZXdJbml0LCBBZnRlclZpZXdDaGVja2VkXHJcbntcclxuICAvKipcclxuICAgKiBQcmVmaXggb2YgdGhlIGNhcHRjaGEgZWxlbWVudFxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCByZWFkb25seSBjYXB0Y2hhRWxlbVByZWZpeCA9IFwibmd4X2NhcHRjaGFfaWRfXCI7XHJcblxyXG4gIHByaXZhdGUgc2V0dXBDYXB0Y2hhOiBib29sZWFuID0gdHJ1ZTtcclxuXHJcbiAgLyoqXHJcbiAgICogR29vZ2xlJ3Mgc2l0ZSBrZXkuXHJcbiAgICogWW91IGNhbiBmaW5kIHRoaXMgdW5kZXIgaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9yZWNhcHRjaGFcclxuICAgKi9cclxuICBASW5wdXQoKSBzaXRlS2V5Pzogc3RyaW5nO1xyXG5cclxuICAvKipcclxuICAgKiBJbmRpY2F0ZXMgaWYgZ2xvYmFsIGRvbWFpbiAncmVjYXB0Y2hhLm5ldCcgc2hvdWxkIGJlIHVzZWQgaW5zdGVhZCBvZiBkZWZhdWx0IGRvbWFpbiAoJ2dvb2dsZS5jb20nKVxyXG4gICAqL1xyXG4gIEBJbnB1dCgpIHVzZUdsb2JhbERvbWFpbjogYm9vbGVhbiA9IGZhbHNlO1xyXG5cclxuICBASW5wdXQoKSB1c2VFbnRlcnByaXNlOiBib29sZWFuID0gZmFsc2U7XHJcblxyXG4gIC8qKlxyXG4gICAqIFR5cGVcclxuICAgKi9cclxuICBASW5wdXQoKSB0eXBlOiBcImF1ZGlvXCIgfCBcImltYWdlXCIgPSBcImltYWdlXCI7XHJcblxyXG4gIC8qKlxyXG4gICAqIExhbmd1YWdlIGNvZGUuIEF1dG8tZGV0ZWN0cyB0aGUgdXNlcidzIGxhbmd1YWdlIGlmIHVuc3BlY2lmaWVkLlxyXG4gICAqL1xyXG4gIEBJbnB1dCgpIGhsPzogc3RyaW5nO1xyXG5cclxuICAvKipcclxuICAgKiBUYWIgaW5kZXhcclxuICAgKi9cclxuICBASW5wdXQoKSB0YWJJbmRleCA9IDA7XHJcblxyXG4gIC8qKlxyXG4gICAqIENhbGxlZCB3aGVuIGNhcHRjaGEgcmVjZWl2ZXMgc3VjY2Vzc2Z1bCByZXNwb25zZS5cclxuICAgKiBDYXB0Y2hhIHJlc3BvbnNlIHRva2VuIGlzIHBhc3NlZCB0byBldmVudC5cclxuICAgKi9cclxuICBAT3V0cHV0KCkgc3VjY2VzcyA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xyXG5cclxuICAvKipcclxuICAgKiBDYWxsZWQgd2hlbiBjYXB0Y2hhIGlzIGxvYWRlZC4gRXZlbnQgcmVjZWl2ZXMgaWQgb2YgdGhlIGNhcHRjaGFcclxuICAgKi9cclxuICBAT3V0cHV0KCkgbG9hZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQ2FsbGVkIHdoZW4gY2FwdGNoYSBpcyByZXNldC5cclxuICAgKi9cclxuICBAT3V0cHV0KCkgcmVzZXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIENhbGxlZCB3aGVuIGNhcHRjaGEgaXMgbG9hZGVkICYgcmVhZHkuIEkuZS4gd2hlbiB5b3UgbmVlZCB0byBleGVjdXRlIGNhcHRjaGEgb24gY29tcG9uZW50IGxvYWQuXHJcbiAgICovXHJcbiAgQE91dHB1dCgpIHJlYWR5ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG5cclxuICAvKipcclxuICAgKiBFcnJvciBjYWxsYmFja1xyXG4gICAqL1xyXG4gIEBPdXRwdXQoKSBlcnJvciA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuXHJcbiAgLyoqXHJcbiAgICogRXhwaXJlZCBjYWxsYmFja1xyXG4gICAqL1xyXG4gIEBPdXRwdXQoKSBleHBpcmUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XHJcblxyXG4gIGFic3RyYWN0IGNhcHRjaGFXcmFwcGVyRWxlbT86IEVsZW1lbnRSZWY7XHJcblxyXG4gIC8qKlxyXG4gICAqIEluZGljYXRlcyBpZiBjYXB0Y2hhIHNob3VsZCBiZSBzZXQgb24gbG9hZFxyXG4gICAqL1xyXG4gIHByaXZhdGUgc2V0dXBBZnRlckxvYWQgPSBmYWxzZTtcclxuXHJcbiAgLyoqXHJcbiAgICogQ2FwdGNoYSBlbGVtZW50XHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGNhcHRjaGFFbGVtPzogSFRNTEVsZW1lbnQ7XHJcblxyXG4gIC8qKlxyXG4gICAqIElkIG9mIHRoZSBjYXB0Y2hhIGVsZW1cclxuICAgKi9cclxuICBwcm90ZWN0ZWQgY2FwdGNoYUlkPzogbnVtYmVyO1xyXG5cclxuICAvKipcclxuICAgKiBIb2xkcyBsYXN0IHJlc3BvbnNlIHZhbHVlXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGN1cnJlbnRSZXNwb25zZT86IHN0cmluZztcclxuXHJcbiAgLyoqXHJcbiAgICogSWYgZW5hYmxlZCwgY2FwdGNoYSB3aWxsIHJlc2V0IGFmdGVyIHJlY2VpdmluZyBzdWNjZXNzIHJlc3BvbnNlLiBUaGlzIGlzIHVzZWZ1bFxyXG4gICAqIHdoZW4gaW52aXNpYmxlIGNhcHRjaGEgbmVlZCB0byBiZSByZXNvbHZlZCBtdWx0aXBsZSB0aW1lcyBvbiBzYW1lIHBhZ2VcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgcmVzZXRDYXB0Y2hhQWZ0ZXJTdWNjZXNzID0gZmFsc2U7XHJcblxyXG4gIC8qKlxyXG4gICAqIENhcHRjaGEgdHlwZVxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWNhcHRjaGFUeXBlOiBSZUNhcHRjaGFUeXBlO1xyXG5cclxuICAvKipcclxuICAgKiBSZXF1aXJlZCBieSBDb250cm9sVmFsdWVBY2Nlc3NvclxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBvbkNoYW5nZTogKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpID0+IHZvaWQgPSAodmFsKSA9PiB7fTtcclxuICBwcm90ZWN0ZWQgb25Ub3VjaGVkOiAodmFsdWU6IHN0cmluZyB8IHVuZGVmaW5lZCkgPT4gdm9pZCA9ICh2YWwpID0+IHt9O1xyXG5cclxuICAvKipcclxuICAgKiBJbmRpY2F0ZXMgaWYgY2FwdGNoYSBpcyBsb2FkZWRcclxuICAgKi9cclxuICBwdWJsaWMgaXNMb2FkZWQgPSBmYWxzZTtcclxuXHJcbiAgLyoqXHJcbiAgICogUmVmZXJlbmNlIHRvIGdsb2JhbCByZUNhcHRjaGEgQVBJXHJcbiAgICovXHJcbiAgcHVibGljIHJlQ2FwdGNoYUFwaT86IGFueTtcclxuXHJcbiAgLyoqXHJcbiAgICogSWQgb2YgdGhlIERPTSBlbGVtZW50IHdyYXBwaW5nIGNhcHRjaGFcclxuICAgKi9cclxuICBwdWJsaWMgY2FwdGNoYUVsZW1JZD86IHN0cmluZztcclxuXHJcbiAgLyoqXHJcbiAgICogRm9ybSBDb250cm9sIHRvIGJlIGVuYWJsZSB1c2FnZSBpbiByZWFjdGl2ZSBmb3Jtc1xyXG4gICAqL1xyXG4gIHB1YmxpYyBjb250cm9sPzogQWJzdHJhY3RDb250cm9sIHwgbnVsbDtcclxuXHJcbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKFxyXG4gICAgcHJvdGVjdGVkIHJlbmRlcmVyOiBSZW5kZXJlcjIsXHJcbiAgICBwcm90ZWN0ZWQgem9uZTogTmdab25lLFxyXG4gICAgcHJvdGVjdGVkIGluamVjdG9yOiBJbmplY3RvcixcclxuICAgIHByb3RlY3RlZCBzY3JpcHRTZXJ2aWNlOiBTY3JpcHRTZXJ2aWNlLFxyXG4gICAgQEluamVjdChQTEFURk9STV9JRCkgcHJvdGVjdGVkIHBsYXRmb3JtSWQ6IE9iamVjdFxyXG4gICkge31cclxuXHJcbiAgbmdBZnRlclZpZXdJbml0KCkge1xyXG4gICAgdGhpcy5jb250cm9sID0gdGhpcy5pbmplY3Rvci5nZXQ8TmdDb250cm9sIHwgdW5kZWZpbmVkPihcclxuICAgICAgTmdDb250cm9sLFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIEluamVjdEZsYWdzLk9wdGlvbmFsXHJcbiAgICApPy5jb250cm9sO1xyXG4gIH1cclxuXHJcbiAgbmdBZnRlclZpZXdDaGVja2VkKCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuc2V0dXBDYXB0Y2hhKSB7XHJcbiAgICAgIHRoaXMuc2V0dXBDYXB0Y2hhID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuc2V0dXBDb21wb25lbnQoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldHMgcmVDYXB0Y2hhIHByb3BlcnRpZXNcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0Q2FwdGNoYVByb3BlcnRpZXMoKTogYW55O1xyXG5cclxuICAvKipcclxuICAgKiBVc2VkIGZvciBjYXB0Y2hhIHNwZWNpZmljIHNldHVwXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNhcHRjaGFTcGVjaWZpY1NldHVwKCk6IHZvaWQ7XHJcblxyXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcclxuICAgIC8vIGNsZWFudXAgc2NyaXB0cyBpZiBsYW5ndWFnZSBjaGFuZ2VkIGJlY2F1c2UgdGhleSBuZWVkIHRvIGJlIHJlbG9hZGVkXHJcbiAgICBpZiAoY2hhbmdlcyAmJiBjaGFuZ2VzLmhsKSB7XHJcbiAgICAgIC8vIGNsZWFudXAgc2NyaXB0cyB3aGVuIGxhbmd1YWdlIGNoYW5nZXNcclxuICAgICAgaWYgKFxyXG4gICAgICAgICFjaGFuZ2VzLmhsLmZpcnN0Q2hhbmdlICYmXHJcbiAgICAgICAgY2hhbmdlcy5obC5jdXJyZW50VmFsdWUgIT09IGNoYW5nZXMuaGwucHJldmlvdXNWYWx1ZVxyXG4gICAgICApIHtcclxuICAgICAgICB0aGlzLnNjcmlwdFNlcnZpY2UuY2xlYW51cCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGNoYW5nZXMgJiYgY2hhbmdlcy51c2VHbG9iYWxEb21haW4pIHtcclxuICAgICAgLy8gY2xlYW51cCBzY3JpcHRzIHdoZW4gZG9tYWluIGNoYW5nZXNcclxuICAgICAgaWYgKFxyXG4gICAgICAgICFjaGFuZ2VzLnVzZUdsb2JhbERvbWFpbi5maXJzdENoYW5nZSAmJlxyXG4gICAgICAgIGNoYW5nZXMudXNlR2xvYmFsRG9tYWluLmN1cnJlbnRWYWx1ZSAhPT1cclxuICAgICAgICAgIGNoYW5nZXMudXNlR2xvYmFsRG9tYWluLnByZXZpb3VzVmFsdWVcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhpcy5zY3JpcHRTZXJ2aWNlLmNsZWFudXAoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuc2V0dXBDYXB0Y2hhID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldHMgY2FwdGNoYSByZXNwb25zZSBhcyBwZXIgcmVDYXB0Y2hhIGRvY3NcclxuICAgKi9cclxuICBnZXRSZXNwb25zZSgpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHRoaXMucmVDYXB0Y2hhQXBpLmdldFJlc3BvbnNlKHRoaXMuY2FwdGNoYUlkKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldHMgSWQgb2YgY2FwdGNoYSB3aWRnZXRcclxuICAgKi9cclxuICBnZXRDYXB0Y2hhSWQoKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcclxuICAgIHJldHVybiB0aGlzLmNhcHRjaGFJZDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlc2V0cyBjYXB0Y2hhXHJcbiAgICovXHJcbiAgcmVzZXRDYXB0Y2hhKCk6IHZvaWQge1xyXG4gICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XHJcbiAgICAgIC8vIHJlc2V0IGNhcHRjaGEgdXNpbmcgR29vZ2xlIGpzIGFwaVxyXG4gICAgICB0aGlzLnJlQ2FwdGNoYUFwaS5yZXNldCgpO1xyXG5cclxuICAgICAgLy8gcmVxdWlyZWQgZHVlIHRvIGZvcm1zXHJcbiAgICAgIHRoaXMub25DaGFuZ2UodW5kZWZpbmVkKTtcclxuICAgICAgdGhpcy5vblRvdWNoZWQodW5kZWZpbmVkKTtcclxuXHJcbiAgICAgIC8vIHRyaWdnZXIgcmVzZXQgZXZlbnRcclxuICAgICAgdGhpcy5yZXNldC5uZXh0KCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldHMgbGFzdCBzdWJtaXR0ZWQgY2FwdGNoYSByZXNwb25zZVxyXG4gICAqL1xyXG4gIGdldEN1cnJlbnRSZXNwb25zZSgpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJlc3BvbnNlO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmVsb2FkIGNhcHRjaGEuIFVzZWZ1bCB3aGVuIHByb3BlcnRpZXMgKGkuZS4gdGhlbWUpIGNoYW5nZWQgYW5kIGNhcHRjaGEgbmVlZCB0byByZWZsZWN0IHRoZW1cclxuICAgKi9cclxuICByZWxvYWRDYXB0Y2hhKCk6IHZvaWQge1xyXG4gICAgdGhpcy5zZXR1cENvbXBvbmVudCgpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIGVuc3VyZUNhcHRjaGFFbGVtKGNhcHRjaGFFbGVtSWQ6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgaWYoaXNQbGF0Zm9ybUJyb3dzZXIodGhpcy5wbGF0Zm9ybUlkKSkge1xyXG4gICAgICBjb25zdCBjYXB0Y2hhRWxlbSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGNhcHRjaGFFbGVtSWQpO1xyXG5cclxuICAgICAgaWYgKCFjYXB0Y2hhRWxlbSkge1xyXG4gICAgICAgIHRocm93IEVycm9yKGBDYXB0Y2hhIGVsZW1lbnQgd2l0aCBpZCAnJHtjYXB0Y2hhRWxlbUlkfScgd2FzIG5vdCBmb3VuZGApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBhc3NpZ24gY2FwdGNoYSBhbGVtXHJcbiAgICAgIHRoaXMuY2FwdGNoYUVsZW0gPSBjYXB0Y2hhRWxlbTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlc3BvbnNpYmxlIGZvciBpbnN0YW50aWF0aW5nIGNhcHRjaGEgZWxlbWVudFxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCByZW5kZXJSZUNhcHRjaGEoKTogdm9pZCB7XHJcbiAgICAvLyBydW4gb3V0c2lkZSBhbmd1bGFyIHpvbmUgZHVlIHRvIHRpbWVvdXQgaXNzdWVzIHdoZW4gdGVzdGluZ1xyXG4gICAgLy8gZGV0YWlsczogaHR0cHM6Ly9naXRodWIuY29tL0VubmdhZ2Uvbmd4LWNhcHRjaGEvaXNzdWVzLzI2XHJcbiAgICB0aGlzLnpvbmUucnVuT3V0c2lkZUFuZ3VsYXIoKCkgPT4ge1xyXG4gICAgICAvLyB0byBmaXggcmVDQVBUQ0hBIHBsYWNlaG9sZGVyIGVsZW1lbnQgbXVzdCBiZSBhbiBlbGVtZW50IG9yIGlkXHJcbiAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9Fbm5nYWdlL25neC1jYXB0Y2hhL2lzc3Vlcy85NlxyXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICB0aGlzLmNhcHRjaGFJZCA9IHRoaXMucmVDYXB0Y2hhQXBpLnJlbmRlcihcclxuICAgICAgICAgIHRoaXMuY2FwdGNoYUVsZW1JZCxcclxuICAgICAgICAgIHRoaXMuZ2V0Q2FwdGNoYVByb3BlcnRpZXMoKVxyXG4gICAgICAgICk7XHJcbiAgICAgICAgdGhpcy5yZWFkeS5uZXh0KCk7XHJcbiAgICAgIH0sIDApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBDYWxsZWQgd2hlbiBjYXB0Y2hhIHJlY2VpdmVzIHJlc3BvbnNlXHJcbiAgICogQHBhcmFtIGNhbGxiYWNrIENhbGxiYWNrXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGhhbmRsZUNhbGxiYWNrKGNhbGxiYWNrOiBhbnkpOiB2b2lkIHtcclxuICAgIHRoaXMuY3VycmVudFJlc3BvbnNlID0gY2FsbGJhY2s7XHJcbiAgICB0aGlzLnN1Y2Nlc3MubmV4dChjYWxsYmFjayk7XHJcblxyXG4gICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XHJcbiAgICAgIHRoaXMub25DaGFuZ2UoY2FsbGJhY2spO1xyXG4gICAgICB0aGlzLm9uVG91Y2hlZChjYWxsYmFjayk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBpZiAodGhpcy5yZXNldENhcHRjaGFBZnRlclN1Y2Nlc3MpIHtcclxuICAgICAgdGhpcy5yZXNldENhcHRjaGEoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgZ2V0UHNldWRvVW5pcXVlTnVtYmVyKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gbmV3IERhdGUoKS5nZXRVVENNaWxsaXNlY29uZHMoKSArIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDk5OTkpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBzZXR1cENvbXBvbmVudCgpOiB2b2lkIHtcclxuICAgIC8vIGNhcHRjaGEgc3BlY2lmaWMgc2V0dXBcclxuICAgIHRoaXMuY2FwdGNoYVNwZWNpZmljU2V0dXAoKTtcclxuXHJcbiAgICAvLyBjcmVhdGUgY2FwdGNoYSB3cmFwcGVyXHJcbiAgICB0aGlzLmNyZWF0ZUFuZFNldENhcHRjaGFFbGVtKCk7XHJcblxyXG4gICAgdGhpcy5zY3JpcHRTZXJ2aWNlLnJlZ2lzdGVyQ2FwdGNoYVNjcmlwdChcclxuICAgICAge1xyXG4gICAgICAgIHVzZUdsb2JhbERvbWFpbjogdGhpcy51c2VHbG9iYWxEb21haW4sXHJcbiAgICAgICAgdXNlRW50ZXJwcmlzZTogdGhpcy51c2VFbnRlcnByaXNlLFxyXG4gICAgICB9LFxyXG4gICAgICBcImV4cGxpY2l0XCIsXHJcbiAgICAgIChncmVjYXB0Y2hhKSA9PiB7XHJcbiAgICAgICAgdGhpcy5vbmxvYWRDYWxsYmFjayhncmVjYXB0Y2hhKTtcclxuICAgICAgfSxcclxuICAgICAgdGhpcy5obFxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENhbGxlZCB3aGVuIGdvb2dsZSdzIHJlY2FwdGNoYSBzY3JpcHQgaXMgcmVhZHlcclxuICAgKi9cclxuICBwcml2YXRlIG9ubG9hZENhbGxiYWNrKGdyZWNhcGNoYTogYW55KTogdm9pZCB7XHJcbiAgICAvLyBhc3NpZ24gcmVmZXJlbmNlIHRvIHJlQ2FwdGNoYSBBcGkgb25jZSBpdHMgbG9hZGVkXHJcbiAgICB0aGlzLnJlQ2FwdGNoYUFwaSA9IGdyZWNhcGNoYTtcclxuXHJcbiAgICBpZiAoIXRoaXMucmVDYXB0Y2hhQXBpKSB7XHJcbiAgICAgIHRocm93IEVycm9yKGBSZUNhcHRjaGEgQXBpIHdhcyBub3QgaW5pdGlhbGl6ZWQgY29ycmVjdGx5YCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gbG9hZGVkIGZsYWdcclxuICAgIHRoaXMuaXNMb2FkZWQgPSB0cnVlO1xyXG5cclxuICAgIC8vIGZpcmUgbG9hZCBldmVudFxyXG4gICAgdGhpcy5sb2FkLm5leHQoKTtcclxuXHJcbiAgICAvLyByZW5kZXIgY2FwdGNoYVxyXG4gICAgdGhpcy5yZW5kZXJSZUNhcHRjaGEoKTtcclxuXHJcbiAgICAvLyBzZXR1cCBjb21wb25lbnQgaWYgaXQgd2FzIGZsYWdnZWQgYXMgc3VjaFxyXG4gICAgaWYgKHRoaXMuc2V0dXBBZnRlckxvYWQpIHtcclxuICAgICAgdGhpcy5zZXR1cEFmdGVyTG9hZCA9IGZhbHNlO1xyXG4gICAgICB0aGlzLnNldHVwQ29tcG9uZW50KCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGdlbmVyYXRlTmV3RWxlbUlkKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gdGhpcy5jYXB0Y2hhRWxlbVByZWZpeCArIHRoaXMuZ2V0UHNldWRvVW5pcXVlTnVtYmVyKCk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGNyZWF0ZUFuZFNldENhcHRjaGFFbGVtKCk6IHZvaWQge1xyXG4gICAgLy8gZ2VuZXJhdGUgbmV3IGNhcHRjaGEgaWRcclxuICAgIHRoaXMuY2FwdGNoYUVsZW1JZCA9IHRoaXMuZ2VuZXJhdGVOZXdFbGVtSWQoKTtcclxuXHJcbiAgICBpZiAoIXRoaXMuY2FwdGNoYUVsZW1JZCkge1xyXG4gICAgICB0aHJvdyBFcnJvcihgQ2FwdGNoYSBlbGVtIElkIGlzIG5vdCBzZXRgKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIXRoaXMuY2FwdGNoYVdyYXBwZXJFbGVtKSB7XHJcbiAgICAgIHRocm93IEVycm9yKGBDYXB0Y2hhIERPTSBlbGVtZW50IGlzIG5vdCBpbml0aWFsaXplZGApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIHJlbW92ZSBvbGQgaHRtbFxyXG4gICAgdGhpcy5jYXB0Y2hhV3JhcHBlckVsZW0ubmF0aXZlRWxlbWVudC5pbm5lckhUTUwgPSBcIlwiO1xyXG5cclxuICAgIC8vIGNyZWF0ZSBuZXcgd3JhcHBlciBmb3IgY2FwdGNoYVxyXG4gICAgY29uc3QgbmV3RWxlbSA9IHRoaXMucmVuZGVyZXIuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcclxuICAgIG5ld0VsZW0uaWQgPSB0aGlzLmNhcHRjaGFFbGVtSWQ7XHJcblxyXG4gICAgdGhpcy5yZW5kZXJlci5hcHBlbmRDaGlsZCh0aGlzLmNhcHRjaGFXcmFwcGVyRWxlbS5uYXRpdmVFbGVtZW50LCBuZXdFbGVtKTtcclxuXHJcbiAgICAvLyB3aGVuIHVzZSBjYXB0Y2hhIGluIGNkayBzdGVwcGVyIHRoZW4gdGhyb3dpbmcgZXJyb3IgQ2FwdGNoYSBlbGVtZW50IHdpdGggaWQgJ25neF9jYXB0Y2hhX2lkX1hYWFgnIG5vdCBmb3VuZFxyXG4gICAgLy8gdG8gZml4IGl0IGNoZWNraW5nIGVuc3VyZUNhcHRjaGFFbGVtIGluIHRpbWVvdXQgc28gdGhhdCBpdHMgY2hlY2sgaW4gbmV4dCBjYWxsIGFuZCBpdHMgYWJsZSB0byBmaW5kIGVsZW1lbnRcclxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAvLyB1cGRhdGUgY2FwdGNoYSBlbGVtXHJcbiAgICAgIGlmICh0aGlzLmNhcHRjaGFFbGVtSWQpIHtcclxuICAgICAgICB0aGlzLmVuc3VyZUNhcHRjaGFFbGVtKHRoaXMuY2FwdGNoYUVsZW1JZCk7XHJcbiAgICAgIH1cclxuICAgIH0sIDApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVG8gYmUgYWxpZ25lZCB3aXRoIHRoZSBDb250cm9sVmFsdWVBY2Nlc3NvciBpbnRlcmZhY2Ugd2UgbmVlZCB0byBpbXBsZW1lbnQgdGhpcyBtZXRob2RcclxuICAgKiBIb3dldmVyIGFzIHdlIGRvbid0IHdhbnQgdG8gdXBkYXRlIHRoZSByZWNhcHRjaGEsIHRoaXMgZG9lc24ndCBuZWVkIHRvIGJlIGltcGxlbWVudGVkXHJcbiAgICovXHJcbiAgcHVibGljIHdyaXRlVmFsdWUob2JqOiBhbnkpOiB2b2lkIHt9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGhlbHBzIHVzIHRpZSB0b2dldGhlciByZWNhcHRjaGEgYW5kIG91ciBmb3JtQ29udHJvbCB2YWx1ZXNcclxuICAgKi9cclxuICBwdWJsaWMgcmVnaXN0ZXJPbkNoYW5nZShmbjogYW55KTogdm9pZCB7XHJcbiAgICB0aGlzLm9uQ2hhbmdlID0gZm47XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBBdCBzb21lIHBvaW50IHdlIG1pZ2h0IGJlIGludGVyZXN0ZWQgd2hldGhlciB0aGUgdXNlciBoYXMgdG91Y2hlZCBvdXIgY29tcG9uZW50XHJcbiAgICovXHJcbiAgcHVibGljIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiBhbnkpOiB2b2lkIHtcclxuICAgIHRoaXMub25Ub3VjaGVkID0gZm47XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGVzIGVycm9yIGNhbGxiYWNrXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGhhbmRsZUVycm9yQ2FsbGJhY2soKTogdm9pZCB7XHJcbiAgICB0aGlzLnpvbmUucnVuKCgpID0+IHtcclxuICAgICAgdGhpcy5vbkNoYW5nZSh1bmRlZmluZWQpO1xyXG4gICAgICB0aGlzLm9uVG91Y2hlZCh1bmRlZmluZWQpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5lcnJvci5uZXh0KCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGVzIGV4cGlyZWQgY2FsbGJhY2tcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgaGFuZGxlRXhwaXJlQ2FsbGJhY2soKTogdm9pZCB7XHJcbiAgICB0aGlzLmV4cGlyZS5uZXh0KCk7XHJcblxyXG4gICAgLy8gcmVzZXQgY2FwdGNoYSBvbiBleHBpcmUgY2FsbGJhY2tcclxuICAgIHRoaXMucmVzZXRDYXB0Y2hhKCk7XHJcbiAgfVxyXG59XHJcbiJdfQ==