ngx-teximate
Version:
Angular text animations component
382 lines (374 loc) • 31.6 kB
JavaScript
import { Component, Input, Output, NgZone, ElementRef, EventEmitter, ChangeDetectionStrategy, Directive, Inject, forwardRef, NgModule } from '@angular/core';
import { style, query, stagger, useAnimation, AnimationBuilder } from '@angular/animations';
import { BehaviorSubject, Subscription, fromEvent, merge, of } from 'rxjs';
import { map, filter, switchMap, tap } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class Teximate {
/**
* @param {?} animationBuilder
* @param {?} zone
* @param {?} el
*/
constructor(animationBuilder, zone, el) {
this.animationBuilder = animationBuilder;
this.zone = zone;
this.el = el;
/**
* Stream that emits when animation is started
*/
this.playEmitter = new EventEmitter();
/**
* Stream that emits when animation is done
*/
this.finishEmitter = new EventEmitter();
/**
* Teximate animations
*/
this.players = new Map();
/**
* Teximate state
*/
this._state = new BehaviorSubject('');
this.state = this._state.pipe(map((text) => teximateFactory(text)));
}
/**
* Set animated text
* @param {?} text
* @return {?}
*/
set setText(text) {
this._state.next(text);
}
/**
* @return {?}
*/
get isPlaying() {
return this._isPlaying;
}
/**
* @return {?}
*/
get enterPlayer() {
return this.players.get('enter');
}
/**
* @return {?}
*/
get leavePlayer() {
return this.players.get('leave');
}
/**
* @return {?}
*/
get defaultPlayer() {
return this.players.get('default');
}
/**
* @return {?}
*/
ngAfterViewInit() {
this._isViewInit = true;
this.updateAnimations(true);
}
/**
* @return {?}
*/
ngOnChanges() {
if (this._isViewInit) {
this.updateAnimations();
}
}
/**
* @return {?}
*/
ngOnDestroy() {
// TODO: Use players.forEach to destroy players
if (this.players.has('enter')) {
this.players.get('enter').destroy();
}
if (this.players.has('leave')) {
this.players.get('leave').destroy();
}
if (this.players.has('default')) {
this.players.get('default').destroy();
}
}
/**
* Register a new animation
* @param {?} config
* @return {?}
*/
registerAnimation(config) {
/** @type {?} */
const player = this.buildAnimation(config).create(this.el.nativeElement);
/** TODO: Investigate why onStart and onDone fire only once */
player.onStart(() => {
this._isPlaying = true;
this.playEmitter.emit(config.id);
});
player.onDone(() => {
this._isPlaying = false;
this.finishEmitter.emit(config.id);
});
return this.players.set(config.id, player).get(config.id);
}
/**
* @param {?=} autoPlayEnter
* @return {?}
*/
updateAnimations(autoPlayEnter) {
this.zone.runOutsideAngular(() => {
if (this.enter) {
/** @type {?} */
const enterPlayer = this.registerAnimation(Object.assign({}, this.enter, { id: 'enter', isEnter: true }));
if (autoPlayEnter) {
enterPlayer.play();
}
}
if (this.leave) {
this.registerAnimation(Object.assign({}, this.leave, { id: 'leave' }));
}
if (this.animation) {
this.registerAnimation(Object.assign({ id: 'default' }, this.animation));
}
});
}
/**
* Build animation
* @param {?} config
* @return {?}
*/
buildAnimation(config) {
/** TODO: Use ':enter' and ':leave' for enter and leave animations */
return this.animationBuilder.build([
query(`.teximate-${config.type}`, [
// This is a workaround for enter animation to work
style({ opacity: config.isEnter ? 0 : 1 }),
stagger(config.delay, [useAnimation(config.animation)])
])
]);
}
}
Teximate.decorators = [
{ type: Component, args: [{
selector: 'teximate',
host: {
'aria-label': 'text'
},
template: "<p *ngFor=\"let paragraph of state | async; index as i\"\r\n class=\"teximate-paragraph teximate-paragraph-{{i}}\">\r\n\r\n <span *ngFor=\"let word of paragraph; index as j\"\r\n class=\"teximate-word teximate-word-{{j}}\">\r\n\r\n <span *ngFor=\"let letter of word; index as k\"\r\n class=\"teximate-letter teximate-letter-{{k}}\">\r\n {{letter}}\r\n </span>\r\n </span>\r\n</p>\r\n",
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [".teximate-letter,.teximate-word{display:inline-block}.teximate-word{margin-right:8px}"]
}] }
];
/** @nocollapse */
Teximate.ctorParameters = () => [
{ type: AnimationBuilder },
{ type: NgZone },
{ type: ElementRef }
];
Teximate.propDecorators = {
setText: [{ type: Input, args: ['text',] }],
enter: [{ type: Input }],
leave: [{ type: Input }],
animation: [{ type: Input }],
playEmitter: [{ type: Output, args: ['play',] }],
finishEmitter: [{ type: Output, args: ['finish',] }]
};
/**
* Convert text string into a workable text
* @param {?} text
* @return {?}
*/
function teximateFactory(text) {
/** @type {?} */
const paragraphs = [];
// Split text into paragraphs
text.split('\n').map((paragraph) => {
/** @type {?} */
const words = [];
// Split paragraph into words
paragraph
.split(' ')
.filter(word => word !== '')
.map((word) =>
// Split word into letters
words.push(word.split(/(?!$)/u)));
paragraphs.push(words);
});
return paragraphs;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TeximateEvents {
/**
* @param {?} element
* @param {?} teximate
*/
constructor(element, teximate) {
this.element = element;
this.teximate = teximate;
this.events = Subscription.EMPTY;
this.click = new EventEmitter();
this.hover = new EventEmitter();
}
/**
* @return {?}
*/
ngAfterViewInit() {
this.events = this.teximate.state.pipe(filter(() => !!this.click.observers.length || !!this.hover.observers.length), switchMap(() => {
/** @type {?} */
let clickEvents = of(null);
/** @type {?} */
let hoverEvents = of(null);
/** @type {?} */
const words = this.element.nativeElement.querySelectorAll(this.selector);
if (this.click.observers.length) {
/** @type {?} */
const events = Array.from(words).map((el) => fromEvent(el, 'click'));
clickEvents = merge(...events).pipe(tap(e => this.click.emit(e)));
}
if (this.hover.observers.length) {
/** @type {?} */
const events = Array.from(words).map((el) => fromEvent(el, 'mouseenter'));
hoverEvents = merge(...events).pipe(tap(e => this.hover.emit(e)));
}
return merge(clickEvents, hoverEvents);
})).subscribe();
}
/**
* @return {?}
*/
ngOnDestroy() {
this.events.unsubscribe();
}
}
/** @nocollapse */
TeximateEvents.ctorParameters = () => [
{ type: ElementRef },
{ type: Teximate, decorators: [{ type: Inject, args: [forwardRef(() => Teximate),] }] }
];
TeximateEvents.propDecorators = {
click: [{ type: Output, args: ['',] }],
hover: [{ type: Output, args: ['',] }]
};
class ParagraphEvents extends TeximateEvents {
/**
* @param {?} element
* @param {?} teximate
*/
constructor(element, teximate) {
super(element, teximate);
this.click = new EventEmitter();
this.hover = new EventEmitter();
this.selector = '.teximate-paragraph';
}
}
ParagraphEvents.decorators = [
{ type: Directive, args: [{
selector: 'teximate[paragraphClick], teximate[paragraphHover]'
},] }
];
/** @nocollapse */
ParagraphEvents.ctorParameters = () => [
{ type: ElementRef },
{ type: Teximate, decorators: [{ type: Inject, args: [forwardRef(() => Teximate),] }] }
];
ParagraphEvents.propDecorators = {
click: [{ type: Output, args: ['paragraphClick',] }],
hover: [{ type: Output, args: ['paragraphHover',] }]
};
class WordEvents extends TeximateEvents {
/**
* @param {?} element
* @param {?} teximate
*/
constructor(element, teximate) {
super(element, teximate);
this.click = new EventEmitter();
this.hover = new EventEmitter();
this.selector = '.teximate-word';
}
}
WordEvents.decorators = [
{ type: Directive, args: [{
selector: 'teximate[wordClick], teximate[wordHover]'
},] }
];
/** @nocollapse */
WordEvents.ctorParameters = () => [
{ type: ElementRef },
{ type: Teximate, decorators: [{ type: Inject, args: [forwardRef(() => Teximate),] }] }
];
WordEvents.propDecorators = {
click: [{ type: Output, args: ['wordClick',] }],
hover: [{ type: Output, args: ['wordHover',] }]
};
class LetterEvents extends TeximateEvents {
/**
* @param {?} element
* @param {?} teximate
*/
constructor(element, teximate) {
super(element, teximate);
this.click = new EventEmitter();
this.hover = new EventEmitter();
this.selector = '.teximate-letter';
}
}
LetterEvents.decorators = [
{ type: Directive, args: [{
selector: 'teximate[letterClick], teximate[letterHover]'
},] }
];
/** @nocollapse */
LetterEvents.ctorParameters = () => [
{ type: ElementRef },
{ type: Teximate, decorators: [{ type: Inject, args: [forwardRef(() => Teximate),] }] }
];
LetterEvents.propDecorators = {
click: [{ type: Output, args: ['letterClick',] }],
hover: [{ type: Output, args: ['letterHover',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class TeximateModule {
}
TeximateModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule],
declarations: [
Teximate,
ParagraphEvents,
WordEvents,
LetterEvents
],
exports: [
Teximate,
ParagraphEvents,
WordEvents,
LetterEvents
]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
export { teximateFactory, Teximate, TeximateModule, LetterEvents as ɵd, ParagraphEvents as ɵb, TeximateEvents as ɵa, WordEvents as ɵc };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LXRleGltYXRlLmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9uZ3gtdGV4aW1hdGUvbGliL3RleGltYXRlLnRzIiwibmc6Ly9uZ3gtdGV4aW1hdGUvbGliL3RleGltYXRlLWV2ZW50cy50cyIsIm5nOi8vbmd4LXRleGltYXRlL2xpYi90ZXhpbWF0ZS5tb2R1bGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBJbnB1dCxcbiAgT3V0cHV0LFxuICBBZnRlclZpZXdJbml0LFxuICBPbkNoYW5nZXMsXG4gIE9uRGVzdHJveSxcbiAgTmdab25lLFxuICBFbGVtZW50UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5XG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgc3R5bGUsXG4gIHF1ZXJ5LFxuICBzdGFnZ2VyLFxuICB1c2VBbmltYXRpb24sXG4gIEFuaW1hdGlvblBsYXllcixcbiAgQW5pbWF0aW9uQnVpbGRlcixcbiAgQW5pbWF0aW9uRmFjdG9yeVxufSBmcm9tICdAYW5ndWxhci9hbmltYXRpb25zJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgVGV4dEFuaW1hdGlvbiB9IGZyb20gJy4vdGV4aW1hdGUubW9kZWwnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd0ZXhpbWF0ZScsXG4gIGhvc3Q6IHtcbiAgICAnYXJpYS1sYWJlbCc6ICd0ZXh0J1xuICB9LFxuICB0ZW1wbGF0ZVVybDogJy4vdGV4aW1hdGUuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3RleGltYXRlLnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2hcbn0pXG5leHBvcnQgY2xhc3MgVGV4aW1hdGUgaW1wbGVtZW50cyBBZnRlclZpZXdJbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG5cbiAgLyoqIFNldCBhbmltYXRlZCB0ZXh0ICovXG4gIEBJbnB1dCgndGV4dCcpIHNldCBzZXRUZXh0KHRleHQ6IHN0cmluZykge1xuICAgIHRoaXMuX3N0YXRlLm5leHQodGV4dCk7XG4gIH1cblxuICAvKiogQW5pbWF0aW9uIHRoYXQgdHJpZ2dlcnMgb24gaW5pdCAqL1xuICBASW5wdXQoKSBlbnRlcjogVGV4dEFuaW1hdGlvbjtcblxuICAvKiogQW5pbWF0aW9uIHRoYXQgdHJpZ2dlcnMgb24gZGVzdHJveSAqL1xuICBASW5wdXQoKSBsZWF2ZTogVGV4dEFuaW1hdGlvbjtcblxuICAvKiogQW5pbWF0aW9uIHRoYXQgdHJpZ2dlcnMgd2l0aCB0aGUgcGxheSgpIGZ1bmN0aW9uICovXG4gIEBJbnB1dCgpIGFuaW1hdGlvbjogVGV4dEFuaW1hdGlvbjtcblxuICAvKiogU3RyZWFtIHRoYXQgZW1pdHMgd2hlbiBhbmltYXRpb24gaXMgc3RhcnRlZCAqL1xuICBAT3V0cHV0KCdwbGF5JykgcGxheUVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgLyoqIFN0cmVhbSB0aGF0IGVtaXRzIHdoZW4gYW5pbWF0aW9uIGlzIGRvbmUgKi9cbiAgQE91dHB1dCgnZmluaXNoJykgZmluaXNoRW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICAvKiogVGV4aW1hdGUgYW5pbWF0aW9ucyAqL1xuICBwbGF5ZXJzID0gbmV3IE1hcDxzdHJpbmcsIEFuaW1hdGlvblBsYXllcj4oKTtcblxuICAvKiogVGV4aW1hdGUgc3RhdGUgKi9cbiAgcHJpdmF0ZSBfc3RhdGUgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PHN0cmluZz4oJycpO1xuICBzdGF0ZTogT2JzZXJ2YWJsZTxzdHJpbmdbXVtdW10+O1xuXG4gIC8qKiBUZXhpbWF0ZSBwbGF5aW5nIHN0YXRlICovXG4gIHByaXZhdGUgX2lzUGxheWluZzogYm9vbGVhbjtcbiAgcHJpdmF0ZSBfaXNWaWV3SW5pdDogYm9vbGVhbjtcblxuICBnZXQgaXNQbGF5aW5nKCkge1xuICAgIHJldHVybiB0aGlzLl9pc1BsYXlpbmc7XG4gIH1cblxuICBnZXQgZW50ZXJQbGF5ZXIoKTogQW5pbWF0aW9uUGxheWVyIHtcbiAgICByZXR1cm4gdGhpcy5wbGF5ZXJzLmdldCgnZW50ZXInKTtcbiAgfVxuXG4gIGdldCBsZWF2ZVBsYXllcigpOiBBbmltYXRpb25QbGF5ZXIge1xuICAgIHJldHVybiB0aGlzLnBsYXllcnMuZ2V0KCdsZWF2ZScpO1xuICB9XG5cbiAgZ2V0IGRlZmF1bHRQbGF5ZXIoKTogQW5pbWF0aW9uUGxheWVyIHtcbiAgICByZXR1cm4gdGhpcy5wbGF5ZXJzLmdldCgnZGVmYXVsdCcpO1xuICB9XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBhbmltYXRpb25CdWlsZGVyOiBBbmltYXRpb25CdWlsZGVyLCBwcml2YXRlIHpvbmU6IE5nWm9uZSwgcHJpdmF0ZSBlbDogRWxlbWVudFJlZikge1xuICAgIHRoaXMuc3RhdGUgPSB0aGlzLl9zdGF0ZS5waXBlKG1hcCgodGV4dDogc3RyaW5nKSA9PiB0ZXhpbWF0ZUZhY3RvcnkodGV4dCkpKTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICB0aGlzLl9pc1ZpZXdJbml0ID0gdHJ1ZTtcbiAgICB0aGlzLnVwZGF0ZUFuaW1hdGlvbnModHJ1ZSk7XG4gIH1cblxuICBuZ09uQ2hhbmdlcygpIHtcbiAgICBpZiAodGhpcy5faXNWaWV3SW5pdCkge1xuICAgICAgdGhpcy51cGRhdGVBbmltYXRpb25zKCk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgLy8gVE9ETzogVXNlIHBsYXllcnMuZm9yRWFjaCB0byBkZXN0cm95IHBsYXllcnNcbiAgICBpZiAodGhpcy5wbGF5ZXJzLmhhcygnZW50ZXInKSkge1xuICAgICAgdGhpcy5wbGF5ZXJzLmdldCgnZW50ZXInKS5kZXN0cm95KCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnBsYXllcnMuaGFzKCdsZWF2ZScpKSB7XG4gICAgICB0aGlzLnBsYXllcnMuZ2V0KCdsZWF2ZScpLmRlc3Ryb3koKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucGxheWVycy5oYXMoJ2RlZmF1bHQnKSkge1xuICAgICAgdGhpcy5wbGF5ZXJzLmdldCgnZGVmYXVsdCcpLmRlc3Ryb3koKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXIgYSBuZXcgYW5pbWF0aW9uXG4gICAqL1xuICByZWdpc3RlckFuaW1hdGlvbihjb25maWc6IFRleHRBbmltYXRpb24pOiBBbmltYXRpb25QbGF5ZXIge1xuICAgIGNvbnN0IHBsYXllciA9IHRoaXMuYnVpbGRBbmltYXRpb24oY29uZmlnKS5jcmVhdGUodGhpcy5lbC5uYXRpdmVFbGVtZW50KTtcbiAgICAvKiogVE9ETzogSW52ZXN0aWdhdGUgd2h5IG9uU3RhcnQgYW5kIG9uRG9uZSBmaXJlIG9ubHkgb25jZSAqL1xuICAgIHBsYXllci5vblN0YXJ0KCgpID0+IHtcbiAgICAgIHRoaXMuX2lzUGxheWluZyA9IHRydWU7XG4gICAgICB0aGlzLnBsYXlFbWl0dGVyLmVtaXQoY29uZmlnLmlkKTtcbiAgICB9KTtcbiAgICBwbGF5ZXIub25Eb25lKCgpID0+IHtcbiAgICAgIHRoaXMuX2lzUGxheWluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5maW5pc2hFbWl0dGVyLmVtaXQoY29uZmlnLmlkKTtcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5wbGF5ZXJzLnNldChjb25maWcuaWQsIHBsYXllcikuZ2V0KGNvbmZpZy5pZCk7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZUFuaW1hdGlvbnMoYXV0b1BsYXlFbnRlcj86IGJvb2xlYW4pIHtcbiAgICB0aGlzLnpvbmUucnVuT3V0c2lkZUFuZ3VsYXIoKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuZW50ZXIpIHtcbiAgICAgICAgY29uc3QgZW50ZXJQbGF5ZXIgPSB0aGlzLnJlZ2lzdGVyQW5pbWF0aW9uKHsuLi50aGlzLmVudGVyLCBpZDogJ2VudGVyJywgaXNFbnRlcjogdHJ1ZX0pO1xuICAgICAgICBpZiAoYXV0b1BsYXlFbnRlcikge1xuICAgICAgICAgIGVudGVyUGxheWVyLnBsYXkoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKHRoaXMubGVhdmUpIHtcbiAgICAgICAgdGhpcy5yZWdpc3RlckFuaW1hdGlvbih7Li4udGhpcy5sZWF2ZSwgaWQ6ICdsZWF2ZSd9KTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmFuaW1hdGlvbikge1xuICAgICAgICB0aGlzLnJlZ2lzdGVyQW5pbWF0aW9uKHtpZDogJ2RlZmF1bHQnLCAuLi50aGlzLmFuaW1hdGlvbn0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIGFuaW1hdGlvblxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZEFuaW1hdGlvbihjb25maWc6IFRleHRBbmltYXRpb24pOiBBbmltYXRpb25GYWN0b3J5IHtcbiAgICAvKiogVE9ETzogVXNlICc6ZW50ZXInIGFuZCAnOmxlYXZlJyBmb3IgZW50ZXIgYW5kIGxlYXZlIGFuaW1hdGlvbnMgKi9cbiAgICByZXR1cm4gdGhpcy5hbmltYXRpb25CdWlsZGVyLmJ1aWxkKFtcbiAgICAgIHF1ZXJ5KFxuICAgICAgICBgLnRleGltYXRlLSR7Y29uZmlnLnR5cGV9YCxcbiAgICAgICAgW1xuICAgICAgICAgIC8vIFRoaXMgaXMgYSB3b3JrYXJvdW5kIGZvciBlbnRlciBhbmltYXRpb24gdG8gd29ya1xuICAgICAgICAgIHN0eWxlKHtvcGFjaXR5OiBjb25maWcuaXNFbnRlciA/IDAgOiAxfSksXG4gICAgICAgICAgc3RhZ2dlcihjb25maWcuZGVsYXksIFt1c2VBbmltYXRpb24oY29uZmlnLmFuaW1hdGlvbildKVxuICAgICAgICBdXG4gICAgICApXG4gICAgXSk7XG4gIH1cbn1cblxuLyoqIENvbnZlcnQgdGV4dCBzdHJpbmcgaW50byBhIHdvcmthYmxlIHRleHQgKi9cbmV4cG9ydCBmdW5jdGlvbiB0ZXhpbWF0ZUZhY3RvcnkodGV4dDogc3RyaW5nKTogc3RyaW5nW11bXVtdIHtcbiAgY29uc3QgcGFyYWdyYXBoczogc3RyaW5nW11bXVtdID0gW107XG4gIC8vIFNwbGl0IHRleHQgaW50byBwYXJhZ3JhcGhzXG4gIHRleHQuc3BsaXQoJ1xcbicpLm1hcCgocGFyYWdyYXBoOiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCB3b3Jkczogc3RyaW5nW11bXSA9IFtdO1xuICAgIC8vIFNwbGl0IHBhcmFncmFwaCBpbnRvIHdvcmRzXG4gICAgcGFyYWdyYXBoXG4gICAgICAuc3BsaXQoJyAnKVxuICAgICAgLmZpbHRlcih3b3JkID0+IHdvcmQgIT09ICcnKVxuICAgICAgLm1hcCgod29yZDogc3RyaW5nKSA9PlxuICAgICAgICAvLyBTcGxpdCB3b3JkIGludG8gbGV0dGVyc1xuICAgICAgICB3b3Jkcy5wdXNoKHdvcmQuc3BsaXQoLyg/ISQpL3UpKVxuICAgICAgKTtcbiAgICBwYXJhZ3JhcGhzLnB1c2god29yZHMpO1xuICB9KTtcbiAgcmV0dXJuIHBhcmFncmFwaHM7XG59XG4iLCJpbXBvcnQgeyBEaXJlY3RpdmUsIE91dHB1dCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95LCBJbmplY3QsIEVsZW1lbnRSZWYsIEV2ZW50RW1pdHRlciwgZm9yd2FyZFJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3Vic2NyaXB0aW9uLCBmcm9tRXZlbnQsIG1lcmdlLCBTdWJzY3JpcHRpb25MaWtlLCBvZiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZmlsdGVyLCBzd2l0Y2hNYXAsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IFRleGltYXRlIH0gZnJvbSAnLi90ZXhpbWF0ZSc7XG5cbmV4cG9ydCBjbGFzcyBUZXhpbWF0ZUV2ZW50cyBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveSB7XG5cbiAgc2VsZWN0b3I6IHN0cmluZztcbiAgZXZlbnRzOiBTdWJzY3JpcHRpb25MaWtlID0gU3Vic2NyaXB0aW9uLkVNUFRZO1xuICBAT3V0cHV0KCcnKSBjbGljayA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgnJykgaG92ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgY29uc3RydWN0b3IocHVibGljIGVsZW1lbnQ6IEVsZW1lbnRSZWYsIEBJbmplY3QoZm9yd2FyZFJlZigoKSA9PiBUZXhpbWF0ZSkpIHB1YmxpYyB0ZXhpbWF0ZTogVGV4aW1hdGUpIHtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICB0aGlzLmV2ZW50cyA9IHRoaXMudGV4aW1hdGUuc3RhdGUucGlwZShcbiAgICAgIGZpbHRlcigoKSA9PiAhIXRoaXMuY2xpY2sub2JzZXJ2ZXJzLmxlbmd0aCB8fCAhIXRoaXMuaG92ZXIub2JzZXJ2ZXJzLmxlbmd0aCksXG4gICAgICBzd2l0Y2hNYXAoKCkgPT4ge1xuICAgICAgICBsZXQgY2xpY2tFdmVudHMgPSBvZihudWxsKTtcbiAgICAgICAgbGV0IGhvdmVyRXZlbnRzID0gb2YobnVsbCk7XG4gICAgICAgIGNvbnN0IHdvcmRzID0gdGhpcy5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbCh0aGlzLnNlbGVjdG9yKTtcbiAgICAgICAgaWYgKHRoaXMuY2xpY2sub2JzZXJ2ZXJzLmxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IGV2ZW50cyA9IEFycmF5LmZyb20od29yZHMpLm1hcCgoZWw6IEhUTUxFbGVtZW50KSA9PiBmcm9tRXZlbnQoZWwsICdjbGljaycpKTtcbiAgICAgICAgICBjbGlja0V2ZW50cyA9IG1lcmdlKC4uLmV2ZW50cykucGlwZSh0YXAoZSA9PiB0aGlzLmNsaWNrLmVtaXQoZSkpKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5ob3Zlci5vYnNlcnZlcnMubGVuZ3RoKSB7XG4gICAgICAgICAgY29uc3QgZXZlbnRzID0gQXJyYXkuZnJvbSh3b3JkcykubWFwKChlbDogSFRNTEVsZW1lbnQpID0+IGZyb21FdmVudChlbCwgJ21vdXNlZW50ZXInKSk7XG4gICAgICAgICAgaG92ZXJFdmVudHMgPSBtZXJnZSguLi5ldmVudHMpLnBpcGUodGFwKGUgPT4gdGhpcy5ob3Zlci5lbWl0KGUpKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG1lcmdlKGNsaWNrRXZlbnRzLCBob3ZlckV2ZW50cyk7XG4gICAgICB9KVxuICAgICkuc3Vic2NyaWJlKCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLmV2ZW50cy51bnN1YnNjcmliZSgpO1xuICB9XG5cbn1cblxuQERpcmVjdGl2ZSh7XG4gIHNlbGVjdG9yOiAndGV4aW1hdGVbcGFyYWdyYXBoQ2xpY2tdLCB0ZXhpbWF0ZVtwYXJhZ3JhcGhIb3Zlcl0nXG59KVxuZXhwb3J0IGNsYXNzIFBhcmFncmFwaEV2ZW50cyBleHRlbmRzIFRleGltYXRlRXZlbnRzIHtcbiAgQE91dHB1dCgncGFyYWdyYXBoQ2xpY2snKSBjbGljayA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgncGFyYWdyYXBoSG92ZXInKSBob3ZlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICBjb25zdHJ1Y3RvcihlbGVtZW50OiBFbGVtZW50UmVmLCBASW5qZWN0KGZvcndhcmRSZWYoKCkgPT4gVGV4aW1hdGUpKSB0ZXhpbWF0ZTogVGV4aW1hdGUpIHtcbiAgICBzdXBlcihlbGVtZW50LCB0ZXhpbWF0ZSk7XG4gICAgdGhpcy5zZWxlY3RvciA9ICcudGV4aW1hdGUtcGFyYWdyYXBoJztcbiAgfVxufVxuXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICd0ZXhpbWF0ZVt3b3JkQ2xpY2tdLCB0ZXhpbWF0ZVt3b3JkSG92ZXJdJ1xufSlcbmV4cG9ydCBjbGFzcyBXb3JkRXZlbnRzIGV4dGVuZHMgVGV4aW1hdGVFdmVudHMge1xuICBAT3V0cHV0KCd3b3JkQ2xpY2snKSBjbGljayA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgnd29yZEhvdmVyJykgaG92ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgY29uc3RydWN0b3IoZWxlbWVudDogRWxlbWVudFJlZiwgQEluamVjdChmb3J3YXJkUmVmKCgpID0+IFRleGltYXRlKSkgdGV4aW1hdGU6IFRleGltYXRlKSB7XG4gICAgc3VwZXIoZWxlbWVudCwgdGV4aW1hdGUpO1xuICAgIHRoaXMuc2VsZWN0b3IgPSAnLnRleGltYXRlLXdvcmQnO1xuICB9XG59XG5cbkBEaXJlY3RpdmUoe1xuICBzZWxlY3RvcjogJ3RleGltYXRlW2xldHRlckNsaWNrXSwgdGV4aW1hdGVbbGV0dGVySG92ZXJdJ1xufSlcbmV4cG9ydCBjbGFzcyBMZXR0ZXJFdmVudHMgZXh0ZW5kcyBUZXhpbWF0ZUV2ZW50cyB7XG4gIEBPdXRwdXQoJ2xldHRlckNsaWNrJykgY2xpY2sgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gIEBPdXRwdXQoJ2xldHRlckhvdmVyJykgaG92ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgY29uc3RydWN0b3IoZWxlbWVudDogRWxlbWVudFJlZiwgQEluamVjdChmb3J3YXJkUmVmKCgpID0+IFRleGltYXRlKSkgdGV4aW1hdGU6IFRleGltYXRlKSB7XG4gICAgc3VwZXIoZWxlbWVudCwgdGV4aW1hdGUpO1xuICAgIHRoaXMuc2VsZWN0b3IgPSAnLnRleGltYXRlLWxldHRlcic7XG4gIH1cbn1cbiIsImltcG9ydCB7IE5nTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgVGV4aW1hdGUgfSBmcm9tICcuL3RleGltYXRlJztcbmltcG9ydCB7IFBhcmFncmFwaEV2ZW50cywgV29yZEV2ZW50cywgTGV0dGVyRXZlbnRzIH0gZnJvbSAnLi90ZXhpbWF0ZS1ldmVudHMnO1xuXG5ATmdNb2R1bGUoe1xuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlXSxcbiAgZGVjbGFyYXRpb25zOiBbXG4gICAgVGV4aW1hdGUsXG4gICAgUGFyYWdyYXBoRXZlbnRzLFxuICAgIFdvcmRFdmVudHMsXG4gICAgTGV0dGVyRXZlbnRzXG4gIF0sXG4gIGV4cG9ydHM6IFtcbiAgICBUZXhpbWF0ZSxcbiAgICBQYXJhZ3JhcGhFdmVudHMsXG4gICAgV29yZEV2ZW50cyxcbiAgICBMZXR0ZXJFdmVudHNcbiAgXVxufSlcbmV4cG9ydCBjbGFzcyBUZXhpbWF0ZU1vZHVsZSB7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBLE1Ba0NhLFFBQVE7Ozs7OztJQWlEbkIsWUFBb0IsZ0JBQWtDLEVBQVUsSUFBWSxFQUFVLEVBQWM7UUFBaEYscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUFVLFNBQUksR0FBSixJQUFJLENBQVE7UUFBVSxPQUFFLEdBQUYsRUFBRSxDQUFZOzs7O1FBaENwRixnQkFBVyxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7Ozs7UUFHL0Isa0JBQWEsR0FBRyxJQUFJLFlBQVksRUFBRSxDQUFDOzs7O1FBR3JELFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBMkIsQ0FBQzs7OztRQUdyQyxXQUFNLEdBQUcsSUFBSSxlQUFlLENBQVMsRUFBRSxDQUFDLENBQUM7UUF3Qi9DLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBWSxLQUFLLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDN0U7Ozs7OztJQWhERCxJQUFtQixPQUFPLENBQUMsSUFBWTtRQUNyQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUN4Qjs7OztJQTRCRCxJQUFJLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7S0FDeEI7Ozs7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLElBQUksQ0FB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