@nicky-lenaers/ngx-scroll-to
Version:
A simple Angular 4+ plugin enabling you to smooth scroll to any element on your page and enhance scroll-based features in your app.
273 lines • 32.9 kB
JavaScript
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { ScrollToAnimation } from './scroll-to-animation';
import { DEFAULTS, isElementRef, isNativeElement, isNumber, isString, isWindow, stripHash } from './scroll-to-helpers';
import { ReplaySubject, throwError } from 'rxjs';
import * as i0 from "@angular/core";
/**
* The Scroll To Service handles starting, interrupting
* and ending the actual Scroll Animation. It provides
* some utilities to find the proper HTML Element on a
* given page to setup Event Listeners and calculate
* distances for the Animation.
*/
export class ScrollToService {
/**
* Construct and setup required paratemeters.
*
* @param document A Reference to the Document
* @param platformId Angular Platform ID
*/
constructor(document, platformId) {
this.document = document;
this.platformId = platformId;
this.interruptiveEvents = ['mousewheel', 'DOMMouseScroll', 'touchstart'];
}
/**
* Target an Element to scroll to. Notice that the `TimeOut` decorator
* ensures the executing to take place in the next Angular lifecycle.
* This allows for scrolling to elements that are e.g. initially hidden
* by means of `*ngIf`, but ought to be scrolled to eventually.
*
* @todo type 'any' in Observable should become custom type like 'ScrollToEvent' (base class), see issue comment:
* - https://github.com/nicky-lenaers/ngx-scroll-to/issues/10#issuecomment-317198481
*
* @param options Configuration Object
* @returns Observable
*/
scrollTo(options) {
if (!isPlatformBrowser(this.platformId)) {
return new ReplaySubject().asObservable();
}
return this.start(options);
}
/**
* Start a new Animation.
*
* @todo Emit proper events from subscription
*
* @param options Configuration Object
* @returns Observable
*/
start(options) {
// Merge config with default values
const mergedConfigOptions = {
...DEFAULTS,
...options
};
if (this.animation) {
this.animation.stop();
}
const targetNode = this.getNode(mergedConfigOptions.target);
if (mergedConfigOptions.target && !targetNode) {
return throwError(() => new Error('Unable to find Target Element'));
}
const container = this.getContainer(mergedConfigOptions, targetNode);
if (mergedConfigOptions.container && !container) {
return throwError(() => new Error('Unable to find Container Element'));
}
const listenerTarget = this.getListenerTarget(container) || window;
let to = container ? container.getBoundingClientRect().top : 0;
if (targetNode) {
to = isWindow(listenerTarget) ?
window.scrollY + targetNode.getBoundingClientRect().top :
targetNode.getBoundingClientRect().top;
}
// Create Animation
this.animation = new ScrollToAnimation(container, listenerTarget, isWindow(listenerTarget), to, mergedConfigOptions, isPlatformBrowser(this.platformId));
const onInterrupt = () => this.animation.stop();
this.addInterruptiveEventListeners(listenerTarget, onInterrupt);
// Start Animation
const animation$ = this.animation.start();
this.subscribeToAnimation(animation$, listenerTarget, onInterrupt);
return animation$;
}
/**
* Subscribe to the events emitted from the Scrolling
* Animation. Events might be used for e.g. unsubscribing
* once finished.
*
* @param animation$ The Animation Observable
* @param listenerTarget The Listener Target for events
* @param onInterrupt The handler for Interruptive Events
* @returns Void
*/
subscribeToAnimation(animation$, listenerTarget, onInterrupt) {
const subscription = animation$
.subscribe({
complete: () => {
this.removeInterruptiveEventListeners(this.interruptiveEvents, listenerTarget, onInterrupt);
subscription.unsubscribe();
}
});
}
/**
* Get the container HTML Element in which
* the scrolling should happen.
*
* @param options The Merged Configuration Object
* @param targetNode the targeted HTMLElement
*/
getContainer(options, targetNode) {
let container = null;
if (options.container) {
container = this.getNode(options.container, true);
}
else if (targetNode) {
container = this.getFirstScrollableParent(targetNode);
}
return container;
}
/**
* Add listeners for the Animation Interruptive Events
* to the Listener Target.
*
* @param events List of events to listen to
* @param listenerTarget Target to attach the listener on
* @param handler Handler for when the listener fires
* @returns Void
*/
addInterruptiveEventListeners(listenerTarget, handler) {
if (!listenerTarget) {
listenerTarget = window;
}
this.interruptiveEvents
.forEach(event => listenerTarget
.addEventListener(event, handler, this.supportPassive() ? { passive: true } : false));
}
/**
* Feature-detect support for passive event listeners.
*
* @returns Whether or not passive event listeners are supported
*/
supportPassive() {
let supportsPassive = false;
try {
const opts = Object.defineProperty({}, 'passive', {
get: () => {
supportsPassive = true;
}
});
window.addEventListener('testPassive', null, opts);
window.removeEventListener('testPassive', null, opts);
}
catch (e) {
}
return supportsPassive;
}
/**
* Remove listeners for the Animation Interrupt Event from
* the Listener Target. Specifying the correct handler prevents
* memory leaks and makes the allocated memory available for
* Garbage Collection.
*
* @param events List of Interruptive Events to remove
* @param listenerTarget Target to attach the listener on
* @param handler Handler for when the listener fires
* @returns Void
*/
removeInterruptiveEventListeners(events, listenerTarget, handler) {
if (!listenerTarget) {
listenerTarget = window;
}
events.forEach(event => listenerTarget.removeEventListener(event, handler));
}
/**
* Find the first scrollable parent Node of a given
* Element. The DOM Tree gets searched upwards
* to find this first scrollable parent. Parents might
* be ignored by CSS styles applied to the HTML Element.
*
* @param nativeElement The Element to search the DOM Tree upwards from
* @returns The first scrollable parent HTML Element
*/
getFirstScrollableParent(nativeElement) {
let style = window.getComputedStyle(nativeElement);
const overflowRegex = /(auto|scroll|overlay)/;
if (style.position === 'fixed') {
return null;
}
let parent = nativeElement;
while (parent.parentElement) {
parent = parent.parentElement;
style = window.getComputedStyle(parent);
if (style.position === 'absolute'
|| style.overflow === 'hidden'
|| style.overflowY === 'hidden') {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY)
|| parent.tagName === 'BODY') {
return parent;
}
}
return null;
}
/**
* Get the Target Node to scroll to.
*
* @param id The given ID of the node, either a string or
* an element reference
* @param allowBodyTag Indicate whether or not the Document Body is
* considered a valid Target Node
* @returns The Target Node to scroll to
*/
getNode(id, allowBodyTag = false) {
let targetNode;
if (isString(id)) {
if (allowBodyTag && (id === 'body' || id === 'BODY')) {
targetNode = this.document.body;
}
else {
targetNode = this.document.getElementById(stripHash(id));
}
}
else if (isNumber(id)) {
targetNode = this.document.getElementById(String(id));
}
else if (isElementRef(id)) {
targetNode = id.nativeElement;
}
else if (isNativeElement(id)) {
targetNode = id;
}
return targetNode;
}
/**
* Retrieve the Listener target. This Listener Target is used
* to attach Event Listeners on. In case of the target being
* the Document Body, we need the actual `window` to listen
* for events.
*
* @param container The HTML Container element
* @returns The Listener Target to attach events on
*/
getListenerTarget(container) {
if (!container) {
return null;
}
return this.isDocumentBody(container) ? window : container;
}
/**
* Test if a given HTML Element is the Document Body.
*
* @param element The given HTML Element
* @returns Whether or not the Element is the
* Document Body Element
*/
isDocumentBody(element) {
return element.tagName.toUpperCase() === 'BODY';
}
}
ScrollToService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.7", ngImport: i0, type: ScrollToService, deps: [{ token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
ScrollToService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.7", ngImport: i0, type: ScrollToService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.7", ngImport: i0, type: ScrollToService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [PLATFORM_ID]
}] }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Nyb2xsLXRvLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtc2Nyb2xsLXRvL3NyYy9saWIvc2Nyb2xsLXRvLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxRQUFRLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQVE5RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkgsT0FBTyxFQUFjLGFBQWEsRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7O0FBRTdEOzs7Ozs7R0FNRztBQUVILE1BQU0sT0FBTyxlQUFlO0lBaUIxQjs7Ozs7T0FLRztJQUNILFlBQzRCLFFBQWEsRUFDVixVQUFlO1FBRGxCLGFBQVEsR0FBUixRQUFRLENBQUs7UUFDVixlQUFVLEdBQVYsVUFBVSxDQUFLO1FBRTVDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxDQUFDLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxRQUFRLENBQUMsT0FBOEI7UUFFckMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRTtZQUN2QyxPQUFPLElBQUksYUFBYSxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUM7U0FDM0M7UUFFRCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVEOzs7Ozs7O09B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ZTtrQkFEM0IsVUFBVTs7MEJBeUJOLE1BQU07MkJBQUMsUUFBUTs7MEJBQ2YsTUFBTTsyQkFBQyxXQUFXIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0LCBJbmplY3RhYmxlLCBQTEFURk9STV9JRCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRE9DVU1FTlQsIGlzUGxhdGZvcm1Ccm93c2VyIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcblxuaW1wb3J0IHtcbiAgU2Nyb2xsVG9Db25maWdPcHRpb25zLFxuICBTY3JvbGxUb0NvbmZpZ09wdGlvbnNUYXJnZXQsXG4gIFNjcm9sbFRvTGlzdGVuZXJUYXJnZXQsXG4gIFNjcm9sbFRvVGFyZ2V0XG59IGZyb20gJy4vc2Nyb2xsLXRvLWNvbmZpZy5pbnRlcmZhY2UnO1xuaW1wb3J0IHsgU2Nyb2xsVG9BbmltYXRpb24gfSBmcm9tICcuL3Njcm9sbC10by1hbmltYXRpb24nO1xuaW1wb3J0IHsgREVGQVVMVFMsIGlzRWxlbWVudFJlZiwgaXNOYXRpdmVFbGVtZW50LCBpc051bWJlciwgaXNTdHJpbmcsIGlzV2luZG93LCBzdHJpcEhhc2ggfSBmcm9tICcuL3Njcm9sbC10by1oZWxwZXJzJztcbmltcG9ydCB7IE9ic2VydmFibGUsIFJlcGxheVN1YmplY3QsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJztcblxuLyoqXG4gKiBUaGUgU2Nyb2xsIFRvIFNlcnZpY2UgaGFuZGxlcyBzdGFydGluZywgaW50ZXJydXB0aW5nXG4gKiBhbmQgZW5kaW5nIHRoZSBhY3R1YWwgU2Nyb2xsIEFuaW1hdGlvbi4gSXQgcHJvdmlkZXNcbiAqIHNvbWUgdXRpbGl0aWVzIHRvIGZpbmQgdGhlIHByb3BlciBIVE1MIEVsZW1lbnQgb24gYVxuICogZ2l2ZW4gcGFnZSB0byBzZXR1cCBFdmVudCBMaXN0ZW5lcnMgYW5kIGNhbGN1bGF0ZVxuICogZGlzdGFuY2VzIGZvciB0aGUgQW5pbWF0aW9uLlxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgU2Nyb2xsVG9TZXJ2aWNlIHtcblxuICAvKipcbiAgICogVGhlIGFuaW1hdGlvbiB0aGF0IHByb3ZpZGVzIHRoZSBzY3JvbGxpbmdcbiAgICogdG8gaGFwcGVuIHNtb290aGx5IG92ZXIgdGltZS4gRGVmaW5pbmcgaXQgaGVyZVxuICAgKiBhbGxvd3MgZm9yIHVzYWdlIG9mIGUuZy4gYHN0YXJ0YCBhbmQgYHN0b3BgXG4gICAqIG1ldGhvZHMgd2l0aGluIHRoaXMgQW5ndWxhciBTZXJ2aWNlLlxuICAgKi9cbiAgcHJpdmF0ZSBhbmltYXRpb246IFNjcm9sbFRvQW5pbWF0aW9uO1xuXG4gIC8qKlxuICAgKiBJbnRlcnJ1cHRpdmUgRXZlbnRzIGFsbG93IHRvIHNjcm9sbGluZyBhbmltYXRpb25cbiAgICogdG8gYmUgaW50ZXJydXB0ZWQgYmVmb3JlIGl0IGlzIGZpbmlzaGVkLiBUaGUgbGlzdFxuICAgKiBvZiBJbnRlcnJ1cHRpdmUgRXZlbnRzIHJlcHJlc2VudHMgdGhvc2UuXG4gICAqL1xuICBwcml2YXRlIGludGVycnVwdGl2ZUV2ZW50czogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdCBhbmQgc2V0dXAgcmVxdWlyZWQgcGFyYXRlbWV0ZXJzLlxuICAgKlxuICAgKiBAcGFyYW0gZG9jdW1lbnQgICAgICAgICBBIFJlZmVyZW5jZSB0byB0aGUgRG9jdW1lbnRcbiAgICogQHBhcmFtIHBsYXRmb3JtSWQgICAgICAgQW5ndWxhciBQbGF0Zm9ybSBJRFxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgQEluamVjdChET0NVTUVOVCkgcHJpdmF0ZSBkb2N1bWVudDogYW55LFxuICAgIEBJbmplY3QoUExBVEZPUk1fSUQpIHByaXZhdGUgcGxhdGZvcm1JZDogYW55XG4gICkge1xuICAgIHRoaXMuaW50ZXJydXB0aXZlRXZlbnRzID0gWydtb3VzZXdoZWVsJywgJ0RPTU1vdXNlU2Nyb2xsJywgJ3RvdWNoc3RhcnQnXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUYXJnZXQgYW4gRWxlbWVudCB0byBzY3JvbGwgdG8uIE5vdGljZSB0aGF0IHRoZSBgVGltZU91dGAgZGVjb3JhdG9yXG4gICAqIGVuc3VyZXMgdGhlIGV4ZWN1dGluZyB0byB0YWtlIHBsYWNlIGluIHRoZSBuZXh0IEFuZ3VsYXIgbGlmZWN5Y2xlLlxuICAgKiBUaGlzIGFsbG93cyBmb3Igc2Nyb2xsaW5nIHRvIGVsZW1lbnRzIHRoYXQgYXJlIGUuZy4gaW5pdGlhbGx5IGhpZGRlblxuICAgKiBieSBtZWFucyBvZiBgKm5nSWZgLCBidXQgb3VnaHQgdG8gYmUgc2Nyb2xsZWQgdG8gZXZlbnR1YWxseS5cbiAgICpcbiAgICogQHRvZG8gdHlwZSAnYW55JyBpbiBPYnNlcnZhYmxlIHNob3VsZCBiZWNvbWUgY3VzdG9tIHR5cGUgbGlrZSAnU2Nyb2xsVG9FdmVudCcgKGJhc2UgY2xhc3MpLCBzZWUgaXNzdWUgY29tbWVudDpcbiAgICogIC0gaHR0cHM6Ly9naXRodWIuY29tL25pY2t5LWxlbmFlcnMvbmd4LXNjcm9sbC10by9pc3N1ZXMvMTAjaXNzdWVjb21tZW50LTMxNzE5ODQ4MVxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyAgICAgICAgIENvbmZpZ3VyYXRpb24gT2JqZWN0XG4gICAqIEByZXR1cm5zICAgICAgICAgICAgICAgT2JzZXJ2YWJsZVxuICAgKi9cbiAgc2Nyb2xsVG8ob3B0aW9uczogU2Nyb2xsVG9Db25maWdPcHRpb25zKTogT2JzZXJ2YWJsZTxhbnk+IHtcblxuICAgIGlmICghaXNQbGF0Zm9ybUJyb3dzZXIodGhpcy5wbGF0Zm9ybUlkKSkge1xuICAgICAgcmV0dXJuIG5ldyBSZXBsYXlTdWJqZWN0KCkuYXNPYnNlcnZhYmxlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuc3RhcnQob3B0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgYSBuZXcgQW5pbWF0aW9uLlxuICAgKlxuICAgKiBAdG9kbyBFbWl0IHByb3BlciBldmVudHMgZnJvbSBzdWJzY3JpcHRpb25cbiAgICpcbiAgICogQHBhcmFtIG9wdGlvbnMgICAgICAgICBDb25maWd1cmF0aW9uIE9iamVjdFxuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgIE9ic2VydmFibGVcbiAgICovXG4gIHByaXZhdGUgc3RhcnQob3B0aW9uczogU2Nyb2xsVG9Db25maWdPcHRpb25zKTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcblxuICAgIC8vIE1lcmdlIGNvbmZpZyB3aXRoIGRlZmF1bHQgdmFsdWVzXG4gICAgY29uc3QgbWVyZ2VkQ29uZmlnT3B0aW9ucyA9IHtcbiAgICAgIC4uLkRFRkFVTFRTIGFzIFNjcm9sbFRvQ29uZmlnT3B0aW9ucyxcbiAgICAgIC4uLm9wdGlvbnNcbiAgICB9IGFzIFNjcm9sbFRvQ29uZmlnT3B0aW9uc1RhcmdldDtcblxuICAgIGlmICh0aGlzLmFuaW1hdGlvbikge1xuICAgICAgdGhpcy5hbmltYXRpb24uc3RvcCgpO1xuICAgIH1cblxuICAgIGNvbnN0IHRhcmdldE5vZGUgPSB0aGlzLmdldE5vZGUobWVyZ2VkQ29uZmlnT3B0aW9ucy50YXJnZXQpO1xuICAgIGlmIChtZXJnZWRDb25maWdPcHRpb25zLnRhcmdldCAmJiAhdGFyZ2V0Tm9kZSkge1xuICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gbmV3IEVycm9yKCdVbmFibGUgdG8gZmluZCBUYXJnZXQgRWxlbWVudCcpKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb250YWluZXI6IEhUTUxFbGVtZW50ID0gdGhpcy5nZXRDb250YWluZXIobWVyZ2VkQ29uZmlnT3B0aW9ucywgdGFyZ2V0Tm9kZSk7XG4gICAgaWYgKG1lcmdlZENvbmZpZ09wdGlvbnMuY29udGFpbmVyICYmICFjb250YWluZXIpIHtcbiAgICAgIHJldHVybiB0aHJvd0Vycm9yKCgpID0+IG5ldyBFcnJvcignVW5hYmxlIHRvIGZpbmQgQ29udGFpbmVyIEVsZW1lbnQnKSk7XG4gICAgfVxuXG4gICAgY29uc3QgbGlzdGVuZXJUYXJnZXQgPSB0aGlzLmdldExpc3RlbmVyVGFyZ2V0KGNvbnRhaW5lcikgfHwgd2luZG93O1xuXG4gICAgbGV0IHRvID0gY29udGFpbmVyID8gY29udGFpbmVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCA6IDA7XG5cbiAgICBpZiAodGFyZ2V0Tm9kZSkge1xuICAgICAgdG8gPSBpc1dpbmRvdyhsaXN0ZW5lclRhcmdldCkgP1xuICAgICAgICB3aW5kb3cuc2Nyb2xsWSArIHRhcmdldE5vZGUuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIDpcbiAgICAgICAgdGFyZ2V0Tm9kZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3A7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIEFuaW1hdGlvblxuICAgIHRoaXMuYW5pbWF0aW9uID0gbmV3IFNjcm9sbFRvQW5pbWF0aW9uKFxuICAgICAgY29udGFpbmVyLFxuICAgICAgbGlzdGVuZXJUYXJnZXQsXG4gICAgICBpc1dpbmRvdyhsaXN0ZW5lclRhcmdldCksXG4gICAgICB0byxcbiAgICAgIG1lcmdlZENvbmZpZ09wdGlvbnMsXG4gICAgICBpc1BsYXRmb3JtQnJvd3Nlcih0aGlzLnBsYXRmb3JtSWQpXG4gICAgKTtcbiAgICBjb25zdCBvbkludGVycnVwdCA9ICgpID0+IHRoaXMuYW5pbWF0aW9uLnN0b3AoKTtcbiAgICB0aGlzLmFkZEludGVycnVwdGl2ZUV2ZW50TGlzdGVuZXJzKGxpc3RlbmVyVGFyZ2V0LCBvbkludGVycnVwdCk7XG5cbiAgICAvLyBTdGFydCBBbmltYXRpb25cbiAgICBjb25zdCBhbmltYXRpb24kID0gdGhpcy5hbmltYXRpb24uc3RhcnQoKTtcbiAgICB0aGlzLnN1YnNjcmliZVRvQW5pbWF0aW9uKGFuaW1hdGlvbiQsIGxpc3RlbmVyVGFyZ2V0LCBvbkludGVycnVwdCk7XG5cbiAgICByZXR1cm4gYW5pbWF0aW9uJDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gdGhlIGV2ZW50cyBlbWl0dGVkIGZyb20gdGhlIFNjcm9sbGluZ1xuICAgKiBBbmltYXRpb24uIEV2ZW50cyBtaWdodCBiZSB1c2VkIGZvciBlLmcuIHVuc3Vic2NyaWJpbmdcbiAgICogb25jZSBmaW5pc2hlZC5cbiAgICpcbiAgICogQHBhcmFtIGFuaW1hdGlvbiQgICAgICAgICAgICAgIFRoZSBBbmltYXRpb24gT2JzZXJ2YWJsZVxuICAgKiBAcGFyYW0gbGlzdGVuZXJUYXJnZXQgICAgICAgICAgVGhlIExpc3RlbmVyIFRhcmdldCBmb3IgZXZlbnRzXG4gICAqIEBwYXJhbSBvbkludGVycnVwdCAgICAgICAgICAgICBUaGUgaGFuZGxlciBmb3IgSW50ZXJydXB0aXZlIEV2ZW50c1xuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgICAgICAgICAgVm9pZFxuICAgKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVUb0FuaW1hdGlvbihcbiAgICBhbmltYXRpb24kOiBPYnNlcnZhYmxlPGFueT4sXG4gICAgbGlzdGVuZXJUYXJnZXQ6IFNjcm9sbFRvTGlzdGVuZXJUYXJnZXQsXG4gICAgb25JbnRlcnJ1cHQ6IEV2ZW50TGlzdGVuZXJPckV2ZW50TGlzdGVuZXJPYmplY3RcbiAgKSB7XG4gICAgY29uc3Qgc3Vic2NyaXB0aW9uID0gYW5pbWF0aW9uJFxuICAgICAgLnN1YnNjcmliZShcbiAgICAgICAge1xuICAgICAgICAgIGNvbXBsZXRlOiAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnJlbW92ZUludGVycnVwdGl2ZUV2ZW50TGlzdGVuZXJzKHRoaXMuaW50ZXJydXB0aXZlRXZlbnRzLCBsaXN0ZW5lclRhcmdldCwgb25JbnRlcnJ1cHQpO1xuICAgICAgICAgICAgc3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY29udGFpbmVyIEhUTUwgRWxlbWVudCBpbiB3aGljaFxuICAgKiB0aGUgc2Nyb2xsaW5nIHNob3VsZCBoYXBwZW4uXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zICAgICAgICAgVGhlIE1lcmdlZCBDb25maWd1cmF0aW9uIE9iamVjdFxuICAgKiBAcGFyYW0gdGFyZ2V0Tm9kZSAgICB0aGUgdGFyZ2V0ZWQgSFRNTEVsZW1lbnRcbiAgICovXG4gIHByaXZhdGUgZ2V0Q29udGFpbmVyKG9wdGlvbnM6IFNjcm9sbFRvQ29uZmlnT3B0aW9ucywgdGFyZ2V0Tm9kZTogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudCB8IG51bGwge1xuXG4gICAgbGV0IGNvbnRhaW5lcjogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICAgIGlmIChvcHRpb25zLmNvbnRhaW5lcikge1xuICAgICAgY29udGFpbmVyID0gdGhpcy5nZXROb2RlKG9wdGlvbnMuY29udGFpbmVyLCB0cnVlKTtcbiAgICB9IGVsc2UgaWYgKHRhcmdldE5vZGUpIHtcbiAgICAgIGNvbnRhaW5lciA9IHRoaXMuZ2V0Rmlyc3RTY3JvbGxhYmxlUGFyZW50KHRhcmdldE5vZGUpO1xuICAgIH1cblxuICAgIHJldHVybiBjb250YWluZXI7XG4gIH1cblxuICAvKipcbiAgICogQWRkIGxpc3RlbmVycyBmb3IgdGhlIEFuaW1hdGlvbiBJbnRlcnJ1cHRpdmUgRXZlbnRzXG4gICAqIHRvIHRoZSBMaXN0ZW5lciBUYXJnZXQuXG4gICAqXG4gICAqIEBwYXJhbSBldmVudHMgICAgICAgICAgICBMaXN0IG9mIGV2ZW50cyB0byBsaXN0ZW4gdG9cbiAgICogQHBhcmFtIGxpc3RlbmVyVGFyZ2V0ICAgIFRhcmdldCB0byBhdHRhY2ggdGhlIGxpc3RlbmVyIG9uXG4gICAqIEBwYXJhbSBoYW5kbGVyICAgICAgICAgICBIYW5kbGVyIGZvciB3aGVuIHRoZSBsaXN0ZW5lciBmaXJlc1xuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgICAgVm9pZFxuICAgKi9cbiAgcHJpdmF0ZSBhZGRJbnRlcnJ1cHRpdmVFdmVudExpc3RlbmVycyhcbiAgICBsaXN0ZW5lclRhcmdldDogU2Nyb2xsVG9MaXN0ZW5lclRhcmdldCxcbiAgICBoYW5kbGVyOiBFdmVudExpc3RlbmVyT3JFdmVudExpc3RlbmVyT2JqZWN0KTogdm9pZCB7XG5cbiAgICBpZiAoIWxpc3RlbmVyVGFyZ2V0KSB7XG4gICAgICBsaXN0ZW5lclRhcmdldCA9IHdpbmRvdztcbiAgICB9XG5cbiAgICB0aGlzLmludGVycnVwdGl2ZUV2ZW50c1xuICAgICAgLmZvckVhY2goZXZlbnQgPT4gbGlzdGVuZXJUYXJnZXRcbiAgICAgICAgLmFkZEV2ZW50TGlzdGVuZXIoZXZlbnQsIGhhbmRsZXIsIHRoaXMuc3VwcG9ydFBhc3NpdmUoKSA/IHtwYXNzaXZlOiB0cnVlfSA6IGZhbHNlKSk7XG4gIH1cblxuICAvKipcbiAgICogRmVhdHVyZS1kZXRlY3Qgc3VwcG9ydCBmb3IgcGFzc2l2ZSBldmVudCBsaXN0ZW5lcnMuXG4gICAqXG4gICAqIEByZXR1cm5zICAgICAgIFdoZXRoZXIgb3Igbm90IHBhc3NpdmUgZXZlbnQgbGlzdGVuZXJzIGFyZSBzdXBwb3J0ZWRcbiAgICovXG4gIHByaXZhdGUgc3VwcG9ydFBhc3NpdmUoKTogYm9vbGVhbiB7XG5cbiAgICBsZXQgc3VwcG9ydHNQYXNzaXZlID0gZmFsc2U7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgb3B0cyA9IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh7fSwgJ3Bhc3NpdmUnLCB7XG4gICAgICAgIGdldDogKCkgPT4ge1xuICAgICAgICAgIHN1cHBvcnRzUGFzc2l2ZSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Rlc3RQYXNzaXZlJywgbnVsbCwgb3B0cyk7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigndGVzdFBhc3NpdmUnLCBudWxsLCBvcHRzKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgfVxuXG4gICAgcmV0dXJuIHN1cHBvcnRzUGFzc2l2ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgbGlzdGVuZXJzIGZvciB0aGUgQW5pbWF0aW9uIEludGVycnVwdCBFdmVudCBmcm9tXG4gICAqIHRoZSBMaXN0ZW5lciBUYXJnZXQuIFNwZWNpZnlpbmcgdGhlIGNvcnJlY3QgaGFuZGxlciBwcmV2ZW50c1xuICAgKiBtZW1vcnkgbGVha3MgYW5kIG1ha2VzIHRoZSBhbGxvY2F0ZWQgbWVtb3J5IGF2YWlsYWJsZSBmb3JcbiAgICogR2FyYmFnZSBDb2xsZWN0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gZXZlbnRzICAgICAgICAgICAgTGlzdCBvZiBJbnRlcnJ1cHRpdmUgRXZlbnRzIHRvIHJlbW92ZVxuICAgKiBAcGFyYW0gbGlzdGVuZXJUYXJnZXQgICAgVGFyZ2V0IHRvIGF0dGFjaCB0aGUgbGlzdGVuZXIgb25cbiAgICogQHBhcmFtIGhhbmRsZXIgICAgICAgICAgIEhhbmRsZXIgZm9yIHdoZW4gdGhlIGxpc3RlbmVyIGZpcmVzXG4gICAqIEByZXR1cm5zICAgICAgICAgICAgICAgICBWb2lkXG4gICAqL1xuICBwcml2YXRlIHJlbW92ZUludGVycnVwdGl2ZUV2ZW50TGlzdGVuZXJzKFxuICAgIGV2ZW50czogc3RyaW5nW10sXG4gICAgbGlzdGVuZXJUYXJnZXQ6IFNjcm9sbFRvTGlzdGVuZXJUYXJnZXQsXG4gICAgaGFuZGxlcjogRXZlbnRMaXN0ZW5lck9yRXZlbnRMaXN0ZW5lck9iamVjdCk6IHZvaWQge1xuXG4gICAgaWYgKCFsaXN0ZW5lclRhcmdldCkge1xuICAgICAgbGlzdGVuZXJUYXJnZXQgPSB3aW5kb3c7XG4gICAgfVxuICAgIGV2ZW50cy5mb3JFYWNoKGV2ZW50ID0+IGxpc3RlbmVyVGFyZ2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoZXZlbnQsIGhhbmRsZXIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kIHRoZSBmaXJzdCBzY3JvbGxhYmxlIHBhcmVudCBOb2RlIG9mIGEgZ2l2ZW5cbiAgICogRWxlbWVudC4gVGhlIERPTSBUcmVlIGdldHMgc2VhcmNoZWQgdXB3YXJkc1xuICAgKiB0byBmaW5kIHRoaXMgZmlyc3Qgc2Nyb2xsYWJsZSBwYXJlbnQuIFBhcmVudHMgbWlnaHRcbiAgICogYmUgaWdub3JlZCBieSBDU1Mgc3R5bGVzIGFwcGxpZWQgdG8gdGhlIEhUTUwgRWxlbWVudC5cbiAgICpcbiAgICogQHBhcmFtIG5hdGl2ZUVsZW1lbnQgICAgIFRoZSBFbGVtZW50IHRvIHNlYXJjaCB0aGUgRE9NIFRyZWUgdXB3YXJkcyBmcm9tXG4gICAqIEByZXR1cm5zICAgICAgICAgICAgICAgICBUaGUgZmlyc3Qgc2Nyb2xsYWJsZSBwYXJlbnQgSFRNTCBFbGVtZW50XG4gICAqL1xuICBwcml2YXRlIGdldEZpcnN0U2Nyb2xsYWJsZVBhcmVudChuYXRpdmVFbGVtZW50OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50IHtcblxuICAgIGxldCBzdHlsZTogQ1NTU3R5bGVEZWNsYXJhdGlvbiA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKG5hdGl2ZUVsZW1lbnQpO1xuXG4gICAgY29uc3Qgb3ZlcmZsb3dSZWdleDogUmVnRXhwID0gLyhhdXRvfHNjcm9sbHxvdmVybGF5KS87XG5cbiAgICBpZiAoc3R5bGUucG9zaXRpb24gPT09ICdmaXhlZCcpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGxldCBwYXJlbnQgPSBuYXRpdmVFbGVtZW50O1xuICAgIHdoaWxlIChwYXJlbnQucGFyZW50RWxlbWVudCkge1xuICAgICAgcGFyZW50ID0gcGFyZW50LnBhcmVudEVsZW1lbnQ7XG4gICAgICBzdHlsZSA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKHBhcmVudCk7XG5cbiAgICAgIGlmIChzdHlsZS5wb3NpdGlvbiA9PT0gJ2Fic29sdXRlJ1xuICAgICAgICB8fCBzdHlsZS5vdmVyZmxvdyA9PT0gJ2hpZGRlbidcbiAgICAgICAgfHwgc3R5bGUub3ZlcmZsb3dZID09PSAnaGlkZGVuJykge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKG92ZXJmbG93UmVnZXgudGVzdChzdHlsZS5vdmVyZmxvdyArIHN0eWxlLm92ZXJmbG93WSlcbiAgICAgICAgfHwgcGFyZW50LnRhZ05hbWUgPT09ICdCT0RZJykge1xuICAgICAgICByZXR1cm4gcGFyZW50O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgVGFyZ2V0IE5vZGUgdG8gc2Nyb2xsIHRvLlxuICAgKlxuICAgKiBAcGFyYW0gaWQgICAgICAgICAgICAgIFRoZSBnaXZlbiBJRCBvZiB0aGUgbm9kZSwgZWl0aGVyIGEgc3RyaW5nIG9yXG4gICAqICAgICAgICAgICAgICAgICAgICAgICAgYW4gZWxlbWVudCByZWZlcmVuY2VcbiAgICogQHBhcmFtIGFsbG93Qm9keVRhZyAgICBJbmRpY2F0ZSB3aGV0aGVyIG9yIG5vdCB0aGUgRG9jdW1lbnQgQm9keSBpc1xuICAgKiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNpZGVyZWQgYSB2YWxpZCBUYXJnZXQgTm9kZVxuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgIFRoZSBUYXJnZXQgTm9kZSB0byBzY3JvbGwgdG9cbiAgICovXG4gIHByaXZhdGUgZ2V0Tm9kZShpZDogU2Nyb2xsVG9UYXJnZXQsIGFsbG93Qm9keVRhZzogYm9vbGVhbiA9IGZhbHNlKTogSFRNTEVsZW1lbnQge1xuXG4gICAgbGV0IHRhcmdldE5vZGU6IEhUTUxFbGVtZW50O1xuXG4gICAgaWYgKGlzU3RyaW5nKGlkKSkge1xuICAgICAgaWYgKGFsbG93Qm9keVRhZyAmJiAoaWQgPT09ICdib2R5JyB8fCBpZCA9PT0gJ0JPRFknKSkge1xuICAgICAgICB0YXJnZXROb2RlID0gdGhpcy5kb2N1bWVudC5ib2R5O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGFyZ2V0Tm9kZSA9IHRoaXMuZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoc3RyaXBIYXNoKGlkKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChpc051bWJlcihpZCkpIHtcbiAgICAgIHRhcmdldE5vZGUgPSB0aGlzLmRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFN0cmluZyhpZCkpO1xuICAgIH0gZWxzZSBpZiAoaXNFbGVtZW50UmVmKGlkKSkge1xuICAgICAgdGFyZ2V0Tm9kZSA9IGlkLm5hdGl2ZUVsZW1lbnQ7XG4gICAgfSBlbHNlIGlmIChpc05hdGl2ZUVsZW1lbnQoaWQpKSB7XG4gICAgICB0YXJnZXROb2RlID0gaWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRhcmdldE5vZGU7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmUgdGhlIExpc3RlbmVyIHRhcmdldC4gVGhpcyBMaXN0ZW5lciBUYXJnZXQgaXMgdXNlZFxuICAgKiB0byBhdHRhY2ggRXZlbnQgTGlzdGVuZXJzIG9uLiBJbiBjYXNlIG9mIHRoZSB0YXJnZXQgYmVpbmdcbiAgICogdGhlIERvY3VtZW50IEJvZHksIHdlIG5lZWQgdGhlIGFjdHVhbCBgd2luZG93YCB0byBsaXN0ZW5cbiAgICogZm9yIGV2ZW50cy5cbiAgICpcbiAgICogQHBhcmFtIGNvbnRhaW5lciAgICAgICAgICAgVGhlIEhUTUwgQ29udGFpbmVyIGVsZW1lbnRcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICAgICAgVGhlIExpc3RlbmVyIFRhcmdldCB0byBhdHRhY2ggZXZlbnRzIG9uXG4gICAqL1xuICBwcml2YXRlIGdldExpc3RlbmVyVGFyZ2V0KGNvbnRhaW5lcjogSFRNTEVsZW1lbnQpOiBTY3JvbGxUb0xpc3RlbmVyVGFyZ2V0IHtcbiAgICBpZiAoIWNvbnRhaW5lcikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmlzRG9jdW1lbnRCb2R5KGNvbnRhaW5lcikgPyB3aW5kb3cgOiBjb250YWluZXI7XG4gIH1cblxuICAvKipcbiAgICogVGVzdCBpZiBhIGdpdmVuIEhUTUwgRWxlbWVudCBpcyB0aGUgRG9jdW1lbnQgQm9keS5cbiAgICpcbiAgICogQHBhcmFtIGVsZW1lbnQgICAgICAgICAgICAgVGhlIGdpdmVuIEhUTUwgRWxlbWVudFxuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgICAgICBXaGV0aGVyIG9yIG5vdCB0aGUgRWxlbWVudCBpcyB0aGVcbiAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgRG9jdW1lbnQgQm9keSBFbGVtZW50XG4gICAqL1xuICBwcml2YXRlIGlzRG9jdW1lbnRCb2R5KGVsZW1lbnQ6IEhUTUxFbGVtZW50KTogZWxlbWVudCBpcyBIVE1MQm9keUVsZW1lbnQge1xuICAgIHJldHVybiBlbGVtZW50LnRhZ05hbWUudG9VcHBlckNhc2UoKSA9PT0gJ0JPRFknO1xuICB9XG59XG4iXX0=