@bespunky/angular-zen
Version:
The Angular tools you always wished were there.
162 lines • 26.1 kB
JavaScript
import { Observable, of } from 'rxjs';
import { Injectable } from '@angular/core';
import { HeadService } from '@bespunky/angular-zen/core';
import { UniversalService } from '@bespunky/angular-zen/universal';
import * as i0 from "@angular/core";
import * as i1 from "@bespunky/angular-zen/core";
import * as i2 from "@bespunky/angular-zen/universal";
/**
* Provides methods for lazy loading scripts and styles programatically.
* The service keeps track of loaded files to skip reloading unless specified otherwise in the options of `loadScript()` or `loadStyle()`.
*/
export class LazyLoaderService {
constructor(head, universal) {
this.head = head;
this.universal = universal;
/**
* Defines the default options when calling the `LazyLoaderService.loadScript()` method.
*/
this.defaultScriptOptions = {
async: true,
defer: true,
alreadyLoaded: (url) => this.isCached(url) || this.isScriptPresent(url),
force: false
};
/**
* Defines the default options when calling the `LazyLoaderService.loadStyle()` method.
*/
this.defaultStyleOptions = {
alreadyLoaded: (url) => this.isCached(url) || this.isStylePresent(url),
force: false
};
this.cache = {};
}
/**
* Checks whether the file from the specified url has already been cached.
* @param url The url for the file to check.
* @returns A value indicating whether the file from the specified url has already been cached.
*/
isCached(url) {
return !!this.cache[url];
}
/**
* Checks whether a script element is already present in `<head>` for the given url.
* This doesn't guarantee that the script has been loaded.
*
* @param {string} url The url of the loaded script.
* @returns {boolean} `true` if an element matching the url is present in `<head>`; otherwise `false.
*/
isScriptPresent(url) {
return this.head.contains('script', { src: url });
}
/**
* Checks whether a link element is already present in `<head>` for the given style url.
* This doesn't guarantee that the style has been loaded.
*
* @param {string} url The url of the loaded link.
* @returns {boolean} `true` if an element matching the url is present in `<head>`; otherwise `false.
*/
isStylePresent(url) {
return this.head.contains('link', { rel: 'stylesheet', href: url });
}
/**
* Loads a script programatically.
*
* @param url The full url of the script to load.
* @param options (Optional) Specifies custom options to override default behaviour.
* @returns An observable object which allows subscribers to know when the script has been loaded and access its associated `<script>` element.
* The observable will complete immediately in case the script was already previously loaded.
* If the script was already loaded outside of the service, the observable will stream `undefined`.
*/
loadScript(url, options = this.defaultScriptOptions) {
// Set default options if not specified by caller
options.async = options.async === undefined ? this.defaultScriptOptions.async : options.async;
options.defer = options.defer === undefined ? this.defaultScriptOptions.defer : options.defer;
options.alreadyLoaded = options.alreadyLoaded === undefined ? this.defaultScriptOptions.alreadyLoaded : options.alreadyLoaded;
options.force = options.force === undefined ? this.defaultScriptOptions.force : options.force;
return this.loadFile(url, 'script', options, this.createScriptElement.bind(this));
}
/**
* Loads a style programatically.
*
* @param url The full url of the style to load.
* @param options (Optional) Specifies custom options to override default behaviour.
* @returns An observable object which allows subscribers to know when the style has been loaded and access its associated `<link>` element.
* The observable will complete immediately in case the style was already previously loaded.
* If the style was already loaded outside of the service, the observable will stream `undefined`.
*/
loadStyle(url, options = this.defaultStyleOptions) {
// Set default options if not specified by caller
options.alreadyLoaded = options.alreadyLoaded === undefined ? this.defaultStyleOptions.alreadyLoaded : options.alreadyLoaded;
options.force = options.force === undefined ? this.defaultStyleOptions.force : options.force;
return this.loadFile(url, 'style', options, this.createLinkElement.bind(this));
}
loadFile(url, type, options, createElement) {
if (!this.universal.isPlatformBrowser)
return of(null);
// If the script should be loaded, load it
if (!options.alreadyLoaded(url) || options.force) {
// Initialize a base object to store the data later
const lazyFile = { url, type, completed: false, element: null };
// Create an observable that waits until the script has been loaded and executed
const observable = new Observable(observer => {
// Create the callback that will mark the script as completed and notify the subscriber
const onLoad = () => {
lazyFile.completed = true;
observer.next(lazyFile);
observer.complete();
};
// Create the actual file tag, start downloading and store the element reference
lazyFile.element = createElement(url, onLoad, observer.error.bind(observer), options);
});
// Cache the file and the observable for later use
this.cache[url] = { lazyFile: lazyFile, observable };
return observable;
}
// If the file was already loaded and it shouldn't be downloaded again, complete immediately with the previous link data.
// If the file was already loaded outside of the service, the observable will stream `undefined` as there is nothing cached.
return of(this.cache[url]?.lazyFile);
}
/**
* Creates a `<script>` tag for the given url and adds it to the `<head>` tag to start downloading the script.
*
* @param url The url for the script to download.
* @param onLoad The callback to execute when the script has been downloaded and executed.
* @param onError The callback to execute when script download or execution has failed.
* @param options The options to add to the script.
* @returns A reference to the `<script>` element that was added to the DOM.
*/
createScriptElement(url, onLoad, onError, options) {
return this.head.addScriptElement('text/javascript', url, {
async: options.async,
defer: options.defer,
onload: onLoad,
onerror: onError
});
}
/**
* Creates a `<link>` tag for the given url and adds it to the `<head>` tag to start downloading the link.
*
* @param url The url for the link to download.
* @param onLoad The callback to execute when the script has been downloaded and executed.
* @param onError The callback to execute when script download or execution has failed.
* @returns A reference to the `<link>` element that was added to the DOM.
*/
createLinkElement(url, onLoad, onError) {
return this.head.addLinkElement('stylesheet', {
type: 'text/css',
href: url,
onload: onLoad,
onerror: onError
});
}
}
LazyLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LazyLoaderService, deps: [{ token: i1.HeadService }, { token: i2.UniversalService }], target: i0.ɵɵFactoryTarget.Injectable });
LazyLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LazyLoaderService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LazyLoaderService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: function () { return [{ type: i1.HeadService }, { type: i2.UniversalService }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF6eS1sb2FkZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvYW5ndWxhci16ZW4vYXN5bmMvc3JjL2xhenktbG9hZGVyL2xhenktbG9hZGVyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEMsT0FBTyxFQUFFLFVBQVUsRUFBYyxNQUFNLGVBQWUsQ0FBQztBQUV2RCxPQUFPLEVBQUUsV0FBVyxFQUFRLE1BQU0sNEJBQTRCLENBQUM7QUFDL0QsT0FBTyxFQUFFLGdCQUFnQixFQUFHLE1BQU0saUNBQWlDLENBQUM7Ozs7QUFPcEU7OztHQUdHO0FBSUgsTUFBTSxPQUFPLGlCQUFpQjtJQXlCMUIsWUFBb0IsSUFBaUIsRUFBVSxTQUEyQjtRQUF0RCxTQUFJLEdBQUosSUFBSSxDQUFhO1FBQVUsY0FBUyxHQUFULFNBQVMsQ0FBa0I7UUF2QjFFOztXQUVHO1FBQ0sseUJBQW9CLEdBQXNCO1lBQzlDLEtBQUssRUFBRSxJQUFJO1lBQ1gsS0FBSyxFQUFFLElBQUk7WUFDWCxhQUFhLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUM7WUFDdkUsS0FBSyxFQUFFLEtBQUs7U0FDZixDQUFDO1FBRUQ7O1VBRUU7UUFDSyx3QkFBbUIsR0FBZ0I7WUFDdkMsYUFBYSxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDO1lBQ3RFLEtBQUssRUFBRSxLQUFLO1NBQ2YsQ0FBQztRQVNFLElBQUksQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDO0lBQ3BCLENBQUM7SUFF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TSxFQUFFLE1BQU07WUFDZCxPQUFPLEVBQUUsT0FBTztTQUNuQixDQUFDLENBQUM7SUFDWCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNLLGlCQUFpQixDQUFDLEdBQVcsRUFBRSxNQUFrQixFQUFFLE9BQTZCO1FBRXBGLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxFQUN4QztZQUNJLElBQUksRUFBRSxVQUFVO1lBQ2hCLElBQUksRUFBRSxHQUFHO1lBQ1QsTUFBTSxFQUFFLE1BQU07WUFDZCxPQUFPLEVBQUUsT0FBTztTQUNuQixDQUFDLENBQUM7SUFDWCxDQUFDOzsrR0EvS1EsaUJBQWlCO21IQUFqQixpQkFBaUIsY0FGZCxNQUFNOzRGQUVULGlCQUFpQjtrQkFIN0IsVUFBVTttQkFBQztvQkFDUixVQUFVLEVBQUUsTUFBTTtpQkFDckIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBPYnNlcnZhYmxlLCBvZiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgSW5qZWN0YWJsZSwgRWxlbWVudFJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBIZWFkU2VydmljZSAgICAgICB9IGZyb20gJ0BiZXNwdW5reS9hbmd1bGFyLXplbi9jb3JlJztcbmltcG9ydCB7IFVuaXZlcnNhbFNlcnZpY2UgIH0gZnJvbSAnQGJlc3B1bmt5L2FuZ3VsYXItemVuL3VuaXZlcnNhbCc7XG5pbXBvcnQgeyBTY3JpcHRMb2FkT3B0aW9ucyB9IGZyb20gJy4vc2NyaXB0LWxvYWQtb3B0aW9ucyc7XG5pbXBvcnQgeyBMb2FkT3B0aW9ucyAgICAgICB9IGZyb20gJy4vbG9hZC1vcHRpb25zJztcbmltcG9ydCB7IExhenlMb2FkZWRGaWxlICAgIH0gZnJvbSAnLi9sYXp5LWxvYWRlZC1maWxlJztcblxudHlwZSBFbGVtZW50Q3JlYXRvciA9ICh1cmw6IHN0cmluZywgb25Mb2FkOiAoKSA9PiB2b2lkLCBvbkVycm9yOiAoZXJyb3I6IGFueSkgPT4gdm9pZCwgb3B0aW9uczogTG9hZE9wdGlvbnMpID0+IEVsZW1lbnRSZWY7XG5cbi8qKlxuICogUHJvdmlkZXMgbWV0aG9kcyBmb3IgbGF6eSBsb2FkaW5nIHNjcmlwdHMgYW5kIHN0eWxlcyBwcm9ncmFtYXRpY2FsbHkuXG4gKiBUaGUgc2VydmljZSBrZWVwcyB0cmFjayBvZiBsb2FkZWQgZmlsZXMgdG8gc2tpcCByZWxvYWRpbmcgdW5sZXNzIHNwZWNpZmllZCBvdGhlcndpc2UgaW4gdGhlIG9wdGlvbnMgb2YgYGxvYWRTY3JpcHQoKWAgb3IgYGxvYWRTdHlsZSgpYC5cbiAqL1xuQEluamVjdGFibGUoe1xuICAgIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBMYXp5TG9hZGVyU2VydmljZVxue1xuICAgIC8qKlxuICAgICAqIERlZmluZXMgdGhlIGRlZmF1bHQgb3B0aW9ucyB3aGVuIGNhbGxpbmcgdGhlIGBMYXp5TG9hZGVyU2VydmljZS5sb2FkU2NyaXB0KClgIG1ldGhvZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIGRlZmF1bHRTY3JpcHRPcHRpb25zOiBTY3JpcHRMb2FkT3B0aW9ucyA9IHtcbiAgICAgICAgYXN5bmM6IHRydWUsXG4gICAgICAgIGRlZmVyOiB0cnVlLFxuICAgICAgICBhbHJlYWR5TG9hZGVkOiAodXJsKSA9PiB0aGlzLmlzQ2FjaGVkKHVybCkgfHwgdGhpcy5pc1NjcmlwdFByZXNlbnQodXJsKSxcbiAgICAgICAgZm9yY2U6IGZhbHNlXG4gICAgfTtcblxuICAgICAvKipcbiAgICAgKiBEZWZpbmVzIHRoZSBkZWZhdWx0IG9wdGlvbnMgd2hlbiBjYWxsaW5nIHRoZSBgTGF6eUxvYWRlclNlcnZpY2UubG9hZFN0eWxlKClgIG1ldGhvZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIGRlZmF1bHRTdHlsZU9wdGlvbnM6IExvYWRPcHRpb25zID0ge1xuICAgICAgICBhbHJlYWR5TG9hZGVkOiAodXJsKSA9PiB0aGlzLmlzQ2FjaGVkKHVybCkgfHwgdGhpcy5pc1N0eWxlUHJlc2VudCh1cmwpLFxuICAgICAgICBmb3JjZTogZmFsc2VcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogS2VlcHMgdHJhY2sgb2YgbG9hZGVkIGxhenkgZmlsZXMgc3RhdHVzIGFuZCByZWZlcmVuY2VzXG4gICAgICovXG4gICAgcHJpdmF0ZSBjYWNoZTogeyBbdXJsOiBzdHJpbmddOiB7IGxhenlGaWxlOiBMYXp5TG9hZGVkRmlsZSwgb2JzZXJ2YWJsZTogT2JzZXJ2YWJsZTxMYXp5TG9hZGVkRmlsZT4gfSB9O1xuXG4gICAgY29uc3RydWN0b3IocHJpdmF0ZSBoZWFkOiBIZWFkU2VydmljZSwgcHJpdmF0ZSB1bml2ZXJzYWw6IFVuaXZlcnNhbFNlcnZpY2UpXG4gICAge1xuICAgICAgICB0aGlzLmNhY2hlID0ge307XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIHdoZXRoZXIgdGhlIGZpbGUgZnJvbSB0aGUgc3BlY2lmaWVkIHVybCBoYXMgYWxyZWFkeSBiZWVuIGNhY2hlZC5cbiAgICAgKiBAcGFyYW0gdXJsIFRoZSB1cmwgZm9yIHRoZSBmaWxlIHRvIGNoZWNrLlxuICAgICAqIEByZXR1cm5zIEEgdmFsdWUgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBmaWxlIGZyb20gdGhlIHNwZWNpZmllZCB1cmwgaGFzIGFscmVhZHkgYmVlbiBjYWNoZWQuXG4gICAgICovXG4gICAgcHVibGljIGlzQ2FjaGVkKHVybDogc3RyaW5nKTogYm9vbGVhblxuICAgIHtcbiAgICAgICAgcmV0dXJuICEhdGhpcy5jYWNoZVt1cmxdO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyB3aGV0aGVyIGEgc2NyaXB0IGVsZW1lbnQgaXMgYWxyZWFkeSBwcmVzZW50IGluIGA8aGVhZD5gIGZvciB0aGUgZ2l2ZW4gdXJsLlxuICAgICAqIFRoaXMgZG9lc24ndCBndWFyYW50ZWUgdGhhdCB0aGUgc2NyaXB0IGhhcyBiZWVuIGxvYWRlZC5cbiAgICAgKiBcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsIFRoZSB1cmwgb2YgdGhlIGxvYWRlZCBzY3JpcHQuXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiBhbiBlbGVtZW50IG1hdGNoaW5nIHRoZSB1cmwgaXMgcHJlc2VudCBpbiBgPGhlYWQ+YDsgb3RoZXJ3aXNlIGBmYWxzZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgaXNTY3JpcHRQcmVzZW50KHVybDogc3RyaW5nKTogYm9vbGVhblxuICAgIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaGVhZC5jb250YWlucygnc2NyaXB0JywgeyBzcmM6IHVybCB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3Mgd2hldGhlciBhIGxpbmsgZWxlbWVudCBpcyBhbHJlYWR5IHByZXNlbnQgaW4gYDxoZWFkPmAgZm9yIHRoZSBnaXZlbiBzdHlsZSB1cmwuXG4gICAgICogVGhpcyBkb2Vzbid0IGd1YXJhbnRlZSB0aGF0IHRoZSBzdHlsZSBoYXMgYmVlbiBsb2FkZWQuXG4gICAgICogXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHVybCBUaGUgdXJsIG9mIHRoZSBsb2FkZWQgbGluay5cbiAgICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gYHRydWVgIGlmIGFuIGVsZW1lbnQgbWF0Y2hpbmcgdGhlIHVybCBpcyBwcmVzZW50IGluIGA8aGVhZD5gOyBvdGhlcndpc2UgYGZhbHNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBpc1N0eWxlUHJlc2VudCh1cmw6IHN0cmluZyk6IGJvb2xlYW5cbiAgICB7XG4gICAgICAgIHJldHVybiB0aGlzLmhlYWQuY29udGFpbnMoJ2xpbmsnLCB7IHJlbDogJ3N0eWxlc2hlZXQnLCBocmVmOiB1cmwgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgYSBzY3JpcHQgcHJvZ3JhbWF0aWNhbGx5LlxuICAgICAqXG4gICAgICogQHBhcmFtIHVybCBUaGUgZnVsbCB1cmwgb2YgdGhlIHNjcmlwdCB0byBsb2FkLlxuICAgICAqIEBwYXJhbSBvcHRpb25zIChPcHRpb25hbCkgU3BlY2lmaWVzIGN1c3RvbSBvcHRpb25zIHRvIG92ZXJyaWRlIGRlZmF1bHQgYmVoYXZpb3VyLlxuICAgICAqIEByZXR1cm5zIEFuIG9ic2VydmFibGUgb2JqZWN0IHdoaWNoIGFsbG93cyBzdWJzY3JpYmVycyB0byBrbm93IHdoZW4gdGhlIHNjcmlwdCBoYXMgYmVlbiBsb2FkZWQgYW5kIGFjY2VzcyBpdHMgYXNzb2NpYXRlZCBgPHNjcmlwdD5gIGVsZW1lbnQuXG4gICAgICogICAgICAgICAgVGhlIG9ic2VydmFibGUgd2lsbCBjb21wbGV0ZSBpbW1lZGlhdGVseSBpbiBjYXNlIHRoZSBzY3JpcHQgd2FzIGFscmVhZHkgcHJldmlvdXNseSBsb2FkZWQuXG4gICAgICogICAgICAgICAgSWYgdGhlIHNjcmlwdCB3YXMgYWxyZWFkeSBsb2FkZWQgb3V0c2lkZSBvZiB0aGUgc2VydmljZSwgdGhlIG9ic2VydmFibGUgd2lsbCBzdHJlYW0gYHVuZGVmaW5lZGAuXG4gICAgICovXG4gICAgcHVibGljIGxvYWRTY3JpcHQodXJsOiBzdHJpbmcsIG9wdGlvbnM6IFNjcmlwdExvYWRPcHRpb25zID0gdGhpcy5kZWZhdWx0U2NyaXB0T3B0aW9ucyk6IE9ic2VydmFibGU8TGF6eUxvYWRlZEZpbGUgfCBudWxsPlxuICAgIHtcbiAgICAgICAgLy8gU2V0IGRlZmF1bHQgb3B0aW9ucyBpZiBub3Qgc3BlY2lmaWVkIGJ5IGNhbGxlclxuICAgICAgICBvcHRpb25zLmFzeW5jICAgICAgICAgPSBvcHRpb25zLmFzeW5jICAgICAgICAgPT09IHVuZGVmaW5lZCA/IHRoaXMuZGVmYXVsdFNjcmlwdE9wdGlvbnMuYXN5bmMgOiBvcHRpb25zLmFzeW5jO1xuICAgICAgICBvcHRpb25zLmRlZmVyICAgICAgICAgPSBvcHRpb25zLmRlZmVyICAgICAgICAgPT09IHVuZGVmaW5lZCA/IHRoaXMuZGVmYXVsdFNjcmlwdE9wdGlvbnMuZGVmZXIgOiBvcHRpb25zLmRlZmVyO1xuICAgICAgICBvcHRpb25zLmFscmVhZHlMb2FkZWQgPSBvcHRpb25zLmFscmVhZHlMb2FkZWQgPT09IHVuZGVmaW5lZCA/IHRoaXMuZGVmYXVsdFNjcmlwdE9wdGlvbnMuYWxyZWFkeUxvYWRlZCA6IG9wdGlvbnMuYWxyZWFkeUxvYWRlZDtcbiAgICAgICAgb3B0aW9ucy5mb3JjZSAgICAgICAgID0gb3B0aW9ucy5mb3JjZSAgICAgICAgID09PSB1bmRlZmluZWQgPyB0aGlzLmRlZmF1bHRTY3JpcHRPcHRpb25zLmZvcmNlIDogb3B0aW9ucy5mb3JjZTsgICAgICAgICAgXG5cbiAgICAgICAgcmV0dXJuIHRoaXMubG9hZEZpbGUodXJsLCAnc2NyaXB0Jywgb3B0aW9ucyBhcyBSZXF1aXJlZDxTY3JpcHRMb2FkT3B0aW9ucz4sIHRoaXMuY3JlYXRlU2NyaXB0RWxlbWVudC5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb2FkcyBhIHN0eWxlIHByb2dyYW1hdGljYWxseS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB1cmwgVGhlIGZ1bGwgdXJsIG9mIHRoZSBzdHlsZSB0byBsb2FkLlxuICAgICAqIEBwYXJhbSBvcHRpb25zIChPcHRpb25hbCkgU3BlY2lmaWVzIGN1c3RvbSBvcHRpb25zIHRvIG92ZXJyaWRlIGRlZmF1bHQgYmVoYXZpb3VyLlxuICAgICAqIEByZXR1cm5zIEFuIG9ic2VydmFibGUgb2JqZWN0IHdoaWNoIGFsbG93cyBzdWJzY3JpYmVycyB0byBrbm93IHdoZW4gdGhlIHN0eWxlIGhhcyBiZWVuIGxvYWRlZCBhbmQgYWNjZXNzIGl0cyBhc3NvY2lhdGVkIGA8bGluaz5gIGVsZW1lbnQuXG4gICAgICogICAgICAgICAgVGhlIG9ic2VydmFibGUgd2lsbCBjb21wbGV0ZSBpbW1lZGlhdGVseSBpbiBjYXNlIHRoZSBzdHlsZSB3YXMgYWxyZWFkeSBwcmV2aW91c2x5IGxvYWRlZC5cbiAgICAgKiAgICAgICAgICBJZiB0aGUgc3R5bGUgd2FzIGFscmVhZHkgbG9hZGVkIG91dHNpZGUgb2YgdGhlIHNlcnZpY2UsIHRoZSBvYnNlcnZhYmxlIHdpbGwgc3RyZWFtIGB1bmRlZmluZWRgLlxuICAgICAqL1xuICAgIHB1YmxpYyBsb2FkU3R5bGUodXJsOiBzdHJpbmcsIG9wdGlvbnM6IExvYWRPcHRpb25zID0gdGhpcy5kZWZhdWx0U3R5bGVPcHRpb25zKTogT2JzZXJ2YWJsZTxMYXp5TG9hZGVkRmlsZSB8IG51bGw+XG4gICAge1xuICAgICAgICAvLyBTZXQgZGVmYXVsdCBvcHRpb25zIGlmIG5vdCBzcGVjaWZpZWQgYnkgY2FsbGVyXG4gICAgICAgIG9wdGlvbnMuYWxyZWFkeUxvYWRlZCA9IG9wdGlvbnMuYWxyZWFkeUxvYWRlZCA9PT0gdW5kZWZpbmVkID8gdGhpcy5kZWZhdWx0U3R5bGVPcHRpb25zLmFscmVhZHlMb2FkZWQgOiBvcHRpb25zLmFscmVhZHlMb2FkZWQ7XG4gICAgICAgIG9wdGlvbnMuZm9yY2UgICAgICAgICA9IG9wdGlvbnMuZm9yY2UgICAgICAgICA9PT0gdW5kZWZpbmVkID8gdGhpcy5kZWZhdWx0U3R5bGVPcHRpb25zLmZvcmNlIDogb3B0aW9ucy5mb3JjZTtcblxuICAgICAgICByZXR1cm4gdGhpcy5sb2FkRmlsZSh1cmwsICdzdHlsZScsIG9wdGlvbnMgYXMgUmVxdWlyZWQ8TG9hZE9wdGlvbnM+LCB0aGlzLmNyZWF0ZUxpbmtFbGVtZW50LmJpbmQodGhpcykpO1xuICAgIH1cblxuICAgIHByaXZhdGUgbG9hZEZpbGUodXJsOiBzdHJpbmcsIHR5cGU6ICdzY3JpcHQnIHwgJ3N0eWxlJywgb3B0aW9uczogUmVxdWlyZWQ8TG9hZE9wdGlvbnM+LCBjcmVhdGVFbGVtZW50OiBFbGVtZW50Q3JlYXRvcik6IE9ic2VydmFibGU8TGF6eUxvYWRlZEZpbGUgfCBudWxsPlxuICAgIHtcbiAgICAgICAgaWYgKCF0aGlzLnVuaXZlcnNhbC5pc1BsYXRmb3JtQnJvd3NlcikgcmV0dXJuIG9mKG51bGwpO1xuXG4gICAgICAgIC8vIElmIHRoZSBzY3JpcHQgc2hvdWxkIGJlIGxvYWRlZCwgbG9hZCBpdFxuICAgICAgICBpZiAoIW9wdGlvbnMuYWxyZWFkeUxvYWRlZCh1cmwpIHx8IG9wdGlvbnMuZm9yY2UpXG4gICAgICAgIHtcbiAgICAgICAgICAgIC8vIEluaXRpYWxpemUgYSBiYXNlIG9iamVjdCB0byBzdG9yZSB0aGUgZGF0YSBsYXRlclxuICAgICAgICAgICAgY29uc3QgbGF6eUZpbGU6IExhenlMb2FkZWRGaWxlID0geyB1cmwsIHR5cGUsIGNvbXBsZXRlZDogZmFsc2UsIGVsZW1lbnQ6IG51bGwgfTtcbiAgICAgICAgICAgIC8vIENyZWF0ZSBhbiBvYnNlcnZhYmxlIHRoYXQgd2FpdHMgdW50aWwgdGhlIHNjcmlwdCBoYXMgYmVlbiBsb2FkZWQgYW5kIGV4ZWN1dGVkXG4gICAgICAgICAgICBjb25zdCBvYnNlcnZhYmxlID0gbmV3IE9ic2VydmFibGU8TGF6eUxvYWRlZEZpbGU+KG9ic2VydmVyID0+XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgLy8gQ3JlYXRlIHRoZSBjYWxsYmFjayB0aGF0IHdpbGwgbWFyayB0aGUgc2NyaXB0IGFzIGNvbXBsZXRlZCBhbmQgbm90aWZ5IHRoZSBzdWJzY3JpYmVyXG4gICAgICAgICAgICAgICAgY29uc3Qgb25Mb2FkID0gKCkgPT5cbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGxhenlGaWxlLmNvbXBsZXRlZCA9IHRydWU7XG5cbiAgICAgICAgICAgICAgICAgICAgb2JzZXJ2ZXIubmV4dChsYXp5RmlsZSk7XG4gICAgICAgICAgICAgICAgICAgIG9ic2VydmVyLmNvbXBsZXRlKCk7XG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIC8vIENyZWF0ZSB0aGUgYWN0dWFsIGZpbGUgdGFnLCBzdGFydCBkb3dubG9hZGluZyBhbmQgc3RvcmUgdGhlIGVsZW1lbnQgcmVmZXJlbmNlXG4gICAgICAgICAgICAgICAgbGF6eUZpbGUuZWxlbWVudCA9IGNyZWF0ZUVsZW1lbnQodXJsLCBvbkxvYWQsIG9ic2VydmVyLmVycm9yLmJpbmQob2JzZXJ2ZXIpLCBvcHRpb25zKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBDYWNoZSB0aGUgZmlsZSBhbmQgdGhlIG9ic2VydmFibGUgZm9yIGxhdGVyIHVzZVxuICAgICAgICAgICAgdGhpcy5jYWNoZVt1cmxdID0geyBsYXp5RmlsZTogbGF6eUZpbGUsIG9ic2VydmFibGUgfTtcblxuICAgICAgICAgICAgcmV0dXJuIG9ic2VydmFibGU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiB0aGUgZmlsZSB3YXMgYWxyZWFkeSBsb2FkZWQgYW5kIGl0IHNob3VsZG4ndCBiZSBkb3dubG9hZGVkIGFnYWluLCBjb21wbGV0ZSBpbW1lZGlhdGVseSB3aXRoIHRoZSBwcmV2aW91cyBsaW5rIGRhdGEuXG4gICAgICAgIC8vIElmIHRoZSBmaWxlIHdhcyBhbHJlYWR5IGxvYWRlZCBvdXRzaWRlIG9mIHRoZSBzZXJ2aWNlLCB0aGUgb2JzZXJ2YWJsZSB3aWxsIHN0cmVhbSBgdW5kZWZpbmVkYCBhcyB0aGVyZSBpcyBub3RoaW5nIGNhY2hlZC5cbiAgICAgICAgcmV0dXJuIG9mKHRoaXMuY2FjaGVbdXJsXT8ubGF6eUZpbGUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBgPHNjcmlwdD5gIHRhZyBmb3IgdGhlIGdpdmVuIHVybCBhbmQgYWRkcyBpdCB0byB0aGUgYDxoZWFkPmAgdGFnIHRvIHN0YXJ0IGRvd25sb2FkaW5nIHRoZSBzY3JpcHQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdXJsICAgICAgIFRoZSB1cmwgZm9yIHRoZSBzY3JpcHQgdG8gZG93bmxvYWQuXG4gICAgICogQHBhcmFtIG9uTG9hZCAgICBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSB3aGVuIHRoZSBzY3JpcHQgaGFzIGJlZW4gZG93bmxvYWRlZCBhbmQgZXhlY3V0ZWQuXG4gICAgICogQHBhcmFtIG9uRXJyb3IgICBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSB3aGVuIHNjcmlwdCBkb3dubG9hZCBvciBleGVjdXRpb24gaGFzIGZhaWxlZC5cbiAgICAgKiBAcGFyYW0gb3B0aW9ucyAgIFRoZSBvcHRpb25zIHRvIGFkZCB0byB0aGUgc2NyaXB0LlxuICAgICAqIEByZXR1cm5zIEEgcmVmZXJlbmNlIHRvIHRoZSBgPHNjcmlwdD5gIGVsZW1lbnQgdGhhdCB3YXMgYWRkZWQgdG8gdGhlIERPTS5cbiAgICAgKi9cbiAgICBwcml2YXRlIGNyZWF0ZVNjcmlwdEVsZW1lbnQodXJsOiBzdHJpbmcsIG9uTG9hZDogKCkgPT4gdm9pZCwgb25FcnJvcjogKGVycm9yOiBhbnkpID0+IHZvaWQsIG9wdGlvbnM6IFNjcmlwdExvYWRPcHRpb25zKTogRWxlbWVudFJlZjxIVE1MU2NyaXB0RWxlbWVudD5cbiAgICB7XG4gICAgICAgIHJldHVybiB0aGlzLmhlYWQuYWRkU2NyaXB0RWxlbWVudCgndGV4dC9qYXZhc2NyaXB0JywgdXJsLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGFzeW5jOiBvcHRpb25zLmFzeW5jLFxuICAgICAgICAgICAgICAgIGRlZmVyOiBvcHRpb25zLmRlZmVyLFxuICAgICAgICAgICAgICAgIG9ubG9hZDogb25Mb2FkLFxuICAgICAgICAgICAgICAgIG9uZXJyb3I6IG9uRXJyb3JcbiAgICAgICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBgPGxpbms+YCB0YWcgZm9yIHRoZSBnaXZlbiB1cmwgYW5kIGFkZHMgaXQgdG8gdGhlIGA8aGVhZD5gIHRhZyB0byBzdGFydCBkb3dubG9hZGluZyB0aGUgbGluay5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB1cmwgICAgICAgVGhlIHVybCBmb3IgdGhlIGxpbmsgdG8gZG93bmxvYWQuXG4gICAgICogQHBhcmFtIG9uTG9hZCAgICBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSB3aGVuIHRoZSBzY3JpcHQgaGFzIGJlZW4gZG93bmxvYWRlZCBhbmQgZXhlY3V0ZWQuXG4gICAgICogQHBhcmFtIG9uRXJyb3IgICBUaGUgY2FsbGJhY2sgdG8gZXhlY3V0ZSB3aGVuIHNjcmlwdCBkb3dubG9hZCBvciBleGVjdXRpb24gaGFzIGZhaWxlZC5cbiAgICAgKiBAcmV0dXJucyBBIHJlZmVyZW5jZSB0byB0aGUgYDxsaW5rPmAgZWxlbWVudCB0aGF0IHdhcyBhZGRlZCB0byB0aGUgRE9NLlxuICAgICAqL1xuICAgIHByaXZhdGUgY3JlYXRlTGlua0VsZW1lbnQodXJsOiBzdHJpbmcsIG9uTG9hZDogKCkgPT4gdm9pZCwgb25FcnJvcjogKGVycm9yOiBhbnkpID0+IHZvaWQpOiBFbGVtZW50UmVmPEhUTUxMaW5rRWxlbWVudD5cbiAgICB7XG4gICAgICAgIHJldHVybiB0aGlzLmhlYWQuYWRkTGlua0VsZW1lbnQoJ3N0eWxlc2hlZXQnLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHR5cGU6ICd0ZXh0L2NzcycsXG4gICAgICAgICAgICAgICAgaHJlZjogdXJsLFxuICAgICAgICAgICAgICAgIG9ubG9hZDogb25Mb2FkLFxuICAgICAgICAgICAgICAgIG9uZXJyb3I6IG9uRXJyb3JcbiAgICAgICAgICAgIH0pO1xuICAgIH1cbn1cbiJdfQ==