@covalent/guided-tour
Version:
Covalent Guided Tour Module
438 lines • 67.3 kB
JavaScript
import Shepherd from 'shepherd.js';
import { timer, Subject, BehaviorSubject, merge, fromEvent, forkJoin, } from 'rxjs';
import { takeUntil, skipWhile, filter, skip, first } from 'rxjs/operators';
export var ITourEvent;
(function (ITourEvent) {
ITourEvent["click"] = "click";
ITourEvent["pointerover"] = "pointerover";
ITourEvent["keyup"] = "keyup";
ITourEvent["added"] = "added";
ITourEvent["removed"] = "removed";
})(ITourEvent || (ITourEvent = {}));
class TourButtonsActions {
}
const SHEPHERD_DEFAULT_FIND_TIME_BEFORE_SHOW = 100;
const SHEPHERD_DEFAULT_FIND_INTERVAL = 500;
const SHEPHERD_DEFAULT_FIND_ATTEMPTS = 20;
const overriddenEvents = [
ITourEvent.click,
ITourEvent.pointerover,
ITourEvent.removed,
ITourEvent.added,
ITourEvent.keyup,
];
const keyEvents = new Map([
[13, 'enter'],
[27, 'esc'],
]);
const defaultStepOptions = {
scrollTo: { behavior: 'smooth', block: 'center' },
cancelIcon: {
enabled: true,
},
};
const MAT_ICON_BUTTON = 'mdc-icon-button material-icons mat-mdc-icon-button mat-mdc-button-base';
const MAT_BUTTON = 'mdc-button mat-mdc-button mat-mdc-button-base';
const MAT_BUTTON_INVISIBLE = 'shepherd-void-button';
export class CovalentGuidedTour extends TourButtonsActions {
constructor(stepOptions = defaultStepOptions) {
super();
this.stepOptions = stepOptions;
this.newTour();
}
newTour(opts) {
this.shepherdTour = new Shepherd.Tour(Object.assign({
defaultStepOptions: this.stepOptions,
}, opts));
this._destroyedEvent$ = new Subject();
// listen to cancel and complete to clean up abortOn events
merge(fromEvent(this.shepherdTour, 'cancel'), fromEvent(this.shepherdTour, 'complete'))
.pipe(first())
.subscribe(() => {
this._destroyedEvent$.next();
this._destroyedEvent$.complete();
});
// if abortOn was passed, we bind the event and execute complete
if (opts && opts.abortOn) {
const abortArr$ = [];
opts.abortOn.forEach((abortOn) => {
const abortEvent$ = new Subject();
abortArr$.push(abortEvent$);
this._bindEvent(abortOn, undefined, abortEvent$, this._destroyedEvent$);
});
const abortSubs = merge(...abortArr$)
.pipe(takeUntil(this._destroyedEvent$))
.subscribe(() => {
this.shepherdTour.complete();
abortSubs.unsubscribe();
});
}
}
back() {
this.shepherdTour.back();
}
cancel() {
this.shepherdTour.cancel();
}
next() {
this.shepherdTour.next();
}
finish() {
this.shepherdTour.complete();
}
addSteps(steps) {
this.shepherdTour.addSteps(this._prepareTour(steps));
}
start() {
this.shepherdTour.start();
}
_prepareTour(originalSteps, finishLabel = 'finish') {
// create Subjects for back and forward events
const backEvent$ = new Subject();
const forwardEvent$ = new Subject();
let _backFlow = false;
// create Subject for your end
const destroyedEvent$ = new Subject();
/**
* This function adds the step progress in the footer of the shepherd tooltip
*/
const appendProgressFunc = function () {
// get all the footers that are available in the DOM
const footers = Array.from(document.querySelectorAll('.shepherd-footer'));
// get the last footer since Shepherd always puts the active one at the end
const footer = footers[footers.length - 1];
// generate steps html element
const progress = document.createElement('span');
progress.className = 'shepherd-progress';
progress.innerText = `${this.shepherdTour.currentStep.options.count}/${stepTotal}`;
// insert into the footer before the first button
footer.insertBefore(progress, footer.querySelector('.shepherd-button'));
};
let stepTotal = 0;
const steps = originalSteps.map((step) => {
let showProgress = () => {
//
};
if (step.attachToOptions?.skipFromStepCount === true) {
showProgress = function () {
return;
};
}
else if (step.attachToOptions?.skipFromStepCount === undefined ||
step.attachToOptions?.skipFromStepCount === false) {
step.count = ++stepTotal;
showProgress = appendProgressFunc.bind(this);
}
return Object.assign({}, step, {
when: {
show: showProgress,
},
});
});
const finishButton = {
text: finishLabel,
action: this['finish'].bind(this),
classes: MAT_BUTTON,
};
const voidButton = {
text: '',
action() {
return;
},
classes: MAT_BUTTON_INVISIBLE,
};
// listen to the destroyed event to clean up all the streams
this._destroyedEvent$.pipe(first()).subscribe(() => {
backEvent$.complete();
forwardEvent$.complete();
destroyedEvent$.next();
destroyedEvent$.complete();
});
const totalSteps = steps.length;
steps.forEach((step, index) => {
// create buttons specific for the step
// this is done to create more control on events
const nextButton = {
text: 'chevron_right',
action: () => {
// intercept the next action and trigger event
forwardEvent$.next();
this.shepherdTour.next();
},
classes: MAT_ICON_BUTTON,
};
const backButton = {
text: 'chevron_left',
action: () => {
// intercept the back action and trigger event
backEvent$.next();
_backFlow = true;
// check if 'goBackTo' is set to jump to a particular step, else just go back
if (step.attachToOptions && step.attachToOptions.goBackTo) {
this.shepherdTour.show(step.attachToOptions.goBackTo, false);
}
else {
this.shepherdTour.back();
}
},
classes: step.advanceOnOptions?.allowGoBack === false
? MAT_BUTTON_INVISIBLE
: MAT_ICON_BUTTON,
};
// check if highlight was provided for the step, else fallback into shepherds usage
step.highlightClass =
step.attachToOptions && step.attachToOptions.highlight
? 'shepherd-highlight'
: step.highlightClass;
// Adding buttons in the steps if no buttons are defined
if (!step.buttons || step.buttons.length === 0) {
if (index === 0) {
// first step
step.buttons = [nextButton];
}
else if (index === totalSteps - 1) {
// last step
step.buttons = [backButton, finishButton];
}
else {
step.buttons = [backButton, nextButton];
}
}
// checks "advanceOn" to override listeners
let advanceOn = step.advanceOn;
// remove the shepherd "advanceOn" infavor of ours if the event is part of our list
if ((typeof advanceOn === 'object' &&
!Array.isArray(advanceOn) &&
advanceOn.event &&
overriddenEvents.indexOf(advanceOn.event.split('.')[0]) > -1) ||
advanceOn instanceof Array) {
step.advanceOn = undefined;
step.buttons =
step.advanceOnOptions && step.advanceOnOptions.allowGoBack
? [backButton, voidButton]
: [voidButton];
}
// adds a default beforeShowPromise function
step.beforeShowPromise = () => {
return new Promise((resolve) => {
const additionalCapabilitiesSetup = () => {
if (advanceOn && !step.advanceOn) {
if (!Array.isArray(advanceOn)) {
advanceOn = [advanceOn];
}
const advanceArr$ = [];
advanceOn.forEach((_, i) => {
const advanceEvent$ = new Subject();
advanceArr$.push(advanceEvent$);
// we start a timer of attempts to find an element in the dom
this._bindEvent(_, step.advanceOnOptions, advanceEvent$, destroyedEvent$);
});
const advanceSubs = forkJoin(...advanceArr$)
.pipe(takeUntil(merge(destroyedEvent$, backEvent$)))
.subscribe(() => {
// check if we need to advance to a specific step, else advance to next step
if (step.advanceOnOptions && step.advanceOnOptions.jumpTo) {
this.shepherdTour.show(step.advanceOnOptions.jumpTo);
}
else {
this.shepherdTour.next();
}
forwardEvent$.next();
advanceSubs.unsubscribe();
});
}
// if abortOn was passed on the step, we bind the event and execute complete
if (step.abortOn) {
const abortArr$ = [];
step.abortOn.forEach((abortOn) => {
const abortEvent$ = new Subject();
abortArr$.push(abortEvent$);
this._bindEvent(abortOn, undefined, abortEvent$, destroyedEvent$);
});
const abortSubs = merge(...abortArr$)
.pipe(takeUntil(merge(destroyedEvent$, backEvent$, forwardEvent$)))
.subscribe(() => {
this.shepherdTour.complete();
abortSubs.unsubscribe();
});
}
};
const _stopTimer$ = new Subject();
const _retriesReached$ = new Subject();
const _retryAttempts$ = new BehaviorSubject(-1);
let id;
// checks if "attachTo" is a string or an object to get the id of an element
if (typeof step.attachTo === 'string') {
id = step.attachTo;
}
else if (typeof step.attachTo === 'object' &&
typeof step.attachTo.element === 'string') {
id = step.attachTo.element;
}
// if we have an id as a string in either case, we use it (we ignore it if its HTMLElement)
if (id) {
// if current step is the first step of the tour, we set the buttons to be only "next"
// we had to use `any` since the tour doesnt expose the steps in any fashion nor a way to check if we have modified them at all
if (this.shepherdTour.getCurrentStep() ===
this.shepherdTour.steps[0]) {
this.shepherdTour.getCurrentStep()?.updateStepOptions({
buttons: originalSteps[index].advanceOn
? [voidButton]
: [nextButton],
});
}
// register to the attempts observable to notify deeveloper when number has been reached
_retryAttempts$
.pipe(skip(1), skipWhile((val) => {
if (step.attachToOptions &&
step.attachToOptions.retries !== undefined) {
return val < step.attachToOptions.retries;
}
return val < SHEPHERD_DEFAULT_FIND_ATTEMPTS;
}), takeUntil(merge(_stopTimer$.asObservable(), destroyedEvent$)))
.subscribe((attempts) => {
_retriesReached$.next(1);
_retriesReached$.complete();
// if attempts have been reached, we check "skipIfNotFound" to move on to the next step
if (step.attachToOptions &&
step.attachToOptions.skipIfNotFound) {
// if we get to this step coming back from a step and it wasnt found
// then we either check if its the first step and try going forward
// or we keep going back until we find a step that actually exists
if (_backFlow) {
if (this.shepherdTour.steps.indexOf(this.shepherdTour.getCurrentStep()) === 0) {
this.shepherdTour.next();
}
else {
this.shepherdTour.back();
}
_backFlow = false;
}
else {
// destroys current step if we need to skip it to remove it from the tour
const currentStep = this.shepherdTour.getCurrentStep();
currentStep?.destroy();
this.shepherdTour.next();
this.shepherdTour.removeStep(currentStep?.id ?? '');
}
}
else if (step.attachToOptions && step.attachToOptions.else) {
// if "skipIfNotFound" is not true, then we check if "else" has been set to jump to a specific step
this.shepherdTour.show(step.attachToOptions.else);
}
else {
// tslint:disable-next-line:no-console
console.warn(`Retries reached trying to find ${id}. Retried ${attempts} times.`);
// else we show the step regardless
resolve();
}
});
// we start a timer of attempts to find an element in the dom
timer((step.attachToOptions && step.attachToOptions.timeBeforeShow) ||
SHEPHERD_DEFAULT_FIND_TIME_BEFORE_SHOW, (step.attachToOptions && step.attachToOptions.interval) ||
SHEPHERD_DEFAULT_FIND_INTERVAL)
.pipe(
// the timer will continue either until we find the element or the number of attempts has been reached
takeUntil(merge(_stopTimer$, _retriesReached$, destroyedEvent$)))
.subscribe(() => {
const element = document.querySelector(id ?? '');
// if the element has been found, we stop the timer and resolve the promise
if (element) {
_stopTimer$.next();
_stopTimer$.complete();
additionalCapabilitiesSetup();
resolve();
}
else {
_retryAttempts$.next(_retryAttempts$.value + 1);
}
});
// stop find interval if user stops the tour
destroyedEvent$.subscribe(() => {
_stopTimer$.next();
_stopTimer$.complete();
_retriesReached$.next(1);
_retriesReached$.complete();
});
}
else {
// resolve observable until the timeBeforeShow has passsed or use default
timer((step.attachToOptions && step.attachToOptions.timeBeforeShow) ||
SHEPHERD_DEFAULT_FIND_TIME_BEFORE_SHOW)
.pipe(takeUntil(merge(destroyedEvent$)))
.subscribe(() => {
resolve();
});
}
});
};
});
return steps;
}
_bindEvent(eventOn, eventOnOptions, event$, destroyedEvent$) {
const selector = eventOn.selector ?? '';
const event = eventOn.event;
// we start a timer of attempts to find an element in the dom
const timerSubs = timer((eventOnOptions && eventOnOptions.timeBeforeShow) ||
SHEPHERD_DEFAULT_FIND_TIME_BEFORE_SHOW, (eventOnOptions && eventOnOptions.interval) ||
SHEPHERD_DEFAULT_FIND_INTERVAL)
.pipe(takeUntil(destroyedEvent$))
.subscribe(() => {
const element = document.querySelector(selector);
// if the element has been found, we stop the timer and resolve the promise
if (element) {
timerSubs.unsubscribe();
if (event === ITourEvent.added) {
// if event is "Added" trigger a soon as this is attached.
event$.next();
event$.complete();
}
else if (event === ITourEvent.click ||
event === ITourEvent.pointerover ||
(event && event.indexOf(ITourEvent.keyup) > -1)) {
// we use normal listeners for mouseevents
const mainEvent = event?.split('.')[0];
const subEvent = event?.split('.')[1];
fromEvent(element, mainEvent)
.pipe(filter(($event) => {
// only trigger if the event is a keyboard event and part of out list
if ($event instanceof KeyboardEvent) {
if (keyEvents.get($event.keyCode) === subEvent) {
return true;
}
return false;
}
else {
return true;
}
}), takeUntil(merge(event$.asObservable(), destroyedEvent$)))
.subscribe(() => {
event$.next();
event$.complete();
});
}
else if (event === ITourEvent.removed) {
// and we will use MutationObserver for DOM events
const observer = new MutationObserver(() => {
if (!document.body.contains(element)) {
event$.next();
event$.complete();
observer.disconnect();
}
});
// stop listenining if tour is closed
destroyedEvent$.subscribe(() => {
observer.disconnect();
});
// observe for any DOM interaction in the element
observer.observe(element, {
childList: true,
subtree: true,
attributes: true,
});
}
}
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3VpZGVkLnRvdXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWJzL2FuZ3VsYXItZ3VpZGVkLXRvdXIvc3JjL2xpYi9ndWlkZWQudG91ci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLFFBQVEsTUFBTSxhQUFhLENBQUM7QUFDbkMsT0FBTyxFQUNMLEtBQUssRUFDTCxPQUFPLEVBQ1AsZUFBZSxFQUNmLEtBQUssRUFFTCxTQUFTLEVBQ1QsUUFBUSxHQUNULE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUszRSxNQUFNLENBQU4sSUFBWSxVQU1YO0FBTkQsV0FBWSxVQUFVO0lBQ3BCLDZCQUFpQixDQUFBO0lBQ2pCLHlDQUE2QixDQUFBO0lBQzdCLDZCQUFpQixDQUFBO0lBQ2pCLDZCQUFpQixDQUFBO0lBQ2pCLGlDQUFxQixDQUFBO0FBQ3ZCLENBQUMsRUFOVyxVQUFVLEtBQVYsVUFBVSxRQU1yQjtBQXNDRCxNQUFlLGtCQUFrQjtDQVFoQztBQUVELE1BQU0sc0NBQXNDLEdBQUcsR0FBRyxDQUFDO0FBQ25ELE1BQU0sOEJBQThCLEdBQUcsR0FBRyxDQUFDO0FBQzNDLE1BQU0sOEJBQThCLEdBQUcsRUFBRSxDQUFDO0FBRTFDLE1BQU0sZ0JBQWdCLEdBQWE7SUFDakMsVUFBVSxDQUFDLEtBQUs7SUFDaEIsVUFBVSxDQUFDLFdBQVc7SUFDdEIsVUFBVSxDQUFDLE9BQU87SUFDbEIsVUFBVSxDQUFDLEtBQUs7SUFDaEIsVUFBVSxDQUFDLEtBQUs7Q0FDakIsQ0FBQztBQUVGLE1BQU0sU0FBUyxHQUF3QixJQUFJLEdBQUcsQ0FBaUI7SUFDN0QsQ0FBQyxFQUFFLEVBQUUsT0FBTyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDO0NBQ1osQ0FBQyxDQUFDO0FB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QUFDLEdBQUcsRUFBRTt3QkFDZCxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7d0JBQ2QsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDO29CQUNwQixDQUFDLENBQUMsQ0FBQztpQkFDTjtxQkFBTSxJQUFJLEtBQUssS0FBSyxVQUFVLENBQUMsT0FBTyxFQUFFO29CQUN2QyxrREFBa0Q7b0JBQ2xELE1BQU0sUUFBUSxHQUFxQixJQUFJLGdCQUFnQixDQUFDLEdBQUcsRUFBRTt3QkFDM0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFOzRCQUNwQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7NEJBQ2QsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDOzRCQUNsQixRQUFRLENBQUMsVUFBVSxFQUFFLENBQUM7eUJBQ3ZCO29CQUNILENBQUMsQ0FBQyxDQUFDO29CQUVILHFDQUFxQztvQkFDckMsZUFBZSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUU7d0JBQzdCLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztvQkFDeEIsQ0FBQyxDQUFDLENBQUM7b0JBQ0gsaURBQWlEO29CQUNqRCxRQUFRLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRTt3QkFDeEIsU0FBUyxFQUFFLElBQUk7d0JBQ2YsT0FBTyxFQUFFLElBQUk7d0JBQ2IsVUFBVSxFQUFFLElBQUk7cUJBQ2pCLENBQUMsQ0FBQztpQkFDSjthQUNGO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgU2hlcGhlcmQgZnJvbSAnc2hlcGhlcmQuanMnO1xuaW1wb3J0IHtcbiAgdGltZXIsXG4gIFN1YmplY3QsXG4gIEJlaGF2aW9yU3ViamVjdCxcbiAgbWVyZ2UsXG4gIFN1YnNjcmlwdGlvbixcbiAgZnJvbUV2ZW50LFxuICBmb3JrSm9pbixcbn0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0YWtlVW50aWwsIHNraXBXaGlsZSwgZmlsdGVyLCBza2lwLCBmaXJzdCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuZXhwb3J0IHR5cGUgVG91clN0ZXAgPSBTaGVwaGVyZC5TdGVwLlN0ZXBPcHRpb25zO1xuZXhwb3J0IHR5cGUgVG91clN0ZXBCdXR0b24gPSBTaGVwaGVyZC5TdGVwLlN0ZXBPcHRpb25zQnV0dG9uO1xuXG5leHBvcnQgZW51bSBJVG91ckV2ZW50IHtcbiAgJ2NsaWNrJyA9ICdjbGljaycsXG4gICdwb2ludGVyb3ZlcicgPSAncG9pbnRlcm92ZXInLFxuICAna2V5dXAnID0gJ2tleXVwJyxcbiAgJ2FkZGVkJyA9ICdhZGRlZCcsIC8vIGFkZGVkIHRvIERPTVxuICAncmVtb3ZlZCcgPSAncmVtb3ZlZCcsIC8vIHJlbW92ZWQgZnJvbSBET01cbn1cblxuZXhwb3J0IGludGVyZmFjZSBJVG91ckV2ZW50T24ge1xuICBzZWxlY3Rvcj86IHN0cmluZzsgLy8gY3NzIHNlbGVjdG9yXG4gIGV2ZW50Pzoga2V5b2YgdHlwZW9mIElUb3VyRXZlbnQ7IC8vIGNsaWNrLCBwb2ludGVyb3Zlciwga2V5dXAsIGFkZGVkLCByZW1vdmVkXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVRvdXJFdmVudE9uT3B0aW9ucyB7XG4gIHRpbWVCZWZvcmVTaG93PzogbnVtYmVyOyAvLyBkZWxheSBiZWZvcmUgc3RlcCBpcyBkaXNwbGF5ZWRcbiAgaW50ZXJ2YWw/OiBudW1iZXI7IC8vIHRpbWUgYmV0d2VlbiBzZWFyY2hlcyBmb3IgZWxlbWVudCwgZGVmYXVsdHMgdG8gNTAwbXNcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJVG91ck9wdGlvbnMgZXh0ZW5kcyBTaGVwaGVyZC5Ub3VyLlRvdXJPcHRpb25zIHtcbiAgYWJvcnRPbj86IElUb3VyRXZlbnRPbltdOyAvLyBldmVudHMgdG8gYWJvcnQgb25cbn1cblxuZXhwb3J0IGludGVyZmFjZSBJVG91clN0ZXBBdHRhY2hUb09wdGlvbnMgZXh0ZW5kcyBJVG91ckV2ZW50T25PcHRpb25zIHtcbiAgaGlnaGxpZ2h0PzogYm9vbGVhbjtcbiAgcmV0cmllcz86IG51bWJlcjsgLy8gIyBudW0gb2YgYXR0ZW1wdHMgdG8gZmluZCBlbGVtZW50XG4gIHNraXBJZk5vdEZvdW5kPzogYm9vbGVhbjsgLy8gaWYgZWxlbWVudCBpcyBub3QgZm91bmQgYWZ0ZXIgbiByZXRyaWVzLCBtb3ZlIG9uIHRvIG5leHQgc3RlcFxuICBlbHNlPzogc3RyaW5nOyAvLyBpZiBlbGVtZW50IGlzIG5vdCBmb3VuZCwgZ28gdG8gc3RlcCB3aXRoIHRoaXMgaWRcbiAgZ29CYWNrVG8/OiBzdHJpbmc7IC8vIGJhY2sgYnV0dG9uIGdvZXMgYmFjayB0byBzdGVwIHdpdGggdGhpcyBpZFxuICBza2lwRnJvbVN0ZXBDb3VudD86IGJvb2xlYW47IC8vIHNob3cvaGlkZSBwcm9ncmVzcyBvbiBzdGVwXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVRvdXJTdGVwQWR2YW5jZU9uT3B0aW9ucyBleHRlbmRzIElUb3VyRXZlbnRPbk9wdGlvbnMge1xuICBqdW1wVG8/OiBzdHJpbmc7IC8vIG5leHQgYnV0dG9uIHdpbGwganVtcCB0byBzdGVwIHdpdGggdGhpcyBpZFxuICBhbGxvd0dvQmFjaz86IGJvb2xlYW47IC8vIGFsbG93IGJhY2sgd2l0aGluIHRoaXMgc3RlcFxufVxuXG5leHBvcnQgaW50ZXJmYWNlIElUb3VyU3RlcCBleHRlbmRzIFRvdXJTdGVwIHtcbiAgYXR0YWNoVG9PcHRpb25zPzogSVRvdXJTdGVwQXR0YWNoVG9PcHRpb25zO1xuICBhZHZhbmNlT25PcHRpb25zPzogSVRvdXJTdGVwQWR2YW5jZU9uT3B0aW9ucztcbiAgYWR2YW5jZU9uPzogSVRvdXJFdmVudE9uW10gfCBJVG91ckV2ZW50T24gfCBhbnk7XG4gIGFib3J0T24/OiBJVG91ckV2ZW50T25bXTtcbiAgY291bnQ/OiBudW1iZXI7XG59XG5cbmFic3RyYWN0IGNsYXNzIFRvdXJCdXR0b25zQWN0aW9ucyB7XG4gIGFic3RyYWN0IG5leHQoKTogdm9pZDtcblxuICBhYnN0cmFjdCBiYWNrKCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgY2FuY2VsKCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgZmluaXNoKCk6IHZvaWQ7XG59XG5cbmNvbnN0IFNIRVBIRVJEX0RFRkFVTFRfRklORF9USU1FX0JFRk9SRV9TSE9XID0gMTAwO1xuY29uc3QgU0hFUEhFUkRfREVGQVVMVF9GSU5EX0lOVEVSVkFMID0gNTAwO1xuY29uc3QgU0hFUEhFUkRfREVGQVVMVF9GSU5EX0FUVEVNUFRTID0gMjA7XG5cbmNvbnN0IG92ZXJyaWRkZW5FdmVudHM6IHN0cmluZ1tdID0gW1xuICBJVG91ckV2ZW50LmNsaWNrLFxuICBJVG91ckV2ZW50LnBvaW50ZXJvdmVyLFxuICBJVG91ckV2ZW50LnJlbW92ZWQsXG4gIElUb3VyRXZlbnQuYWRkZWQsXG4gIElUb3VyRXZlbnQua2V5dXAsXG5dO1xuXG5jb25zdCBrZXlFdmVudHM6IE1hcDxudW1iZXIsIHN0cmluZz4gPSBuZXcgTWFwPG51bWJlciwgc3RyaW5nPihbXG4gIFsxMywgJ2VudGVyJ10sXG4gIFsyNywgJ2VzYyddLFxuXSk7XG5cbmNvbnN0IGRlZmF1bHRTdGVwT3B0aW9uczogVG91clN0ZXAgPSB7XG4gIHNjcm9sbFRvOiB7IGJlaGF2aW9yOiAnc21vb3RoJywgYmxvY2s6ICdjZW50ZXInIH0sXG4gIGNhbmNlbEljb246IHtcbiAgICBlbmFibGVkOiB0cnVlLFxuICB9LFxufTtcblxuY29uc3QgTUFUX0lDT05fQlVUVE9OID1cbiAgJ21kYy1pY29uLWJ1dHRvbiBtYXRlcmlhbC1pY29ucyBtYXQtbWRjLWljb24tYnV0dG9uIG1hdC1tZGMtYnV0dG9uLWJhc2UnO1xuY29uc3QgTUFUX0JVVFRPTiA9ICdtZGMtYnV0dG9uIG1hdC1tZGMtYnV0dG9uIG1hdC1tZGMtYnV0dG9uLWJhc2UnO1xuY29uc3QgTUFUX0JVVFRPTl9JTlZJU0lCTEUgPSAnc2hlcGhlcmQtdm9pZC1idXR0b24nO1xuXG5leHBvcnQgY2xhc3MgQ292YWxlbnRHdWlkZWRUb3VyIGV4dGVuZHMgVG91ckJ1dHRvbnNBY3Rpb25zIHtcbiAgcHJpdmF0ZSBfZGVzdHJveWVkRXZlbnQkITogU3ViamVjdDx2b2lkPjtcblxuICBzaGVwaGVyZFRvdXIhOiBTaGVwaGVyZC5Ub3VyO1xuICBzdGVwT3B0aW9uczogSVRvdXJTdGVwO1xuXG4gIGNvbnN0cnVjdG9yKHN0ZXBPcHRpb25zOiBJVG91clN0ZXAgPSBkZWZhdWx0U3RlcE9wdGlvbnMpIHtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5zdGVwT3B0aW9ucyA9IHN0ZXBPcHRpb25zO1xuICAgIHRoaXMubmV3VG91cigpO1xuICB9XG5cbiAgbmV3VG91cihvcHRzPzogSVRvdXJPcHRpb25zKTogdm9pZCB7XG4gICAgdGhpcy5zaGVwaGVyZFRvdXIgPSBuZXcgU2hlcGhlcmQuVG91cihcbiAgICAgIE9iamVjdC5hc3NpZ24oXG4gICAgICAgIHtcbiAgICAgICAgICBkZWZhdWx0U3RlcE9wdGlvbnM6IHRoaXMuc3RlcE9wdGlvbnMsXG4gICAgICAgIH0sXG4gICAgICAgIG9wdHNcbiAgICAgIClcbiAgICApO1xuXG4gICAgdGhpcy5fZGVzdHJveWVkRXZlbnQkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgICAvLyBsaXN0ZW4gdG8gY2FuY2VsIGFuZCBjb21wbGV0ZSB0byBjbGVhbiB1cCBhYm9ydE9uIGV2ZW50c1xuICAgIG1lcmdlKFxuICAgICAgZnJvbUV2ZW50KHRoaXMuc2hlcGhlcmRUb3VyLCAnY2FuY2VsJyksXG4gICAgICBmcm9tRXZlbnQodGhpcy5zaGVwaGVyZFRvdXIsICdjb21wbGV0ZScpXG4gICAgKVxuICAgICAgLnBpcGUoZmlyc3QoKSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICB0aGlzLl9kZXN0cm95ZWRFdmVudCQubmV4dCgpO1xuICAgICAgICB0aGlzLl9kZXN0cm95ZWRFdmVudCQuY29tcGxldGUoKTtcbiAgICAgIH0pO1xuXG4gICAgLy8gaWYgYWJvcnRPbiB3YXMgcGFzc2VkLCB3ZSBiaW5kIHRoZSBldmVudCBhbmQgZXhlY3V0ZSBjb21wbGV0ZVxuICAgIGlmIChvcHRzICYmIG9wdHMuYWJvcnRPbikge1xuICAgICAgY29uc3QgYWJvcnRBcnIkOiBTdWJqZWN0PHZvaWQ+W10gPSBbXTtcbiAgICAgIG9wdHMuYWJvcnRPbi5mb3JFYWNoKChhYm9ydE9uOiBJVG91ckV2ZW50T24pID0+IHtcbiAgICAgICAgY29uc3QgYWJvcnRFdmVudCQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgICAgICBhYm9ydEFyciQucHVzaChhYm9ydEV2ZW50JCk7XG4gICAgICAgIHRoaXMuX2JpbmRFdmVudChhYm9ydE9uLCB1bmRlZmluZWQsIGFib3J0RXZlbnQkLCB0aGlzLl9kZXN0cm95ZWRFdmVudCQpO1xuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IGFib3J0U3ViczogU3Vic2NyaXB0aW9uID0gbWVyZ2UoLi4uYWJvcnRBcnIkKVxuICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fZGVzdHJveWVkRXZlbnQkKSlcbiAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5zaGVwaGVyZFRvdXIuY29tcGxldGUoKTtcbiAgICAgICAgICBhYm9ydFN1YnMudW5zdWJzY3JpYmUoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgYmFjaygpOiB2b2lkIHtcbiAgICB0aGlzLnNoZXBoZXJkVG91ci5iYWNrKCk7XG4gIH1cblxuICBjYW5jZWwoKTogdm9pZCB7XG4gICAgdGhpcy5zaGVwaGVyZFRvdXIuY2FuY2VsKCk7XG4gIH1cblxuICBuZXh0KCk6IHZvaWQge1xuICAgIHRoaXMuc2hlcGhlcmRUb3VyLm5leHQoKTtcbiAgfVxuXG4gIGZpbmlzaCgpOiB2b2lkIHtcbiAgICB0aGlzLnNoZXBoZXJkVG91ci5jb21wbGV0ZSgpO1xuICB9XG5cbiAgYWRkU3RlcHMoc3RlcHM6IElUb3VyU3RlcFtdKTogdm9pZCB7XG4gICAgdGhpcy5zaGVwaGVyZFRvdXIuYWRkU3RlcHModGhpcy5fcHJlcGFyZVRvdXIoc3RlcHMpKTtcbiAgfVxuXG4gIHN0YXJ0KCk6IHZvaWQge1xuICAgIHRoaXMuc2hlcGhlcmRUb3VyLnN0YXJ0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgX3ByZXBhcmVUb3VyKFxuICAgIG9yaWdpbmFsU3RlcHM6IElUb3VyU3RlcFtdLFxuICAgIGZpbmlzaExhYmVsOiBzdHJpbmcgPSAnZmluaXNoJ1xuICApOiBJVG91clN0ZXBbXSB7XG4gICAgLy8gY3JlYXRlIFN1YmplY3RzIGZvciBiYWNrIGFuZCBmb3J3YXJkIGV2ZW50c1xuICAgIGNvbnN0IGJhY2tFdmVudCQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgIGNvbnN0IGZvcndhcmRFdmVudCQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgIGxldCBfYmFja0Zsb3cgPSBmYWxzZTtcbiAgICAvLyBjcmVhdGUgU3ViamVjdCBmb3IgeW91ciBlbmRcbiAgICBjb25zdCBkZXN0cm95ZWRFdmVudCQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgIC8qKlxuICAgICAqIFRoaXMgZnVuY3Rpb24gYWRkcyB0aGUgc3RlcCBwcm9ncmVzcyBpbiB0aGUgZm9vdGVyIG9mIHRoZSBzaGVwaGVyZCB0b29sdGlwXG4gICAgICovXG4gICAgY29uc3QgYXBwZW5kUHJvZ3Jlc3NGdW5jID0gZnVuY3Rpb24gKHRoaXM6IGFueSk6IHZvaWQge1xuICAgICAgLy8gZ2V0IGFsbCB0aGUgZm9vdGVycyB0aGF0IGFyZSBhdmFpbGFibGUgaW4gdGhlIERPTVxuICAgICAgY29uc3QgZm9vdGVyczogRWxlbWVudFtdID0gQXJyYXkuZnJvbTxFbGVtZW50PihcbiAgICAgICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLnNoZXBoZXJkLWZvb3RlcicpXG4gICAgICApO1xuICAgICAgLy8gZ2V0IHRoZSBsYXN0IGZvb3RlciBzaW5jZSBTaGVwaGVyZCBhbHdheXMgcHV0cyB0aGUgYWN0aXZlIG9uZSBhdCB0aGUgZW5kXG4gICAgICBjb25zdCBmb290ZXI6IEVsZW1lbnQgPSBmb290ZXJzW2Zvb3RlcnMubGVuZ3RoIC0gMV07XG4gICAgICAvLyBnZW5lcmF0ZSBzdGVwcyBodG1sIGVsZW1lbnRcbiAgICAgIGNvbnN0IHByb2dyZXNzOiBIVE1MU3BhbkVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgICBwcm9ncmVzcy5jbGFzc05hbWUgPSAnc2hlcGhlcmQtcHJvZ3Jlc3MnO1xuICAgICAgcHJvZ3Jlc3MuaW5uZXJUZXh0ID0gYCR7dGhpcy5zaGVwaGVyZFRvdXIuY3VycmVudFN0ZXAub3B0aW9ucy5jb3VudH0vJHtzdGVwVG90YWx9YDtcbiAgICAgIC8vIGluc2VydCBpbnRvIHRoZSBmb290ZXIgYmVmb3JlIHRoZSBmaXJzdCBidXR0b25cbiAgICAgIGZvb3Rlci5pbnNlcnRCZWZvcmUocHJvZ3Jlc3MsIGZvb3Rlci5xdWVyeVNlbGVjdG9yKCcuc2hlcGhlcmQtYnV0dG9uJykpO1xuICAgIH07XG5cbiAgICBsZXQgc3RlcFRvdGFsID0gMDtcbiAgICBjb25zdCBzdGVwczogSVRvdXJTdGVwW10gPSBvcmlnaW5hbFN0ZXBzLm1hcCgoc3RlcDogSVRvdXJTdGVwKSA9PiB7XG4gICAgICBsZXQgc2hvd1Byb2dyZXNzOiAoKSA9PiB2b2lkID0gKCkgPT4ge1xuICAgICAgICAvL1xuICAgICAgfTtcbiAgICAgIGlmIChzdGVwLmF0dGFjaFRvT3B0aW9ucz8uc2tpcEZyb21TdGVwQ291bnQgPT09IHRydWUpIHtcbiAgICAgICAgc2hvd1Byb2dyZXNzID0gZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfTtcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIHN0ZXAuYXR0YWNoVG9PcHRpb25zPy5za2lwRnJvbVN0ZXBDb3VudCA9PT0gdW5kZWZpbmVkIHx8XG4gICAgICAgIHN0ZXAuYXR0YWNoVG9PcHRpb25zPy5za2lwRnJvbVN0ZXBDb3VudCA9PT0gZmFsc2VcbiAgICAgICkge1xuICAgICAgICBzdGVwLmNvdW50ID0gKytzdGVwVG90YWw7XG4gICAgICAgIHNob3dQcm9ncmVzcyA9IGFwcGVuZFByb2dyZXNzRnVuYy5iaW5kKHRoaXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oe30sIHN0ZXAsIHtcbiAgICAgICAgd2hlbjoge1xuICAgICAgICAgIHNob3c6IHNob3dQcm9ncmVzcyxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgY29uc3QgZmluaXNoQnV0dG9uOiBUb3VyU3RlcEJ1dHRvbiA9IHtcbiAgICAgIHRleHQ6IGZpbmlzaExhYmVsLFxuICAgICAgYWN0aW9uOiB0aGlzWydmaW5pc2gnXS5iaW5kKHRoaXMpLFxuICAgICAgY2xhc3NlczogTUFUX0JVVFRPTixcbiAgICB9O1xuXG4gICAgY29uc3Qgdm9pZEJ1dHRvbjogVG91clN0ZXBCdXR0b24gPSB7XG4gICAgICB0ZXh0OiAnJyxcbiAgICAgIGFjdGlvbigpOiB2b2lkIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSxcbiAgICAgIGNsYXNzZXM6IE1BVF9CVVRUT05fSU5WSVNJQkxFLFxuICAgIH07XG5cbiAgICAvLyBsaXN0ZW4gdG8gdGhlIGRlc3Ryb3llZCBldmVudCB0byBjbGVhbiB1cCBhbGwgdGhlIHN0cmVhbXNcbiAgICB0aGlzLl9kZXN0cm95ZWRFdmVudCQucGlwZShmaXJzdCgpKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgYmFja0V2ZW50JC5jb21wbGV0ZSgpO1xuICAgICAgZm9yd2FyZEV2ZW50JC5jb21wbGV0ZSgpO1xuICAgICAgZGVzdHJveWVkRXZlbnQkLm5leHQoKTtcbiAgICAgIGRlc3Ryb3llZEV2ZW50JC5jb21wbGV0ZSgpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgdG90YWxTdGVwczogbnVtYmVyID0gc3RlcHMubGVuZ3RoO1xuICAgIHN0ZXBzLmZvckVhY2goKHN0ZXA6IElUb3VyU3RlcCwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgLy8gY3JlYXRlIGJ1dHRvbnMgc3BlY2lmaWMgZm9yIHRoZSBzdGVwXG4gICAgICAvLyB0aGlzIGlzIGRvbmUgdG8gY3JlYXRlIG1vcmUgY29udHJvbCBvbiBldmVudHNcbiAgICAgIGNvbnN0IG5leHRCdXR0b246IFRvdXJTdGVwQnV0dG9uID0ge1xuICAgICAgICB0ZXh0OiAnY2hldnJvbl9yaWdodCcsXG4gICAgICAgIGFjdGlvbjogKCkgPT4ge1xuICAgICAgICAgIC8vIGludGVyY2VwdCB0aGUgbmV4dCBhY3Rpb24gYW5kIHRyaWdnZXIgZXZlbnRcbiAgICAgICAgICBmb3J3YXJkRXZlbnQkLm5leHQoKTtcbiAgICAgICAgICB0aGlzLnNoZXBoZXJkVG91ci5uZXh0KCk7XG4gICAgICAgIH0sXG4gICAgICAgIGNsYXNzZXM6IE1BVF9JQ09OX0JVVFRPTixcbiAgICAgIH07XG4gICAgICBjb25zdCBiYWNrQnV0dG9uOiBUb3VyU3RlcEJ1dHRvbiA9IHtcbiAgICAgICAgdGV4dDogJ2NoZXZyb25fbGVmdCcsXG4gICAgICAgIGFjdGlvbjogKCkgPT4ge1xuICAgICAgICAgIC8vIGludGVyY2VwdCB0aGUgYmFjayBhY3Rpb24gYW5kIHRyaWdnZXIgZXZlbnRcbiAgICAgICAgICBiYWNrRXZlbnQkLm5leHQoKTtcbiAgICAgICAgICBfYmFja0Zsb3cgPSB0cnVlO1xuICAgICAgICAgIC8vIGNoZWNrIGlmICdnb0JhY2tUbycgaXMgc2V0IHRvIGp1bXAgdG8gYSBwYXJ0aWN1bGFyIHN0ZXAsIGVsc2UganVzdCBnbyBiYWNrXG4gICAgICAgICAgaWYgKHN0ZXAuYXR0YWNoVG9PcHRpb25zICYmIHN0ZXAuYXR0YWNoVG9PcHRpb25zLmdvQmFja1RvKSB7XG4gICAgICAgICAgICB0aGlzLnNoZXBoZXJkVG91ci5zaG93KHN0ZXAuYXR0YWNoVG9PcHRpb25zLmdvQmFja1RvLCBmYWxzZSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2hlcGhlcmRUb3VyLmJhY2soKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGNsYXNzZXM6XG4gICAgICAgICAgc3RlcC5hZHZhbmN