UNPKG

@covalent/guided-tour

Version:
438 lines 67.3 kB
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