ngx-acuw
Version:
Angular components using WEBGL (threejs)
587 lines • 83.6 kB
JavaScript
import { Component, EventEmitter, HostListener, Input, Output, ViewChild, } from '@angular/core';
import { interval, NEVER, Observable, of, Subject, Subscription, zip, } from 'rxjs';
import { delay, switchMap, tap } from 'rxjs/operators';
import { DoubleSide, Mesh, PerspectiveCamera, PlaneBufferGeometry, Scene, ShaderMaterial, Vector4, WebGLRenderer, } from 'three';
import { TextureLoader } from 'three';
import { RxjsTween } from '../tween/rxjs-tween';
import { Direction } from './interfaces/direction';
import { ImageTransitionShaders } from './shaders/imageTransitionShaders';
import * as i0 from "@angular/core";
import * as i1 from "../performance-monitor/performance-monitor.component";
import * as i2 from "@angular/common";
export class ImageTransitionComponent {
constructor(ngZone) {
this.ngZone = ngZone;
this.imageUrls = new Array();
this.transitionDuration = 1000;
this.animationEnabled = true;
this.startIndex = 0;
this.showPerformanceMonitor = false;
this.imageIndexChange = new EventEmitter();
this.pAutoPlay = false;
this.pAutoPlayInterval = 5000;
this.pImageSize = 'cover';
this.pTransitionType = 'split';
this.pIntensity = 40.0;
this.pScaleX = 50.0;
this.pScaleY = 50.0;
this.pWidth = 0.5;
this.pDistortionImageUrl = '';
this.renderer = new WebGLRenderer({
antialias: true,
alpha: true,
});
this.scene = new Scene();
this.textures = new Array();
this.nextImageIndex = 0;
this.tranistionOngoing = false;
this.shaders = new ImageTransitionShaders();
this.autoPlay$ = new Observable();
this.autoPlaySubscription = new Subscription();
this.subscriptions = new Subscription();
this.basicAssetsLoaded$$ = new Subject();
this.forwardTextureLoaded$$ = new Subject();
this.backwardTextureLoaded$$ = new Subject();
this.showLoadingSpinner$$ = new Subject();
this.transitToForwardTexture$$ = new Subject();
this.transitToBackwardTexture$$ = new Subject();
// When true -> show spinner after a delay
// When false -> hide spinner immediatelly
this.showLoadingSpinner$ = this.showLoadingSpinner$$.pipe(switchMap((show) => {
if (show) {
return of(show).pipe(delay(300));
}
else {
return of(show);
}
}));
this.subscribeToInitScene();
this.subscribeTransistionToBackwardTexture();
this.subscribeTransistionToForwardTexture();
}
get displacementImageUrl() {
return this.pDistortionImageUrl;
}
set displacementImageUrl(displacementImageUrl) {
this.pDistortionImageUrl = displacementImageUrl;
if (this.material != null) {
this.setShaderProperties();
}
}
get imageSize() {
return this.pImageSize;
}
set imageSize(imageSize) {
this.pImageSize = imageSize;
if (this.mesh != null) {
this.resize();
}
}
get autoPlay() {
return this.pAutoPlay;
}
set autoPlay(autoplay) {
this.pAutoPlay = autoplay;
if (this.mesh != null) {
if (this.pAutoPlay === true) {
this.setAutoPlayInterval();
}
else {
this.stopAutoPlayInterval();
}
}
}
get autoPlayInterval() {
return this.pAutoPlayInterval;
}
set autoPlayInterval(autoPlayInterval) {
this.pAutoPlayInterval = autoPlayInterval;
if (this.mesh != null) {
if (this.pAutoPlay === true) {
this.setAutoPlayInterval();
}
}
}
get transitionType() {
return this.pTransitionType;
}
set transitionType(transitionType) {
this.pTransitionType = transitionType;
if (this.material != null) {
this.setShaderProperties();
}
}
get sizeX() {
return this.pScaleX;
}
set sizeX(sizeX) {
this.pScaleX = sizeX;
if (this.material != null) {
this.setShaderProperties();
}
}
get sizeY() {
return this.pScaleY;
}
set sizeY(sizeY) {
this.pScaleY = sizeY;
if (this.material != null) {
this.setShaderProperties();
}
}
get width() {
return this.pWidth;
}
set width(width) {
this.pWidth = width;
if (this.material != null) {
this.setShaderProperties();
}
}
get intensity() {
return this.intensity;
}
set intensity(intensity) {
this.pIntensity = intensity;
if (this.material != null) {
this.setShaderProperties();
}
}
ngAfterViewInit() {
if (this.imageUrls.length < 2) {
throw new Error('At least two images are required');
}
// Init camera
this.camera = new PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.001, 1000);
this.camera.position.set(0, 0, 2);
// Init renderer
this.renderer.setSize(this.threejsContainer.nativeElement.clientWidth, this.threejsContainer.nativeElement.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.threejsContainer.nativeElement.appendChild(this.renderer.domElement);
// Create scene
this.scene = new Scene();
// Load currenct, backwards and forwards textures
this.textures = new Array(this.imageUrls.length);
this.loadBasicAssets();
const backwardImgIdx = this.startIndex === 0 ? this.imageUrls.length - 1 : this.startIndex - 1;
this.loadTexture(backwardImgIdx, Direction.backward);
const forwardImgIdx = this.startIndex === this.imageUrls.length - 1 ? 0 : this.startIndex + 1;
this.loadTexture(forwardImgIdx, Direction.forward);
// Init autoPlay Observable
if (this.pAutoPlay === true) {
this.setAutoPlayInterval();
}
this.animate();
}
ngOnDestroy() {
// Cancel Animation
cancelAnimationFrame(this.animationFrameId);
// Stop autoplay animation
this.stopAutoPlayInterval();
// Remove threejs container from DOM
this.threejsContainer.nativeElement.removeChild(this.renderer.domElement);
// Dispose textures
this.textures.forEach((t) => {
t.dispose();
});
// Dispose material
if (this.material) {
this.material.dispose();
}
// Clear scene
if (this.scene) {
this.scene.clear();
}
// Dispose renderer
if (this.renderer) {
this.renderer.dispose();
}
this.subscriptions.unsubscribe();
}
subscribeToInitScene() {
this.subscriptions.add(this.basicAssetsLoaded$$
.pipe(tap(() => this.initBasiscScene()))
.subscribe());
}
subscribeTransistionToForwardTexture() {
this.subscriptions.add(zip(this.transitToForwardTexture$$, this.forwardTextureLoaded$$)
.pipe(tap(() => this.showLoadingSpinner$$.next(false)), tap(() => this.prepAndLoadNextImg(Direction.forward)))
.subscribe());
}
subscribeTransistionToBackwardTexture() {
this.subscriptions.add(zip(this.transitToBackwardTexture$$, this.backwardTextureLoaded$$)
.pipe(tap(() => this.showLoadingSpinner$$.next(false)), tap(() => this.prepAndLoadNextImg(Direction.backward)))
.subscribe());
}
loadBasicAssets() {
this.showLoadingSpinner$$.next(true);
const promises = new Array();
// Initial image to be shown
promises.push(new Promise((resolve) => {
this.textures[this.startIndex] = new TextureLoader().load(this.imageUrls[this.startIndex], resolve);
}));
if (this.displacementImageUrl !== '') {
promises.push(new Promise((resolve) => {
this.displacementTexture = new TextureLoader().load(this.displacementImageUrl, resolve);
}));
}
Promise.all(promises)
.then(() => this.basicAssetsLoaded$$.next())
.catch((err) => console.error(err))
.finally(() => this.showLoadingSpinner$$.next(false));
}
loadTexture(index, direction) {
new TextureLoader()
.loadAsync(this.imageUrls[index])
.then((texture) => {
this.textures[index] = texture;
switch (direction) {
case Direction.forward:
this.forwardTextureLoaded$$.next();
break;
case Direction.backward:
this.backwardTextureLoaded$$.next();
break;
}
})
.catch((err) => console.error(err));
}
initBasiscScene() {
// Create geometry
const geometry = new PlaneBufferGeometry(1, 1, 2, 2);
this.nextImageIndex = this.startIndex;
// Emit event to set current image index
this.imageIndexChange.emit(this.nextImageIndex);
this.material = new ShaderMaterial({
side: DoubleSide,
uniforms: {
time: { value: 0 },
progress: { value: 0 },
border: { value: 0 },
intensity: { value: 50.0 },
angle1: { value: Math.PI / 4 },
angle2: { value: -Math.PI + Math.PI / 4 },
scaleX: { value: 40.0 },
scaleY: { value: 40.0 },
transition: { value: 40.0 },
swipe: { value: 0 },
width: { value: 0.5 },
radius: { value: 0 },
texture1: { value: this.textures[this.startIndex] },
texture2: { value: null },
displacementTexture: { value: this.displacementTexture },
resolution1: { value: new Vector4() },
resolution2: { value: new Vector4() },
},
// wireframe: true,
vertexShader: this.shaders.vertex,
});
this.setShaderProperties();
this.mesh = new Mesh(geometry, this.material);
this.scene.add(this.mesh);
// Call the resize method once to set the texture resultion correclty
this.resize();
}
/**
* Sets the autoPlay interval
*/
setAutoPlayInterval() {
this.autoPlay$ = interval(this.pAutoPlayInterval);
this.autoPlaySubscription = this.autoPlay$.pipe(tap(() => this.transitToForwardTexture$$.next())).subscribe();
}
/**
* Resets the autoPlay interval
*/
resetAutoPlayInterval() {
this.autoPlay$ = NEVER;
this.autoPlaySubscription.unsubscribe();
this.setAutoPlayInterval();
}
/**
* Stops the autoPlay interval
*/
stopAutoPlayInterval() {
this.autoPlay$ = NEVER;
this.autoPlaySubscription.unsubscribe();
}
/**
* Sets the shader properties depending on the transition type
*/
setShaderProperties() {
switch (this.transitionType) {
case 'split':
this.material.uniforms.intensity.value = this.pIntensity;
this.material.fragmentShader = this.shaders.splitTransitionFrag;
break;
case 'fade':
this.material.fragmentShader = this.shaders.fadeFrag;
break;
case 'noise':
this.material.uniforms.scaleX.value = this.pScaleX;
this.material.uniforms.scaleY.value = this.pScaleY;
this.material.uniforms.width.value = this.pWidth;
this.material.fragmentShader = this.shaders.noiseFrag;
break;
case 'blur':
this.material.uniforms.intensity.value = this.pIntensity;
this.material.fragmentShader = this.shaders.blurFrag;
break;
case 'distortion':
this.material.uniforms.displacementTexture.value =
new TextureLoader().load(this.displacementImageUrl);
this.material.uniforms.intensity.value = this.pIntensity;
this.material.fragmentShader = this.shaders.distortionFrag;
break;
default:
break;
}
this.material.needsUpdate = true;
}
/**
* Animation
*/
animate() {
if (this.animationEnabled === true) {
this.renderer.render(this.scene, this.camera);
}
if (this.performanceMonitor && this.showPerformanceMonitor) {
this.performanceMonitor.end();
}
this.ngZone.runOutsideAngular(() => {
this.animationFrameId = window.requestAnimationFrame(() => this.animate());
});
}
/**
* Resizes the canvas and updates the texture resulution information of the images
*/
resize() {
// Get the with and height of the container
const containerWidth = this.threejsContainer.nativeElement.offsetWidth;
const containerHeight = this.threejsContainer.nativeElement.offsetHeight;
// return the function, if eihter with or heigth is undefined
if (!containerHeight || !containerHeight) {
return;
}
this.renderer.setSize(containerWidth, containerHeight);
this.camera.aspect = containerWidth / containerHeight;
this.updateTextureResolution(this.nextImageIndex, 1);
const dist = this.camera.position.z;
const height = 1;
this.camera.fov = 2 * (180 / Math.PI) * Math.atan(height / (2 * dist));
this.mesh.scale.x = this.camera.aspect;
this.mesh.scale.y = 1;
this.camera.updateProjectionMatrix();
}
/**
* Updates the resulution of the texture for the shader depending on the image size type
* @param textureNumber Number of the texture
*/
updateTextureResolution(textureNumber, targetGlslTexture) {
const texture = this.textures[textureNumber];
const containerWidth = this.threejsContainer.nativeElement.offsetWidth;
const containerHeight = this.threejsContainer.nativeElement.offsetHeight;
// Adapt the size of the image
const imageAspect = texture.image.height / texture.image.width;
const containerAspect = containerHeight / containerWidth;
let a1;
let a2;
if (this.pImageSize === 'cover') {
if (containerAspect > imageAspect) {
a1 = (containerWidth / containerHeight) * imageAspect;
a2 = 1;
}
else {
a1 = 1;
a2 = containerHeight / containerWidth / imageAspect;
}
}
else if (this.pImageSize === 'contain') {
if (containerAspect < imageAspect) {
a1 = (containerWidth / containerHeight) * imageAspect;
a2 = 1;
}
else {
a1 = 1;
a2 = containerHeight / containerWidth / imageAspect;
}
}
if (targetGlslTexture === 1) {
this.material.uniforms.resolution1.value.x = containerWidth;
this.material.uniforms.resolution1.value.y = containerHeight;
this.material.uniforms.resolution1.value.z = a1;
this.material.uniforms.resolution1.value.w = a2;
}
else if (targetGlslTexture === 2) {
this.material.uniforms.resolution2.value.x = containerWidth;
this.material.uniforms.resolution2.value.y = containerHeight;
this.material.uniforms.resolution2.value.z = a1;
this.material.uniforms.resolution2.value.w = a2;
}
}
/**
* Starts the transition effect to the next image
* @param posDirection indicator, if the next or previous image should be loaded
*/
transitionToNextTexture(direction) {
// EventEmitter
this.imageIndexChange.emit(this.nextImageIndex);
this.ngZone.runOutsideAngular(() => {
if (direction === Direction.backward) {
this.material.uniforms.texture2.value =
this.material.uniforms.texture1.value;
this.material.uniforms.resolution2.value.x =
this.material.uniforms.resolution1.value.x;
this.material.uniforms.resolution2.value.y =
this.material.uniforms.resolution1.value.y;
this.material.uniforms.resolution2.value.z =
this.material.uniforms.resolution1.value.z;
this.material.uniforms.resolution2.value.w =
this.material.uniforms.resolution1.value.w;
this.material.uniforms.progress.value = 1;
// Set the next image to texture1 and update the resolution
this.material.uniforms.texture1.value =
this.textures[this.nextImageIndex];
this.updateTextureResolution(this.nextImageIndex, 1);
// Start the tween for doing the transition
RxjsTween.createTween(RxjsTween.linear, 1, 0, this.transitionDuration).subscribe({
next: (val) => {
this.material.uniforms.progress.value = val;
},
complete: () => {
// Set the transition flag to false to indicate that the transition animation is finished
this.tranistionOngoing = false;
// Reset progress to 1, thus the texture from texture 2 needs to be set to texture 1
this.material.uniforms.texture2.value =
this.textures[this.nextImageIndex];
this.updateTextureResolution(this.nextImageIndex, 2);
this.material.uniforms.progress.value = 0;
},
});
}
else {
// Set the next image to texture2 and update the resolution
this.material.uniforms.texture2.value =
this.textures[this.nextImageIndex];
this.updateTextureResolution(this.nextImageIndex, 2);
// Start the tween for doing the transition
RxjsTween.createTween(RxjsTween.linear, 0, 1, this.transitionDuration).subscribe({
next: (val) => {
this.material.uniforms.progress.value = val;
},
complete: () => {
// Set the transition flag to false to indicate that the transition animation is finished
this.tranistionOngoing = false;
// Reset progress to 0, thus the texture from texture 2 needs to be set to texture 1
this.material.uniforms.texture1.value =
this.textures[this.nextImageIndex];
this.updateTextureResolution(this.nextImageIndex, 1);
this.material.uniforms.progress.value = 0;
},
});
}
});
}
prepAndLoadNextImg(direction) {
// Set the flag to indicate that the transition animation is ongoing
this.tranistionOngoing = true;
let nextButOne = 0;
if (direction === Direction.forward) {
// Set the next index
this.nextImageIndex =
this.nextImageIndex < this.imageUrls.length - 1
? this.nextImageIndex + 1
: 0;
// Check if another texture needs to be loaded
nextButOne =
this.nextImageIndex + 1 > this.imageUrls.length - 1
? 0
: this.nextImageIndex + 1;
this.loadTexture(nextButOne, Direction.forward);
}
else {
// Update the number of the current shown image
this.nextImageIndex =
this.nextImageIndex > 0
? this.nextImageIndex - 1
: this.imageUrls.length - 1;
// Check if another texture needs to be loaded
nextButOne =
this.nextImageIndex - 1 < 0
? this.imageUrls.length - 1
: this.nextImageIndex - 1;
this.loadTexture(nextButOne, Direction.backward);
}
this.transitionToNextTexture(direction);
}
nextImage(direction) {
if (this.tranistionOngoing) {
console.warn('cannot go to next image. transition still ongoing.');
return;
}
if (this.pAutoPlay) {
this.resetAutoPlayInterval();
}
this.showLoadingSpinner$$.next(true);
switch (direction) {
case Direction.backward:
this.transitToBackwardTexture$$.next();
break;
case Direction.forward:
this.transitToForwardTexture$$.next();
break;
}
}
//#region public methods
next() {
this.nextImage(Direction.forward);
}
prev() {
this.nextImage(Direction.backward);
}
}
ImageTransitionComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: ImageTransitionComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
ImageTransitionComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: ImageTransitionComponent, selector: "lib-image-transition", inputs: { imageUrls: "imageUrls", displacementImageUrl: "displacementImageUrl", imageSize: "imageSize", autoPlay: "autoPlay", autoPlayInterval: "autoPlayInterval", transitionDuration: "transitionDuration", transitionType: "transitionType", sizeX: "sizeX", sizeY: "sizeY", width: "width", intensity: "intensity", animationEnabled: "animationEnabled", startIndex: "startIndex", showPerformanceMonitor: "showPerformanceMonitor" }, outputs: { imageIndexChange: "imageIndexChange" }, host: { listeners: { "window:resize": "resize()" } }, viewQueries: [{ propertyName: "threejsContainer", first: true, predicate: ["threejsContainer"], descendants: true }, { propertyName: "performanceMonitor", first: true, predicate: ["performanceMonitor"], descendants: true }], ngImport: i0, template: "<div #threejsContainer class=\"threejs-container\">\r\n <div *ngIf=\"showLoadingSpinner$ | async\" class=\"loading-container\">\r\n <div class=\"lds-ripple\">\r\n <div></div>\r\n <div></div>\r\n </div>\r\n </div>\r\n</div>\r\n<acuw-performance-monitor\r\n *ngIf=\"showPerformanceMonitor\"\r\n #performanceMonitor\r\n></acuw-performance-monitor>\r\n", styles: [".threejs-container{position:relative;width:100%;height:100%}.loading-container{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center}.lds-ripple{display:inline-block;position:relative;width:80px;height:80px;background-color:#0006;border-radius:40px;box-shadow:0 0 24px 12px #00000080}.lds-ripple div{position:absolute;border:4px solid #fff;opacity:1;border-radius:50%;animation:lds-ripple 1s cubic-bezier(0,.2,.8,1) infinite}.lds-ripple div:nth-child(2){animation-delay:-.5s}@keyframes lds-ripple{0%{top:36px;left:36px;width:0;height:0;opacity:1}to{top:0px;left:0px;width:72px;height:72px;opacity:0}}\n"], components: [{ type: i1.PerformanceMonitorComponent, selector: "acuw-performance-monitor" }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "async": i2.AsyncPipe } });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: ImageTransitionComponent, decorators: [{
type: Component,
args: [{ selector: 'lib-image-transition', template: "<div #threejsContainer class=\"threejs-container\">\r\n <div *ngIf=\"showLoadingSpinner$ | async\" class=\"loading-container\">\r\n <div class=\"lds-ripple\">\r\n <div></div>\r\n <div></div>\r\n </div>\r\n </div>\r\n</div>\r\n<acuw-performance-monitor\r\n *ngIf=\"showPerformanceMonitor\"\r\n #performanceMonitor\r\n></acuw-performance-monitor>\r\n", styles: [".threejs-container{position:relative;width:100%;height:100%}.loading-container{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center}.lds-ripple{display:inline-block;position:relative;width:80px;height:80px;background-color:#0006;border-radius:40px;box-shadow:0 0 24px 12px #00000080}.lds-ripple div{position:absolute;border:4px solid #fff;opacity:1;border-radius:50%;animation:lds-ripple 1s cubic-bezier(0,.2,.8,1) infinite}.lds-ripple div:nth-child(2){animation-delay:-.5s}@keyframes lds-ripple{0%{top:36px;left:36px;width:0;height:0;opacity:1}to{top:0px;left:0px;width:72px;height:72px;opacity:0}}\n"] }]
}], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { imageUrls: [{
type: Input
}], displacementImageUrl: [{
type: Input
}], imageSize: [{
type: Input
}], autoPlay: [{
type: Input
}], autoPlayInterval: [{
type: Input
}], transitionDuration: [{
type: Input
}], transitionType: [{
type: Input
}], sizeX: [{
type: Input
}], sizeY: [{
type: Input
}], width: [{
type: Input
}], intensity: [{
type: Input
}], animationEnabled: [{
type: Input
}], startIndex: [{
type: Input
}], showPerformanceMonitor: [{
type: Input
}], imageIndexChange: [{
type: Output
}], threejsContainer: [{
type: ViewChild,
args: ['threejsContainer']
}], performanceMonitor: [{
type: ViewChild,
args: ['performanceMonitor']
}], resize: [{
type: HostListener,
args: ['window:resize']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtdHJhbnNpdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtYWN1dy9zcmMvbGliL2ltYWdlLXRyYW5zaXRpb24vaW1hZ2UtdHJhbnNpdGlvbi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtYWN1dy9zcmMvbGliL2ltYWdlLXRyYW5zaXRpb24vaW1hZ2UtdHJhbnNpdGlvbi5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsU0FBUyxFQUVULFlBQVksRUFDWixZQUFZLEVBQ1osS0FBSyxFQUdMLE1BQU0sRUFDTixTQUFTLEdBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNMLFFBQVEsRUFDUixLQUFLLEVBQ0wsVUFBVSxFQUNWLEVBQUUsRUFDRixPQUFPLEVBQ1AsWUFBWSxFQUNaLEdBQUcsR0FDSixNQUFNLE1BQU0sQ0FBQztBQUNkLE9BQU8sRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3ZELE9BQU8sRUFDTCxVQUFVLEVBQ1YsSUFBSSxFQUNKLGlCQUFpQixFQUNqQixtQkFBbUIsRUFDbkIsS0FBSyxFQUNMLGNBQWMsRUFFZCxPQUFPLEVBQ1AsYUFBYSxHQUNkLE1BQU0sT0FBTyxDQUFDO0FBQ2YsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLE9BQU8sQ0FBQztBQUV0QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDOzs7O0FBTzFFLE1BQU0sT0FBTyx3QkFBd0I7SUF3S25DLFlBQW9CLE1BQWM7UUFBZCxXQUFNLEdBQU4sTUFBTSxDQUFRO1FBdkt6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