UNPKG

igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

878 lines • 97.3 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import { DOCUMENT } from '@angular/common'; import { GlobalPositionStrategy } from './position/global-position-strategy'; import { NoOpScrollStrategy } from './scroll/NoOpScrollStrategy'; import { ApplicationRef, ComponentFactoryResolver, ElementRef, EventEmitter, Inject, Injectable, Injector, NgZone } from '@angular/core'; import { AnimationBuilder } from '@angular/animations'; import { fromEvent, Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { showMessage } from '../../core/deprecateDecorators'; import * as i0 from "@angular/core"; import * as i1 from "@angular/animations"; import * as i2 from "@angular/common"; /** @type {?} */ let warningShown = false; /** * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/overlay_main.html) * The overlay service allows users to show components on overlay div above all other elements in the page. */ export class IgxOverlayService { /** * @param {?} _factoryResolver * @param {?} _appRef * @param {?} _injector * @param {?} builder * @param {?} document * @param {?} _zone */ constructor(_factoryResolver, _appRef, _injector, builder, document, _zone) { this._factoryResolver = _factoryResolver; this._appRef = _appRef; this._injector = _injector; this.builder = builder; this.document = document; this._zone = _zone; this._componentId = 0; this._overlayInfos = []; this.destroy$ = new Subject(); this._defaultSettings = { positionStrategy: new GlobalPositionStrategy(), scrollStrategy: new NoOpScrollStrategy(), modal: true, closeOnOutsideClick: true }; /** * Emitted before the component is opened. * ```typescript * onOpening(event: OverlayCancelableEventArgs){ * const onOpening = event; * } * ``` */ this.onOpening = new EventEmitter(); /** * Emitted after the component is opened and all animations are finished. * ```typescript * onOpened(event: OverlayEventArgs){ * const onOpened = event; * } * ``` */ this.onOpened = new EventEmitter(); /** * Emitted before the component is closed. * ```typescript * onClosing(event: OverlayCancelableEventArgs){ * const onClosing = event; * } * ``` */ this.onClosing = new EventEmitter(); /** * Emitted after the component is closed and all animations are finished. * ```typescript * onClosed(event: OverlayEventArgs){ * const onClosed = event; * } * ``` */ this.onClosed = new EventEmitter(); /** * Emitted before animation is started * ```typescript * onAnimation(event: OverlayAnimationEventArgs){ * const onAnimation = event; * } * ``` */ this.onAnimation = new EventEmitter(); this.documentClicked = (ev) => { // if we get to modal overlay just return - we should not close anything under it // if we get to non-modal overlay do the next: // 1. Check it has close on outside click. If not go on to next overlay; // 2. If true check if click is on the element. If it is on the element we have closed // already all previous non-modal with close on outside click elements, so we return. If // not close the overlay and check next for (let i = this._overlayInfos.length; i--;) { /** @type {?} */ const info = this._overlayInfos[i]; if (info.settings.modal) { return; } if (info.settings.closeOnOutsideClick) { // if the click is on the element do not close this overlay if (!info.elementRef.nativeElement.contains(ev.target)) { // if we should exclude position target check if the click is over it. If so do not close overlay /** @type {?} */ const positionTarget = (/** @type {?} */ (info.settings.positionStrategy.settings.target)); /** @type {?} */ let clickOnPositionTarget = false; if (positionTarget) { /** @type {?} */ const positionTargetRect = positionTarget.getBoundingClientRect(); clickOnPositionTarget = ev.clientX >= positionTargetRect.left && ev.clientX <= positionTargetRect.right && ev.clientY >= positionTargetRect.top && ev.clientY <= positionTargetRect.bottom; } if (!(info.settings.excludePositionTarget && clickOnPositionTarget)) { // if the click is outside click, but close animation has started do nothing if (!(info.closeAnimationPlayer && info.closeAnimationPlayer.hasStarted())) { this._hide(info.id, ev); } } } else { // TODO: should we return here, or continue with next overlays return; } } } }; /** * @hidden */ this.repositionAll = () => { for (let i = this._overlayInfos.length; i--;) { this.reposition(this._overlayInfos[i].id); } }; this._document = (/** @type {?} */ (this.document)); } /** * @param {?} component * @param {?=} settings * @param {?=} moduleRef * @return {?} */ attach(component, settings, moduleRef) { /** @type {?} */ let info; info = this.getOverlayInfo(component, moduleRef); // if there is no info most probably wrong type component was provided and we just go out if (!info) { return null; } info.id = (this._componentId++).toString(); settings = Object.assign({}, this._defaultSettings, settings); info.settings = settings; this._overlayInfos.push(info); return info.id; } /** * @param {?} compOrId * @param {?=} settings * @return {?} */ show(compOrId, settings) { /** @type {?} */ let info; /** @type {?} */ let id; if (typeof compOrId === 'string') { id = compOrId; info = this.getOverlayById(compOrId); if (!info) { console.warn('igxOverlay.show was called with wrong id: ' + compOrId); return null; } } else { warningShown = showMessage('`show(component, settings?)` overload is deprecated. Use `attach(component)` to obtain an Id.' + 'Then `show(id, settings?)` with provided Id.', warningShown); id = (this._componentId++).toString(); info = this.getOverlayInfo(compOrId); // if there is no info most probably wrong type component was provided and we just go out if (!info) { return; } info.id = id; } settings = Object.assign({}, this._defaultSettings, info.settings, settings); info.settings = settings; this._show(info); return id; } /** * Hides the component with the ID provided as a parameter. * ```typescript * this.overlay.hide(id); * ``` * @param {?} id * @return {?} */ hide(id) { this._hide(id); } /** * Hides all the components and the overlay. * ```typescript * this.overlay.hideAll(); * ``` * @return {?} */ hideAll() { // since overlays are removed on animation done, que all hides for (let i = this._overlayInfos.length; i--;) { this.hide(this._overlayInfos[i].id); } } /** * Repositions the component with ID provided as a parameter. * ```typescript * this.overlay.reposition(id); * ``` * @param {?} id * @return {?} */ reposition(id) { /** @type {?} */ const overlayInfo = this.getOverlayById(id); if (!overlayInfo || !overlayInfo.settings) { console.error('Wrong id provided in overlay.reposition method. Id: ' + id); return; } /** @type {?} */ const contentElement = overlayInfo.elementRef.nativeElement.parentElement; /** @type {?} */ const contentElementRect = contentElement.getBoundingClientRect(); overlayInfo.settings.positionStrategy.position(contentElement, { width: contentElementRect.width, height: contentElementRect.height }, this._document, false); } /** * @private * @param {?} info * @return {?} */ _show(info) { /** @type {?} */ const eventArgs = { id: info.id, componentRef: info.componentRef, cancel: false }; this.onOpening.emit(eventArgs); if (eventArgs.cancel) { if (info.componentRef) { this._appRef.detachView(info.componentRef.hostView); info.componentRef.destroy(); } return; } // if there is no close animation player, or there is one but it is not started yet we are in clear // opening. Otherwise, if there is close animation player playing animation now we should not setup // overlay this is already done if (!info.closeAnimationPlayer || (info.closeAnimationPlayer && !info.closeAnimationPlayer.hasStarted())) { /** @type {?} */ const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); info.initialSize = { width: elementRect.width, height: elementRect.height }; info.hook = this.placeElementHook(info.elementRef.nativeElement); this.moveElementToOverlay(info); if (info.componentRef) { info.componentRef.changeDetectorRef.detectChanges(); } this.updateSize(info); if (this._overlayInfos.indexOf(info) === -1) { this._overlayInfos.push(info); } info.settings.positionStrategy.position(info.elementRef.nativeElement.parentElement, { width: info.initialSize.width, height: info.initialSize.height }, document, true); info.settings.scrollStrategy.initialize(this._document, this, info.id); info.settings.scrollStrategy.attach(); } this.addOutsideClickListener(info); this.addResizeHandler(info.id); if (info.settings.modal) { this.setupModalWrapper(info); } if (info.settings.positionStrategy.settings.openAnimation) { this.playOpenAnimation(info); } else { // to eliminate flickering show the element just before onOpened fire info.elementRef.nativeElement.parentElement.style.visibility = ''; this.onOpened.emit({ id: info.id, componentRef: info.componentRef }); } } /** * @private * @param {?} id * @param {?=} event * @return {?} */ _hide(id, event) { /** @type {?} */ const info = this.getOverlayById(id); if (!info) { console.warn('igxOverlay.hide was called with wrong id: ' + id); return; } /** @type {?} */ const eventArgs = { id, componentRef: info.componentRef, cancel: false, event }; this.onClosing.emit(eventArgs); if (eventArgs.cancel) { return; } // TODO: synchronize where these are added/attached and where removed/detached info.settings.scrollStrategy.detach(); this.removeOutsideClickListener(info); this.removeResizeHandler(info.id); /** @type {?} */ const child = info.elementRef.nativeElement; if (info.settings.modal) { /** @type {?} */ const parent = (/** @type {?} */ (child.parentNode.parentNode)); this.applyAnimationParams(parent, info.settings.positionStrategy.settings.closeAnimation); parent.classList.remove('igx-overlay__wrapper--modal'); parent.classList.add('igx-overlay__wrapper'); } if (info.settings.positionStrategy.settings.closeAnimation) { this.playCloseAnimation(info); } else { this.onCloseDone(info); } } /** * @private * @param {?} component * @param {?=} moduleRef * @return {?} */ getOverlayInfo(component, moduleRef) { /** @type {?} */ const info = { ngZone: this._zone }; if (component instanceof ElementRef) { info.elementRef = (/** @type {?} */ (component)); } else { /** @type {?} */ let dynamicFactory; /** @type {?} */ const factoryResolver = moduleRef ? moduleRef.componentFactoryResolver : this._factoryResolver; try { dynamicFactory = factoryResolver.resolveComponentFactory(component); } catch (error) { console.error(error); return null; } /** @type {?} */ const injector = moduleRef ? moduleRef.injector : this._injector; /** @type {?} */ const dynamicComponent = dynamicFactory.create(injector); this._appRef.attachView(dynamicComponent.hostView); // If the element is newly created from a Component, it is wrapped in 'ng-component' tag - we do not want that. /** @type {?} */ const element = dynamicComponent.location.nativeElement; info.elementRef = (/** @type {?} */ ({ nativeElement: element })); info.componentRef = dynamicComponent; } return info; } /** * @private * @param {?} element * @return {?} */ placeElementHook(element) { if (!element.parentElement) { return null; } /** @type {?} */ const hook = this._document.createElement('div'); element.parentElement.insertBefore(hook, element); return hook; } /** * @private * @param {?} info * @return {?} */ moveElementToOverlay(info) { /** @type {?} */ const wrapperElement = this.getWrapperElement(); /** @type {?} */ const contentElement = this.getContentElement(wrapperElement, info.settings.modal); this.getOverlayElement(info).appendChild(wrapperElement); /** @type {?} */ const elementScrollTop = info.elementRef.nativeElement.scrollTop; contentElement.appendChild(info.elementRef.nativeElement); if (elementScrollTop) { info.elementRef.nativeElement.scrollTop = elementScrollTop; } } /** * @private * @return {?} */ getWrapperElement() { /** @type {?} */ const wrapper = this._document.createElement('div'); wrapper.classList.add('igx-overlay__wrapper'); return wrapper; } /** * @private * @param {?} wrapperElement * @param {?} modal * @return {?} */ getContentElement(wrapperElement, modal) { /** @type {?} */ const content = this._document.createElement('div'); if (modal) { content.classList.add('igx-overlay__content--modal'); content.addEventListener('click', (ev) => { ev.stopPropagation(); }); } else { content.classList.add('igx-overlay__content'); } content.addEventListener('scroll', (ev) => { ev.stopPropagation(); }); // hide element to eliminate flickering. Show the element exactly before animation starts content.style.visibility = 'hidden'; wrapperElement.appendChild(content); return content; } /** * @private * @param {?} info * @return {?} */ getOverlayElement(info) { if (info.settings.outlet) { return info.settings.outlet.nativeElement; } if (!this._overlayElement) { this._overlayElement = this._document.createElement('div'); this._overlayElement.classList.add('igx-overlay'); this._document.body.appendChild(this._overlayElement); } return this._overlayElement; } /** * @private * @param {?} info * @return {?} */ updateSize(info) { if (info.componentRef) { // if we are positioning component this is first time it gets visible // and we can finally get its size info.initialSize = info.elementRef.nativeElement.getBoundingClientRect(); } // set content div width only if element to show has width if (info.initialSize.width !== 0) { info.elementRef.nativeElement.parentElement.style.width = info.initialSize.width + 'px'; } } /** * @private * @param {?} info * @return {?} */ setupModalWrapper(info) { /** @type {?} */ const wrapperElement = info.elementRef.nativeElement.parentElement.parentElement; fromEvent(wrapperElement, 'keydown').pipe(filter((ev) => ev.key === 'Escape' || ev.key === 'Esc'), takeUntil(this.destroy$)).subscribe(() => this.hide(info.id)); wrapperElement.classList.remove('igx-overlay__wrapper'); this.applyAnimationParams(wrapperElement, info.settings.positionStrategy.settings.openAnimation); wrapperElement.classList.add('igx-overlay__wrapper--modal'); } /** * @private * @param {?} info * @return {?} */ onCloseDone(info) { this.cleanUp(info); this.onClosed.emit({ id: info.id, componentRef: info.componentRef }); } /** * @private * @param {?} info * @return {?} */ cleanUp(info) { /** @type {?} */ const child = info.elementRef.nativeElement; /** @type {?} */ const outlet = this.getOverlayElement(info); if (!outlet.contains(child)) { console.warn('Component with id:' + info.id + ' is already removed!'); return; } outlet.removeChild(child.parentNode.parentNode); if (info.componentRef) { this._appRef.detachView(info.componentRef.hostView); info.componentRef.destroy(); } if (info.hook) { info.hook.parentElement.insertBefore(info.elementRef.nativeElement, info.hook); info.hook.parentElement.removeChild(info.hook); } /** @type {?} */ const index = this._overlayInfos.indexOf(info); this._overlayInfos.splice(index, 1); // this._overlayElement.parentElement check just for tests that manually delete the element if (this._overlayInfos.length === 0 && this._overlayElement && this._overlayElement.parentElement) { this._overlayElement.parentElement.removeChild(this._overlayElement); this._overlayElement = null; } } /** * @private * @param {?} info * @return {?} */ playOpenAnimation(info) { if (!info.openAnimationPlayer) { /** @type {?} */ const animationBuilder = this.builder.build(info.settings.positionStrategy.settings.openAnimation); info.openAnimationPlayer = animationBuilder.create(info.elementRef.nativeElement); // AnimationPlayer.getPosition returns always 0. To workaround this we are getting inner WebAnimationPlayer // and then getting the positions from it. // This is logged in Angular here - https://github.com/angular/angular/issues/18891 // As soon as this is resolved we can remove this hack /** @type {?} */ const innerRenderer = ((/** @type {?} */ (info.openAnimationPlayer)))._renderer; info.openAnimationInnerPlayer = innerRenderer.engine.players[innerRenderer.engine.players.length - 1]; info.openAnimationPlayer.onDone(() => { this.onOpened.emit({ id: info.id, componentRef: info.componentRef }); if (info.openAnimationPlayer) { info.openAnimationPlayer.reset(); info.openAnimationPlayer = null; } if (info.closeAnimationPlayer && info.closeAnimationPlayer.hasStarted()) { info.closeAnimationPlayer.reset(); } }); } // if there is opening animation already started do nothing if (info.openAnimationPlayer.hasStarted()) { return; } // if there is closing animation already started start open animation from where close one has reached // and remove close animation if (info.closeAnimationPlayer && info.closeAnimationPlayer.hasStarted()) { // getPosition() returns what part of the animation is passed, e.g. 0.5 if half the animation // is done, 0.75 if 3/4 of the animation is done. As we need to start next animation from where // the previous has finished we need the amount up to 1, therefore we are subtracting what // getPosition() returns from one /** @type {?} */ const position = 1 - info.closeAnimationInnerPlayer.getPosition(); info.closeAnimationPlayer.reset(); info.closeAnimationPlayer = null; info.openAnimationPlayer.init(); info.openAnimationPlayer.setPosition(position); } this.onAnimation.emit({ id: info.id, animationPlayer: info.openAnimationPlayer, animationType: 'open' }); // to eliminate flickering show the element just before animation start info.elementRef.nativeElement.parentElement.style.visibility = ''; info.openAnimationPlayer.play(); } /** * @private * @param {?} info * @return {?} */ playCloseAnimation(info) { if (!info.closeAnimationPlayer) { /** @type {?} */ const animationBuilder = this.builder.build(info.settings.positionStrategy.settings.closeAnimation); info.closeAnimationPlayer = animationBuilder.create(info.elementRef.nativeElement); // AnimationPlayer.getPosition returns always 0. To workaround this we are getting inner WebAnimationPlayer // and then getting the positions from it. // This is logged in Angular here - https://github.com/angular/angular/issues/18891 // As soon as this is resolved we can remove this hack /** @type {?} */ const innerRenderer = ((/** @type {?} */ (info.closeAnimationPlayer)))._renderer; info.closeAnimationInnerPlayer = innerRenderer.engine.players[innerRenderer.engine.players.length - 1]; info.closeAnimationPlayer.onDone(() => { if (info.closeAnimationPlayer) { info.closeAnimationPlayer.reset(); info.closeAnimationPlayer = null; } if (info.openAnimationPlayer && info.openAnimationPlayer.hasStarted()) { info.openAnimationPlayer.reset(); } this.onCloseDone(info); }); } // if there is closing animation already started do nothing if (info.closeAnimationPlayer.hasStarted()) { return; } // if there is opening animation already started start close animation from where open one has reached // and remove open animation if (info.openAnimationPlayer && info.openAnimationPlayer.hasStarted()) { // getPosition() returns what part of the animation is passed, e.g. 0.5 if half the animation // is done, 0.75 if 3/4 of the animation is done. As we need to start next animation from where // the previous has finished we need the amount up to 1, therefore we are subtracting what // getPosition() returns from one /** @type {?} */ const position = 1 - info.openAnimationInnerPlayer.getPosition(); info.openAnimationPlayer.reset(); info.openAnimationPlayer = null; info.closeAnimationPlayer.init(); info.closeAnimationPlayer.setPosition(position); } this.onAnimation.emit({ id: info.id, animationPlayer: info.closeAnimationPlayer, animationType: 'close' }); info.closeAnimationPlayer.play(); } // TODO: check if applyAnimationParams will work with complex animations /** * @private * @param {?} wrapperElement * @param {?} animationOptions * @return {?} */ applyAnimationParams(wrapperElement, animationOptions) { if (!animationOptions) { wrapperElement.style.transitionDuration = '0ms'; return; } if (animationOptions.type === 10 /* AnimateRef */) { animationOptions = ((/** @type {?} */ (animationOptions))).animation; } if (!animationOptions.options || !animationOptions.options.params) { return; } /** @type {?} */ const params = (/** @type {?} */ (animationOptions.options.params)); if (params.duration) { wrapperElement.style.transitionDuration = params.duration; } if (params.easing) { wrapperElement.style.transitionTimingFunction = params.easing; } } /** * @hidden \@internal * @param {?} id * @return {?} */ getOverlayById(id) { if (!id) { return null; } /** @type {?} */ const info = this._overlayInfos.find(e => e.id === id); return info; } /** * @private * @param {?} info * @return {?} */ addOutsideClickListener(info) { if (info.settings.closeOnOutsideClick) { if (info.settings.modal) { fromEvent(info.elementRef.nativeElement.parentElement.parentElement, 'click') .pipe(takeUntil(this.destroy$)) .subscribe(() => this.hide(info.id)); } else if ( // if all overlays minus closing overlays equals one add the handler this._overlayInfos.filter(x => x.settings.closeOnOutsideClick && !x.settings.modal).length - this._overlayInfos.filter(x => x.settings.closeOnOutsideClick && !x.settings.modal && x.closeAnimationPlayer && x.closeAnimationPlayer.hasStarted()).length === 1) { this._document.addEventListener('click', this.documentClicked, true); } } } /** * @private * @param {?} info * @return {?} */ removeOutsideClickListener(info) { if (info.settings.modal === false) { /** @type {?} */ let shouldRemoveClickEventListener = true; this._overlayInfos.forEach(o => { if (o.settings.modal === false && o.id !== info.id) { shouldRemoveClickEventListener = false; } }); if (shouldRemoveClickEventListener) { this._document.removeEventListener('click', this.documentClicked, true); } } } /** * @private * @param {?} id * @return {?} */ addResizeHandler(id) { /** @type {?} */ const closingOverlaysCount = this._overlayInfos .filter(o => o.closeAnimationPlayer && o.closeAnimationPlayer.hasStarted()) .length; if (this._overlayInfos.length - closingOverlaysCount === 1) { this._document.defaultView.addEventListener('resize', this.repositionAll); } } /** * @private * @param {?} id * @return {?} */ removeResizeHandler(id) { /** @type {?} */ const closingOverlaysCount = this._overlayInfos .filter(o => o.closeAnimationPlayer && o.closeAnimationPlayer.hasStarted()) .length; if (this._overlayInfos.length - closingOverlaysCount === 1) { this._document.defaultView.removeEventListener('resize', this.repositionAll); } } /** * @hidden * @return {?} */ ngOnDestroy() { this.destroy$.next(true); this.destroy$.complete(); } } IgxOverlayService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ IgxOverlayService.ctorParameters = () => [ { type: ComponentFactoryResolver }, { type: ApplicationRef }, { type: Injector }, { type: AnimationBuilder }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }, { type: NgZone } ]; /** @nocollapse */ IgxOverlayService.ngInjectableDef = i0.defineInjectable({ factory: function IgxOverlayService_Factory() { return new IgxOverlayService(i0.inject(i0.ComponentFactoryResolver), i0.inject(i0.ApplicationRef), i0.inject(i0.INJECTOR), i0.inject(i1.AnimationBuilder), i0.inject(i2.DOCUMENT), i0.inject(i0.NgZone)); }, token: IgxOverlayService, providedIn: "root" }); if (false) { /** * @type {?} * @private */ IgxOverlayService.prototype._componentId; /** * @type {?} * @private */ IgxOverlayService.prototype._overlayInfos; /** * @type {?} * @private */ IgxOverlayService.prototype._overlayElement; /** * @type {?} * @private */ IgxOverlayService.prototype._document; /** * @type {?} * @private */ IgxOverlayService.prototype.destroy$; /** * @type {?} * @private */ IgxOverlayService.prototype._defaultSettings; /** * Emitted before the component is opened. * ```typescript * onOpening(event: OverlayCancelableEventArgs){ * const onOpening = event; * } * ``` * @type {?} */ IgxOverlayService.prototype.onOpening; /** * Emitted after the component is opened and all animations are finished. * ```typescript * onOpened(event: OverlayEventArgs){ * const onOpened = event; * } * ``` * @type {?} */ IgxOverlayService.prototype.onOpened; /** * Emitted before the component is closed. * ```typescript * onClosing(event: OverlayCancelableEventArgs){ * const onClosing = event; * } * ``` * @type {?} */ IgxOverlayService.prototype.onClosing; /** * Emitted after the component is closed and all animations are finished. * ```typescript * onClosed(event: OverlayEventArgs){ * const onClosed = event; * } * ``` * @type {?} */ IgxOverlayService.prototype.onClosed; /** * Emitted before animation is started * ```typescript * onAnimation(event: OverlayAnimationEventArgs){ * const onAnimation = event; * } * ``` * @type {?} */ IgxOverlayService.prototype.onAnimation; /** * @type {?} * @private */ IgxOverlayService.prototype.documentClicked; /** * @hidden * @type {?} */ IgxOverlayService.prototype.repositionAll; /** * @type {?} * @private */ IgxOverlayService.prototype._factoryResolver; /** * @type {?} * @private */ IgxOverlayService.prototype._appRef; /** * @type {?} * @private */ IgxOverlayService.prototype._injector; /** * @type {?} * @private */ IgxOverlayService.prototype.builder; /** * @type {?} * @private */ IgxOverlayService.prototype.document; /** * @type {?} * @private */ IgxOverlayService.prototype._zone; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3ZlcmxheS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL2lnbml0ZXVpLWFuZ3VsYXIvIiwic291cmNlcyI6WyJsaWIvc2VydmljZXMvb3ZlcmxheS9vdmVybGF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDN0UsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFVakUsT0FBTyxFQUNILGNBQWMsRUFFZCx3QkFBd0IsRUFFeEIsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sVUFBVSxFQUNWLFFBQVEsRUFJUixNQUFNLEVBQ1QsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLGdCQUFnQixFQUFrRixNQUFNLHFCQUFxQixDQUFDO0FBQ3ZJLE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdDQUFnQyxDQUFDOzs7OztJQUV6RCxZQUFZLEdBQUcsS0FBSzs7Ozs7QUFPeEIsTUFBTSxPQUFPLGlCQUFpQjs7Ozs7Ozs7O0lBZ0UxQixZQUNZLGdCQUEwQyxFQUMxQyxPQUF1QixFQUN2QixTQUFtQixFQUNuQixPQUF5QixFQUNQLFFBQWEsRUFDL0IsS0FBYTtRQUxiLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBMEI7UUFDMUMsWUFBTyxHQUFQLE9BQU8sQ0FBZ0I7UUFDdkIsY0FBUyxHQUFULFNBQVMsQ0FBVTtRQUNuQixZQUFPLEdBQVAsT0FBTyxDQUFrQjtRQUNQLGFBQVEsR0FBUixRQUFRLENBQUs7UUFDL0IsVUFBSyxHQUFMLEtBQUssQ0FBUTtRQXJFakIsaUJBQVksR0FBRyxD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