UNPKG

@paulvmoreau/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. This is a fork from @nicky-lenaers' scroll to module that supports angular 10 and angular 11

273 lines 32.8 kB
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('Unable to find Target Element'); } const container = this.getContainer(mergedConfigOptions, targetNode); if (mergedConfigOptions.container && !container) { return throwError('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(() => { }, () => { }, () => { 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.1.3", ngImport: i0, type: ScrollToService, deps: [{ token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); ScrollToService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.3", ngImport: i0, type: ScrollToService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.3", 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Nyb2xsLXRvLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtc2Nyb2xsLXRvL3NyYy9saWIvc2Nyb2xsLXRvLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxRQUFRLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUc5RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkgsT0FBTyxFQUFjLGFBQWEsRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7O0FBRTdEOzs7Ozs7R0FNRztBQUVILE1BQU0sT0FBTyxlQUFlO0lBaUIxQjs7Ozs7T0FLRztJQUNILFlBQzRCLFFBQWEsRUFDVixVQUFlO1FBRGxCLGFBQVEsR0FBUixRQUFRLENBQUs7UUFDVixlQUFVLEdBQVYsVUFBVSxDQUFLO1FBRTVDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxDQUFDLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxRQUFRLENBQUMsT0FBOEI7UUFFckMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRTtZQUN2QyxPQUFPLElBQUksYUFBYSxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUM7U0FDM0M7UUFFRCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVEOzs7Ozs7O09B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Q2YsTUFBTTsyQkFBQyxXQUFXIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0LCBJbmplY3RhYmxlLCBQTEFURk9STV9JRCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRE9DVU1FTlQsIGlzUGxhdGZvcm1Ccm93c2VyIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcblxuaW1wb3J0IHsgU2Nyb2xsVG9Db25maWdPcHRpb25zLCBTY3JvbGxUb0NvbmZpZ09wdGlvbnNUYXJnZXQsIFNjcm9sbFRvTGlzdGVuZXJUYXJnZXQsIFNjcm9sbFRvVGFyZ2V0IH0gZnJvbSAnLi9zY3JvbGwtdG8tY29uZmlnLmludGVyZmFjZSc7XG5pbXBvcnQgeyBTY3JvbGxUb0FuaW1hdGlvbiB9IGZyb20gJy4vc2Nyb2xsLXRvLWFuaW1hdGlvbic7XG5pbXBvcnQgeyBERUZBVUxUUywgaXNFbGVtZW50UmVmLCBpc05hdGl2ZUVsZW1lbnQsIGlzTnVtYmVyLCBpc1N0cmluZywgaXNXaW5kb3csIHN0cmlwSGFzaCB9IGZyb20gJy4vc2Nyb2xsLXRvLWhlbHBlcnMnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgUmVwbGF5U3ViamVjdCwgdGhyb3dFcnJvciB9IGZyb20gJ3J4anMnO1xuXG4vKipcbiAqIFRoZSBTY3JvbGwgVG8gU2VydmljZSBoYW5kbGVzIHN0YXJ0aW5nLCBpbnRlcnJ1cHRpbmdcbiAqIGFuZCBlbmRpbmcgdGhlIGFjdHVhbCBTY3JvbGwgQW5pbWF0aW9uLiBJdCBwcm92aWRlc1xuICogc29tZSB1dGlsaXRpZXMgdG8gZmluZCB0aGUgcHJvcGVyIEhUTUwgRWxlbWVudCBvbiBhXG4gKiBnaXZlbiBwYWdlIHRvIHNldHVwIEV2ZW50IExpc3RlbmVycyBhbmQgY2FsY3VsYXRlXG4gKiBkaXN0YW5jZXMgZm9yIHRoZSBBbmltYXRpb24uXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBTY3JvbGxUb1NlcnZpY2Uge1xuXG4gIC8qKlxuICAgKiBUaGUgYW5pbWF0aW9uIHRoYXQgcHJvdmlkZXMgdGhlIHNjcm9sbGluZ1xuICAgKiB0byBoYXBwZW4gc21vb3RobHkgb3ZlciB0aW1lLiBEZWZpbmluZyBpdCBoZXJlXG4gICAqIGFsbG93cyBmb3IgdXNhZ2Ugb2YgZS5nLiBgc3RhcnRgIGFuZCBgc3RvcGBcbiAgICogbWV0aG9kcyB3aXRoaW4gdGhpcyBBbmd1bGFyIFNlcnZpY2UuXG4gICAqL1xuICBwcml2YXRlIGFuaW1hdGlvbjogU2Nyb2xsVG9BbmltYXRpb247XG5cbiAgLyoqXG4gICAqIEludGVycnVwdGl2ZSBFdmVudHMgYWxsb3cgdG8gc2Nyb2xsaW5nIGFuaW1hdGlvblxuICAgKiB0byBiZSBpbnRlcnJ1cHRlZCBiZWZvcmUgaXQgaXMgZmluaXNoZWQuIFRoZSBsaXN0XG4gICAqIG9mIEludGVycnVwdGl2ZSBFdmVudHMgcmVwcmVzZW50cyB0aG9zZS5cbiAgICovXG4gIHByaXZhdGUgaW50ZXJydXB0aXZlRXZlbnRzOiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogQ29uc3RydWN0IGFuZCBzZXR1cCByZXF1aXJlZCBwYXJhdGVtZXRlcnMuXG4gICAqXG4gICAqIEBwYXJhbSBkb2N1bWVudCAgICAgICAgIEEgUmVmZXJlbmNlIHRvIHRoZSBEb2N1bWVudFxuICAgKiBAcGFyYW0gcGxhdGZvcm1JZCAgICAgICBBbmd1bGFyIFBsYXRmb3JtIElEXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KERPQ1VNRU5UKSBwcml2YXRlIGRvY3VtZW50OiBhbnksXG4gICAgQEluamVjdChQTEFURk9STV9JRCkgcHJpdmF0ZSBwbGF0Zm9ybUlkOiBhbnlcbiAgKSB7XG4gICAgdGhpcy5pbnRlcnJ1cHRpdmVFdmVudHMgPSBbJ21vdXNld2hlZWwnLCAnRE9NTW91c2VTY3JvbGwnLCAndG91Y2hzdGFydCddO1xuICB9XG5cbiAgLyoqXG4gICAqIFRhcmdldCBhbiBFbGVtZW50IHRvIHNjcm9sbCB0by4gTm90aWNlIHRoYXQgdGhlIGBUaW1lT3V0YCBkZWNvcmF0b3JcbiAgICogZW5zdXJlcyB0aGUgZXhlY3V0aW5nIHRvIHRha2UgcGxhY2UgaW4gdGhlIG5leHQgQW5ndWxhciBsaWZlY3ljbGUuXG4gICAqIFRoaXMgYWxsb3dzIGZvciBzY3JvbGxpbmcgdG8gZWxlbWVudHMgdGhhdCBhcmUgZS5nLiBpbml0aWFsbHkgaGlkZGVuXG4gICAqIGJ5IG1lYW5zIG9mIGAqbmdJZmAsIGJ1dCBvdWdodCB0byBiZSBzY3JvbGxlZCB0byBldmVudHVhbGx5LlxuICAgKlxuICAgKiBAdG9kbyB0eXBlICdhbnknIGluIE9ic2VydmFibGUgc2hvdWxkIGJlY29tZSBjdXN0b20gdHlwZSBsaWtlICdTY3JvbGxUb0V2ZW50JyAoYmFzZSBjbGFzcyksIHNlZSBpc3N1ZSBjb21tZW50OlxuICAgKiAgLSBodHRwczovL2dpdGh1Yi5jb20vbmlja3ktbGVuYWVycy9uZ3gtc2Nyb2xsLXRvL2lzc3Vlcy8xMCNpc3N1ZWNvbW1lbnQtMzE3MTk4NDgxXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zICAgICAgICAgQ29uZmlndXJhdGlvbiBPYmplY3RcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICBPYnNlcnZhYmxlXG4gICAqL1xuICBzY3JvbGxUbyhvcHRpb25zOiBTY3JvbGxUb0NvbmZpZ09wdGlvbnMpOiBPYnNlcnZhYmxlPGFueT4ge1xuXG4gICAgaWYgKCFpc1BsYXRmb3JtQnJvd3Nlcih0aGlzLnBsYXRmb3JtSWQpKSB7XG4gICAgICByZXR1cm4gbmV3IFJlcGxheVN1YmplY3QoKS5hc09ic2VydmFibGUoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5zdGFydChvcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIG5ldyBBbmltYXRpb24uXG4gICAqXG4gICAqIEB0b2RvIEVtaXQgcHJvcGVyIGV2ZW50cyBmcm9tIHN1YnNjcmlwdGlvblxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyAgICAgICAgIENvbmZpZ3VyYXRpb24gT2JqZWN0XG4gICAqIEByZXR1cm5zICAgICAgICAgICAgICAgT2JzZXJ2YWJsZVxuICAgKi9cbiAgcHJpdmF0ZSBzdGFydChvcHRpb25zOiBTY3JvbGxUb0NvbmZpZ09wdGlvbnMpOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xuXG4gICAgLy8gTWVyZ2UgY29uZmlnIHdpdGggZGVmYXVsdCB2YWx1ZXNcbiAgICBjb25zdCBtZXJnZWRDb25maWdPcHRpb25zID0ge1xuICAgICAgLi4uREVGQVVMVFMgYXMgU2Nyb2xsVG9Db25maWdPcHRpb25zLFxuICAgICAgLi4ub3B0aW9uc1xuICAgIH0gYXMgU2Nyb2xsVG9Db25maWdPcHRpb25zVGFyZ2V0O1xuXG4gICAgaWYgKHRoaXMuYW5pbWF0aW9uKSB7XG4gICAgICB0aGlzLmFuaW1hdGlvbi5zdG9wKCk7XG4gICAgfVxuXG4gICAgY29uc3QgdGFyZ2V0Tm9kZSA9IHRoaXMuZ2V0Tm9kZShtZXJnZWRDb25maWdPcHRpb25zLnRhcmdldCk7XG4gICAgaWYgKG1lcmdlZENvbmZpZ09wdGlvbnMudGFyZ2V0ICYmICF0YXJnZXROb2RlKSB7XG4gICAgICByZXR1cm4gdGhyb3dFcnJvcignVW5hYmxlIHRvIGZpbmQgVGFyZ2V0IEVsZW1lbnQnKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb250YWluZXI6IEhUTUxFbGVtZW50ID0gdGhpcy5nZXRDb250YWluZXIobWVyZ2VkQ29uZmlnT3B0aW9ucywgdGFyZ2V0Tm9kZSk7XG4gICAgaWYgKG1lcmdlZENvbmZpZ09wdGlvbnMuY29udGFpbmVyICYmICFjb250YWluZXIpIHtcbiAgICAgIHJldHVybiB0aHJvd0Vycm9yKCdVbmFibGUgdG8gZmluZCBDb250YWluZXIgRWxlbWVudCcpO1xuICAgIH1cblxuICAgIGNvbnN0IGxpc3RlbmVyVGFyZ2V0ID0gdGhpcy5nZXRMaXN0ZW5lclRhcmdldChjb250YWluZXIpIHx8IHdpbmRvdztcblxuICAgIGxldCB0byA9IGNvbnRhaW5lciA/IGNvbnRhaW5lci5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgOiAwO1xuXG4gICAgaWYgKHRhcmdldE5vZGUpIHtcbiAgICAgIHRvID0gaXNXaW5kb3cobGlzdGVuZXJUYXJnZXQpID9cbiAgICAgICAgd2luZG93LnNjcm9sbFkgKyB0YXJnZXROb2RlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCA6XG4gICAgICAgIHRhcmdldE5vZGUuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZSBBbmltYXRpb25cbiAgICB0aGlzLmFuaW1hdGlvbiA9IG5ldyBTY3JvbGxUb0FuaW1hdGlvbihcbiAgICAgIGNvbnRhaW5lcixcbiAgICAgIGxpc3RlbmVyVGFyZ2V0LFxuICAgICAgaXNXaW5kb3cobGlzdGVuZXJUYXJnZXQpLFxuICAgICAgdG8sXG4gICAgICBtZXJnZWRDb25maWdPcHRpb25zLFxuICAgICAgaXNQbGF0Zm9ybUJyb3dzZXIodGhpcy5wbGF0Zm9ybUlkKVxuICAgICk7XG4gICAgY29uc3Qgb25JbnRlcnJ1cHQgPSAoKSA9PiB0aGlzLmFuaW1hdGlvbi5zdG9wKCk7XG4gICAgdGhpcy5hZGRJbnRlcnJ1cHRpdmVFdmVudExpc3RlbmVycyhsaXN0ZW5lclRhcmdldCwgb25JbnRlcnJ1cHQpO1xuXG4gICAgLy8gU3RhcnQgQW5pbWF0aW9uXG4gICAgY29uc3QgYW5pbWF0aW9uJCA9IHRoaXMuYW5pbWF0aW9uLnN0YXJ0KCk7XG4gICAgdGhpcy5zdWJzY3JpYmVUb0FuaW1hdGlvbihhbmltYXRpb24kLCBsaXN0ZW5lclRhcmdldCwgb25JbnRlcnJ1cHQpO1xuXG4gICAgcmV0dXJuIGFuaW1hdGlvbiQ7XG4gIH1cblxuICAvKipcbiAgICogU3Vic2NyaWJlIHRvIHRoZSBldmVudHMgZW1pdHRlZCBmcm9tIHRoZSBTY3JvbGxpbmdcbiAgICogQW5pbWF0aW9uLiBFdmVudHMgbWlnaHQgYmUgdXNlZCBmb3IgZS5nLiB1bnN1YnNjcmliaW5nXG4gICAqIG9uY2UgZmluaXNoZWQuXG4gICAqXG4gICAqIEBwYXJhbSBhbmltYXRpb24kICAgICAgICAgICAgICBUaGUgQW5pbWF0aW9uIE9ic2VydmFibGVcbiAgICogQHBhcmFtIGxpc3RlbmVyVGFyZ2V0ICAgICAgICAgIFRoZSBMaXN0ZW5lciBUYXJnZXQgZm9yIGV2ZW50c1xuICAgKiBAcGFyYW0gb25JbnRlcnJ1cHQgICAgICAgICAgICAgVGhlIGhhbmRsZXIgZm9yIEludGVycnVwdGl2ZSBFdmVudHNcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICAgICAgICAgIFZvaWRcbiAgICovXG4gIHByaXZhdGUgc3Vic2NyaWJlVG9BbmltYXRpb24oXG4gICAgYW5pbWF0aW9uJDogT2JzZXJ2YWJsZTxhbnk+LFxuICAgIGxpc3RlbmVyVGFyZ2V0OiBTY3JvbGxUb0xpc3RlbmVyVGFyZ2V0LFxuICAgIG9uSW50ZXJydXB0OiBFdmVudExpc3RlbmVyT3JFdmVudExpc3RlbmVyT2JqZWN0XG4gICkge1xuICAgIGNvbnN0IHN1YnNjcmlwdGlvbiA9IGFuaW1hdGlvbiRcbiAgICAgIC5zdWJzY3JpYmUoXG4gICAgICAgICgpID0+IHtcbiAgICAgICAgfSxcbiAgICAgICAgKCkgPT4ge1xuICAgICAgICB9LFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZW1vdmVJbnRlcnJ1cHRpdmVFdmVudExpc3RlbmVycyh0aGlzLmludGVycnVwdGl2ZUV2ZW50cywgbGlzdGVuZXJUYXJnZXQsIG9uSW50ZXJydXB0KTtcbiAgICAgICAgICBzdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICAgICAgfVxuICAgICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGNvbnRhaW5lciBIVE1MIEVsZW1lbnQgaW4gd2hpY2hcbiAgICogdGhlIHNjcm9sbGluZyBzaG91bGQgaGFwcGVuLlxuICAgKlxuICAgKiBAcGFyYW0gb3B0aW9ucyAgICAgICAgIFRoZSBNZXJnZWQgQ29uZmlndXJhdGlvbiBPYmplY3RcbiAgICogQHBhcmFtIHRhcmdldE5vZGUgICAgdGhlIHRhcmdldGVkIEhUTUxFbGVtZW50XG4gICAqL1xuICBwcml2YXRlIGdldENvbnRhaW5lcihvcHRpb25zOiBTY3JvbGxUb0NvbmZpZ09wdGlvbnMsIHRhcmdldE5vZGU6IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnQgfCBudWxsIHtcblxuICAgIGxldCBjb250YWluZXI6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGw7XG5cbiAgICBpZiAob3B0aW9ucy5jb250YWluZXIpIHtcbiAgICAgIGNvbnRhaW5lciA9IHRoaXMuZ2V0Tm9kZShvcHRpb25zLmNvbnRhaW5lciwgdHJ1ZSk7XG4gICAgfSBlbHNlIGlmICh0YXJnZXROb2RlKSB7XG4gICAgICBjb250YWluZXIgPSB0aGlzLmdldEZpcnN0U2Nyb2xsYWJsZVBhcmVudCh0YXJnZXROb2RlKTtcbiAgICB9XG5cbiAgICByZXR1cm4gY29udGFpbmVyO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBsaXN0ZW5lcnMgZm9yIHRoZSBBbmltYXRpb24gSW50ZXJydXB0aXZlIEV2ZW50c1xuICAgKiB0byB0aGUgTGlzdGVuZXIgVGFyZ2V0LlxuICAgKlxuICAgKiBAcGFyYW0gZXZlbnRzICAgICAgICAgICAgTGlzdCBvZiBldmVudHMgdG8gbGlzdGVuIHRvXG4gICAqIEBwYXJhbSBsaXN0ZW5lclRhcmdldCAgICBUYXJnZXQgdG8gYXR0YWNoIHRoZSBsaXN0ZW5lciBvblxuICAgKiBAcGFyYW0gaGFuZGxlciAgICAgICAgICAgSGFuZGxlciBmb3Igd2hlbiB0aGUgbGlzdGVuZXIgZmlyZXNcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICAgIFZvaWRcbiAgICovXG4gIHByaXZhdGUgYWRkSW50ZXJydXB0aXZlRXZlbnRMaXN0ZW5lcnMoXG4gICAgbGlzdGVuZXJUYXJnZXQ6IFNjcm9sbFRvTGlzdGVuZXJUYXJnZXQsXG4gICAgaGFuZGxlcjogRXZlbnRMaXN0ZW5lck9yRXZlbnRMaXN0ZW5lck9iamVjdCk6IHZvaWQge1xuXG4gICAgaWYgKCFsaXN0ZW5lclRhcmdldCkge1xuICAgICAgbGlzdGVuZXJUYXJnZXQgPSB3aW5kb3c7XG4gICAgfVxuXG4gICAgdGhpcy5pbnRlcnJ1cHRpdmVFdmVudHNcbiAgICAgIC5mb3JFYWNoKGV2ZW50ID0+IGxpc3RlbmVyVGFyZ2V0XG4gICAgICAgIC5hZGRFdmVudExpc3RlbmVyKGV2ZW50LCBoYW5kbGVyLCB0aGlzLnN1cHBvcnRQYXNzaXZlKCkgPyB7cGFzc2l2ZTogdHJ1ZX0gOiBmYWxzZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZlYXR1cmUtZGV0ZWN0IHN1cHBvcnQgZm9yIHBhc3NpdmUgZXZlbnQgbGlzdGVuZXJzLlxuICAgKlxuICAgKiBAcmV0dXJucyAgICAgICBXaGV0aGVyIG9yIG5vdCBwYXNzaXZlIGV2ZW50IGxpc3RlbmVycyBhcmUgc3VwcG9ydGVkXG4gICAqL1xuICBwcml2YXRlIHN1cHBvcnRQYXNzaXZlKCk6IGJvb2xlYW4ge1xuXG4gICAgbGV0IHN1cHBvcnRzUGFzc2l2ZSA9IGZhbHNlO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IG9wdHMgPSBPYmplY3QuZGVmaW5lUHJvcGVydHkoe30sICdwYXNzaXZlJywge1xuICAgICAgICBnZXQ6ICgpID0+IHtcbiAgICAgICAgICBzdXBwb3J0c1Bhc3NpdmUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd0ZXN0UGFzc2l2ZScsIG51bGwsIG9wdHMpO1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Rlc3RQYXNzaXZlJywgbnVsbCwgb3B0cyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgIH1cblxuICAgIHJldHVybiBzdXBwb3J0c1Bhc3NpdmU7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGxpc3RlbmVycyBmb3IgdGhlIEFuaW1hdGlvbiBJbnRlcnJ1cHQgRXZlbnQgZnJvbVxuICAgKiB0aGUgTGlzdGVuZXIgVGFyZ2V0LiBTcGVjaWZ5aW5nIHRoZSBjb3JyZWN0IGhhbmRsZXIgcHJldmVudHNcbiAgICogbWVtb3J5IGxlYWtzIGFuZCBtYWtlcyB0aGUgYWxsb2NhdGVkIG1lbW9yeSBhdmFpbGFibGUgZm9yXG4gICAqIEdhcmJhZ2UgQ29sbGVjdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIGV2ZW50cyAgICAgICAgICAgIExpc3Qgb2YgSW50ZXJydXB0aXZlIEV2ZW50cyB0byByZW1vdmVcbiAgICogQHBhcmFtIGxpc3RlbmVyVGFyZ2V0ICAgIFRhcmdldCB0byBhdHRhY2ggdGhlIGxpc3RlbmVyIG9uXG4gICAqIEBwYXJhbSBoYW5kbGVyICAgICAgICAgICBIYW5kbGVyIGZvciB3aGVuIHRoZSBsaXN0ZW5lciBmaXJlc1xuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgICAgVm9pZFxuICAgKi9cbiAgcHJpdmF0ZSByZW1vdmVJbnRlcnJ1cHRpdmVFdmVudExpc3RlbmVycyhcbiAgICBldmVudHM6IHN0cmluZ1tdLFxuICAgIGxpc3RlbmVyVGFyZ2V0OiBTY3JvbGxUb0xpc3RlbmVyVGFyZ2V0LFxuICAgIGhhbmRsZXI6IEV2ZW50TGlzdGVuZXJPckV2ZW50TGlzdGVuZXJPYmplY3QpOiB2b2lkIHtcblxuICAgIGlmICghbGlzdGVuZXJUYXJnZXQpIHtcbiAgICAgIGxpc3RlbmVyVGFyZ2V0ID0gd2luZG93O1xuICAgIH1cbiAgICBldmVudHMuZm9yRWFjaChldmVudCA9PiBsaXN0ZW5lclRhcmdldC5yZW1vdmVFdmVudExpc3RlbmVyKGV2ZW50LCBoYW5kbGVyKSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZCB0aGUgZmlyc3Qgc2Nyb2xsYWJsZSBwYXJlbnQgTm9kZSBvZiBhIGdpdmVuXG4gICAqIEVsZW1lbnQuIFRoZSBET00gVHJlZSBnZXRzIHNlYXJjaGVkIHVwd2FyZHNcbiAgICogdG8gZmluZCB0aGlzIGZpcnN0IHNjcm9sbGFibGUgcGFyZW50LiBQYXJlbnRzIG1pZ2h0XG4gICAqIGJlIGlnbm9yZWQgYnkgQ1NTIHN0eWxlcyBhcHBsaWVkIHRvIHRoZSBIVE1MIEVsZW1lbnQuXG4gICAqXG4gICAqIEBwYXJhbSBuYXRpdmVFbGVtZW50ICAgICBUaGUgRWxlbWVudCB0byBzZWFyY2ggdGhlIERPTSBUcmVlIHVwd2FyZHMgZnJvbVxuICAgKiBAcmV0dXJucyAgICAgICAgICAgICAgICAgVGhlIGZpcnN0IHNjcm9sbGFibGUgcGFyZW50IEhUTUwgRWxlbWVudFxuICAgKi9cbiAgcHJpdmF0ZSBnZXRGaXJzdFNjcm9sbGFibGVQYXJlbnQobmF0aXZlRWxlbWVudDogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudCB7XG5cbiAgICBsZXQgc3R5bGU6IENTU1N0eWxlRGVjbGFyYXRpb24gPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShuYXRpdmVFbGVtZW50KTtcblxuICAgIGNvbnN0IG92ZXJmbG93UmVnZXg6IFJlZ0V4cCA9IC8oYXV0b3xzY3JvbGx8b3ZlcmxheSkvO1xuXG4gICAgaWYgKHN0eWxlLnBvc2l0aW9uID09PSAnZml4ZWQnKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsZXQgcGFyZW50ID0gbmF0aXZlRWxlbWVudDtcbiAgICB3aGlsZSAocGFyZW50LnBhcmVudEVsZW1lbnQpIHtcbiAgICAgIHBhcmVudCA9IHBhcmVudC5wYXJlbnRFbGVtZW50O1xuICAgICAgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShwYXJlbnQpO1xuXG4gICAgICBpZiAoc3R5bGUucG9zaXRpb24gPT09ICdhYnNvbHV0ZSdcbiAgICAgICAgfHwgc3R5bGUub3ZlcmZsb3cgPT09ICdoaWRkZW4nXG4gICAgICAgIHx8IHN0eWxlLm92ZXJmbG93WSA9PT0gJ2hpZGRlbicpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChvdmVyZmxvd1JlZ2V4LnRlc3Qoc3R5bGUub3ZlcmZsb3cgKyBzdHlsZS5vdmVyZmxvd1kpXG4gICAgICAgIHx8IHBhcmVudC50YWdOYW1lID09PSAnQk9EWScpIHtcbiAgICAgICAgcmV0dXJuIHBhcmVudDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIFRhcmdldCBOb2RlIHRvIHNjcm9sbCB0by5cbiAgICpcbiAgICogQHBhcmFtIGlkICAgICAgICAgICAgICBUaGUgZ2l2ZW4gSUQgb2YgdGhlIG5vZGUsIGVpdGhlciBhIHN0cmluZyBvclxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgIGFuIGVsZW1lbnQgcmVmZXJlbmNlXG4gICAqIEBwYXJhbSBhbGxvd0JvZHlUYWcgICAgSW5kaWNhdGUgd2hldGhlciBvciBub3QgdGhlIERvY3VtZW50IEJvZHkgaXNcbiAgICogICAgICAgICAgICAgICAgICAgICAgICBjb25zaWRlcmVkIGEgdmFsaWQgVGFyZ2V0IE5vZGVcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICBUaGUgVGFyZ2V0IE5vZGUgdG8gc2Nyb2xsIHRvXG4gICAqL1xuICBwcml2YXRlIGdldE5vZGUoaWQ6IFNjcm9sbFRvVGFyZ2V0LCBhbGxvd0JvZHlUYWc6IGJvb2xlYW4gPSBmYWxzZSk6IEhUTUxFbGVtZW50IHtcblxuICAgIGxldCB0YXJnZXROb2RlOiBIVE1MRWxlbWVudDtcblxuICAgIGlmIChpc1N0cmluZyhpZCkpIHtcbiAgICAgIGlmIChhbGxvd0JvZHlUYWcgJiYgKGlkID09PSAnYm9keScgfHwgaWQgPT09ICdCT0RZJykpIHtcbiAgICAgICAgdGFyZ2V0Tm9kZSA9IHRoaXMuZG9jdW1lbnQuYm9keTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRhcmdldE5vZGUgPSB0aGlzLmRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHN0cmlwSGFzaChpZCkpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoaXNOdW1iZXIoaWQpKSB7XG4gICAgICB0YXJnZXROb2RlID0gdGhpcy5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChTdHJpbmcoaWQpKTtcbiAgICB9IGVsc2UgaWYgKGlzRWxlbWVudFJlZihpZCkpIHtcbiAgICAgIHRhcmdldE5vZGUgPSBpZC5uYXRpdmVFbGVtZW50O1xuICAgIH0gZWxzZSBpZiAoaXNOYXRpdmVFbGVtZW50KGlkKSkge1xuICAgICAgdGFyZ2V0Tm9kZSA9IGlkO1xuICAgIH1cblxuICAgIHJldHVybiB0YXJnZXROb2RlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlIHRoZSBMaXN0ZW5lciB0YXJnZXQuIFRoaXMgTGlzdGVuZXIgVGFyZ2V0IGlzIHVzZWRcbiAgICogdG8gYXR0YWNoIEV2ZW50IExpc3RlbmVycyBvbi4gSW4gY2FzZSBvZiB0aGUgdGFyZ2V0IGJlaW5nXG4gICAqIHRoZSBEb2N1bWVudCBCb2R5LCB3ZSBuZWVkIHRoZSBhY3R1YWwgYHdpbmRvd2AgdG8gbGlzdGVuXG4gICAqIGZvciBldmVudHMuXG4gICAqXG4gICAqIEBwYXJhbSBjb250YWluZXIgICAgICAgICAgIFRoZSBIVE1MIENvbnRhaW5lciBlbGVtZW50XG4gICAqIEByZXR1cm5zICAgICAgICAgICAgICAgICAgIFRoZSBMaXN0ZW5lciBUYXJnZXQgdG8gYXR0YWNoIGV2ZW50cyBvblxuICAgKi9cbiAgcHJpdmF0ZSBnZXRMaXN0ZW5lclRhcmdldChjb250YWluZXI6IEhUTUxFbGVtZW50KTogU2Nyb2xsVG9MaXN0ZW5lclRhcmdldCB7XG4gICAgaWYgKCFjb250YWluZXIpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5pc0RvY3VtZW50Qm9keShjb250YWluZXIpID8gd2luZG93IDogY29udGFpbmVyO1xuICB9XG5cbiAgLyoqXG4gICAqIFRlc3QgaWYgYSBnaXZlbiBIVE1MIEVsZW1lbnQgaXMgdGhlIERvY3VtZW50IEJvZHkuXG4gICAqXG4gICAqIEBwYXJhbSBlbGVtZW50ICAgICAgICAgICAgIFRoZSBnaXZlbiBIVE1MIEVsZW1lbnRcbiAgICogQHJldHVybnMgICAgICAgICAgICAgICAgICAgV2hldGhlciBvciBub3QgdGhlIEVsZW1lbnQgaXMgdGhlXG4gICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgIERvY3VtZW50IEJvZHkgRWxlbWVudFxuICAgKi9cbiAgcHJpdmF0ZSBpc0RvY3VtZW50Qm9keShlbGVtZW50OiBIVE1MRWxlbWVudCk6IGVsZW1lbnQgaXMgSFRNTEJvZHlFbGVtZW50IHtcbiAgICByZXR1cm4gZWxlbWVudC50YWdOYW1lLnRvVXBwZXJDYXNlKCkgPT09ICdCT0RZJztcbiAgfVxufVxuIl19