403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/landing/animation/copilot/src/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/landing/animation/copilot/src/copilot.js
import { Dom, Event, Tag, Type, Loc } from 'main.core';
import { PULL, PullClient } from 'pull.client';
import { PageObject } from 'landing.pageobject';
import { Skeleton } from 'landing.ui.copilot.skeleton';
import { Lottie } from 'ui.lottie';
import { EventEmitter } from 'main.core.events';
import loaderAnimation
	from '../../../../../components/bitrix/landing.site_copilot/templates/.default/lottie/landing-site-loader.json';
import { AiLoader } from './ai-loader/ai-loader';
import { load } from './change-background-with-animation';
import { Confetti } from 'ui.confetti';

import './css/style.css';

const ANIMATION_DURATION_DELAY = 300;
const ANIMATION_DURATION = 500;

type BlockImage = {
	defaultSrc?: string,
	src?: string,
	src2x?: string,
	id?: number,
	id2x?: number,
	type?: string,
	isSwitchedToDefault?: boolean,
	isSwitchedBackToOriginal?: boolean,
	nodeCode?: string,
	position?: number,
	node?: any,
};

type BlockData = {
	id: number,
	anchor: string,
	images?: BlockImage[],
	element?: HTMLElement,
};

export class Copilot extends EventEmitter
{
	generationId: ?number = null;
	blocksData: BlockData[];
	aiLoader: AiLoader;
	mainDocumentBody: HTMLElement;
	editorWindow: ?Window;
	completedBlockIds: ?Set;
	scrollTimeout: ?number;

	constructor()
	{
		super();
		this.setEventNamespace('BX.Landing.Animation.Copilot');

		this.editorWindow = PageObject.getEditorWindow();
		this.blocksData = [];
		this.aiLoader = new AiLoader({});
		this.mainDocumentBody = window.parent.document.body;
		this.subscribeToPullEvents();
	}

	setBlocksData(blocksData: BlockData[]): Copilot
	{
		this.blocksData = blocksData;

		return this;
	}

	setGenerationId(generationId: number): Copilot
	{
		this.generationId = generationId;

		return this;
	}

	getAiLoader(): AiLoader
	{
		return this.aiLoader;
	}

	init(): void
	{
		this.disableEditor();

		if (!this.editorWindow)
		{
			return;
		}

		this.completedBlockIds = new Set();

		const blocks = PageObject.getBlocks();
		this.blocksData = this.blocksData.map((blockData) => {
			const block = blocks.get(blockData.id);
			const blockDataNew = {
				...blockData,
				element: block.content,
				anchor: block.anchor,
			};

			Skeleton.initOnBlock(blockDataNew.anchor, blockDataNew.element);

			return blockDataNew;
		});

		this.replaceAllImagesToDefaultImage();

		this.emit('init', { blocksData: this.blocksData });

		this.getAiLoader().open();
	}

	subscribeToPullEvents()
	{
		PULL.subscribe({
			type: PullClient.SubscriptionType.Server,
			moduleId: 'landing',
			callback: (event) => {
				if (
					event.params.generationId !== undefined
					&& this.generationId !== null
					&& event.params.generationId !== this.generationId
				)
				{
					return;
				}

				const command = event.command;

				if (command === 'LandingCopilotGeneration:onCopilotImageCreate')
				{
					this.onImageCreate(event);
				}

				if (command === 'LandingCopilotGeneration:onChangeBlockFinish')
				{
					const blocksData = JSON.parse(event.params.params.blockData);
					this.animateBlock(blocksData);
				}
			},
		});
	}

	onImageCreate(event)
	{
		const blockId = parseInt(event.params.params.blockId, 10);
		const selector = event.params.params.selector;
		const position = event.params.params.position;
		const value = event.params.params.value;

		const neededBlockData = this.blocksData.find((blockData) => blockData.id === blockId);
		if (neededBlockData)
		{
			neededBlockData.images.forEach((image) => {
				if (
					image.nodeCode === selector
					&& image.position === position
				)
				{
					const { src = '', src2x = '', id = '', id2x = '' } = Type.isString(value)
						? { src: value, src2x: value, id: '', id2x: '' }
						: value;

					Object.assign(image, {
						src,
						src2x,
						id,
						id2x,
						isSwitchedToDefault: true,
					});
				}
			});
		}

		if (this.completedBlockIds.has(blockId))
		{
			const block = PageObject.getBlocks().get(blockId);
			if (block)
			{
				this.replaceAllDefaultImagesWithImageWithoutAnimation(block, neededBlockData);
			}
		}
	}

	async start(): Promise<void>
	{
		for (const blockData of this.blocksData)
		{
			// eslint-disable-next-line no-await-in-loop
			await this.scrollToBlock(blockData.element);

			// eslint-disable-next-line no-await-in-loop
			await this.replaceDefaultImagesOnce(blockData.id);

			// eslint-disable-next-line no-await-in-loop
			await Skeleton.removeSkeletonFromBlock(blockData.anchor);

			// eslint-disable-next-line no-await-in-loop
			await this.replaceDefaultImagesRecursive(blockData.id);
			this.completedBlockIds.add(blockData.id);

			// eslint-disable-next-line no-await-in-loop
			await this.delay(ANIMATION_DURATION_DELAY);
		}
	}

	async stop(): void
	{
		if (this.blocksData[0]?.element)
		{
			await this.scrollToBlock(this.blocksData[0].element);
		}
		this.getAiLoader().close();
	}

	scrollToBlock(block: HTMLElement): Promise<void>
	{
		return new Promise((resolve) => {
			const windowOfElement = block.ownerDocument.defaultView;
			if (!windowOfElement)
			{
				resolve();

				return;
			}

			let scrollTimeout = null;

			const handleScrollEnd = () => {
				if (scrollTimeout)
				{
					clearTimeout(scrollTimeout);
				}
				scrollTimeout = setTimeout(() => {
					Event.unbind(windowOfElement, 'scroll', handleScrollEnd);
					resolve();
				}, 300);
			};

			Event.bind(windowOfElement, 'scroll', handleScrollEnd);

			if (
				(block.getBoundingClientRect().top === 0 && windowOfElement.scrollY === 0)
				|| block.getBoundingClientRect().top < 5
			)
			{
				resolve();
			}

			windowOfElement.scroll({
				top: block.getBoundingClientRect().top + windowOfElement.scrollY,
				behavior: 'smooth',
			});
		});
	}

	replaceAllImagesToDefaultImage(): void
	{
		this.blocksData.forEach((blockData) => {
			blockData.images.forEach((image) => {
				if (image && image.src !== image.defaultSrc)
				{
					const block = PageObject.getBlocks().get(blockData.id);
					if (block)
					{
						this.setImageToNode(block, image, false);
					}

					Object.assign(image, {
						isSwitchedToDefault: true,
					});
				}
			});
		});
	}

	setImageToNode(block, image, useOriginalSrc = true)
	{
		const selector = `${image.nodeCode}@${image.position}`;
		block.forceInit();
		const node = block.nodes.getBySelector(selector);
		if (!node)
		{
			return;
		}

		if (!this.setImageToStyle(node, image, useOriginalSrc))
		{
			node.setValue(this.getImageValueObject(image, useOriginalSrc), true);
		}
	}

	setImageToStyle(node, image, useOriginalSrc = true): boolean
	{
		const styleObj = node.node.style;
		const src = useOriginalSrc ? image.src : image.defaultSrc;
		const src2x = useOriginalSrc ? image.src : image.defaultSrc;

		if (
			styleObj.getPropertyValue('--bg-url')
			&& src
		)
		{
			styleObj.setProperty('--bg-url', `url("${src}")`);
			if (
				styleObj.getPropertyValue('--bg-url-2x')
				&& src2x
			)
			{
				styleObj.setProperty('--bg-url-2x', `url("${src2x}")`);
			}

			return true;
		}

		return false;
	}

	getImageValueObject(imageNode, useOriginalSrc = true): {}
	{
		if (useOriginalSrc)
		{
			return {
				src: imageNode.src ?? '',
				src2x: imageNode.src2x ?? '',
			};
		}

		return {
			src: imageNode.defaultSrc ?? '',
			src2x: imageNode.defaultSrc ?? '',
		};
	}

	async processBlockImages(blockId: number): Promise<boolean>
	{
		if (this.completedBlockIds.has(blockId))
		{
			return true;
		}

		let allImagesReplaced = true;
		const neededBlockData = this.blocksData.find((blockData) => blockData.id === blockId);
		const block = PageObject.getBlocks().get(blockId);

		if (!block || !neededBlockData?.images)
		{
			return true;
		}

		block.forceInit();

		for (const image of neededBlockData.images)
		{
			if (!image || !image.src || !image.src2x)
			{
				continue;
			}

			const node = block.nodes.getBySelector(`${image.nodeCode}@${image.position}`);
			if (node && image.isSwitchedToDefault === true && !image.isSwitchedBackToOriginal)
			{
				// eslint-disable-next-line no-await-in-loop
				await this.replaceDefaultImageWithImage(image, node, block);
				image.isSwitchedBackToOriginal = true;
			}

			if (image.src === image.defaultSrc)
			{
				allImagesReplaced = false;
			}
		}

		return allImagesReplaced;
	}

	async replaceDefaultImagesOnce(blockId: number): Promise<void>
	{
		await this.processBlockImages(blockId);
	}

	// eslint-disable-next-line consistent-return
	async replaceDefaultImagesRecursive(
		blockId: number,
		currentAttempt = 0,
		maxAttempts = 5,
	): Promise<void>
	{
		const allReplaced = await this.processBlockImages(blockId);
		const canTryAgain = currentAttempt < maxAttempts;

		if (!allReplaced && canTryAgain)
		{
			await this.delay(ANIMATION_DURATION * 2);

			return this.replaceDefaultImagesRecursive(blockId, currentAttempt + 1, maxAttempts);
		}
	}

	replaceAllDefaultImagesWithImageWithoutAnimation(block, blockData: BlockData)
	{
		block.forceInit();
		if (blockData.images)
		{
			for (const image of blockData.images)
			{
				if (image)
				{
					const node = block.nodes.getBySelector(`${image.nodeCode}@${image.position}`);
					if (node && image.isSwitchedToDefault === true && !image.isSwitchedBackToOriginal)
					{
						this.replaceDefaultImageWithImageWithoutAnimation(image, node, block);
						image.isSwitchedBackToOriginal = true;
					}
				}
			}
		}
	}

	async replaceDefaultImageWithImage(image: BlockImage, node: Object, block): Promise<void>
	{
		await load(image.src);
		await load(image.src2x);

		Dom.addClass(node.node, 'landing-animation-copilot-default-image');
		Dom.addClass(node.node, 'fade-out');
		// eslint-disable-next-line no-await-in-loop
		await this.delay(ANIMATION_DURATION);

		this.setImageToNode(block, image);

		Dom.removeClass(node.node, 'fade-out');
		Dom.addClass(node.node, 'fade-in');
		// eslint-disable-next-line no-await-in-loop
		await this.delay(ANIMATION_DURATION);

		Dom.removeClass(node.node, 'fade-in');
		Dom.removeClass(node.node, 'landing-animation-copilot-default-image');
	}

	replaceDefaultImageWithImageWithoutAnimation(image: BlockImage, node: Object, block): void
	{
		this.setImageToNode(block, image);
	}

	delay(ms: number): Promise<void>
	{
		return new Promise((resolve) => {
			setTimeout(resolve, ms);
		});
	}

	async showConfetti(): Promise<void>
	{
		return new Promise((resolve) => {
			setTimeout(async () => {
				Confetti.fire({
					particleCount: 100,
					spread: 70,
					origin: {y: 0.7},
					zIndex: 99999,
				});
				await this.delay(ANIMATION_DURATION * 3);
				resolve();
			}, 1000);
		});
	}

	showLoader()
	{
		if (!this.editorWindow)
		{
			console.error('editorDocument is not defined');

			return;
		}

		const editorDocumentBody = this.editorWindow.document.querySelector('.landing-editor');
		if (!editorDocumentBody)
		{
			console.error('Element with class .landing-editor not found');

			return;
		}
		this.loader = this.getStatusCreate();
		Dom.append(this.loader, editorDocumentBody);
	}

	hideLoader()
	{
		if (this.loader)
		{
			this.loader.remove();
			this.loader = null;
		}
	}

	disableEditor(): Copilot
	{
		Dom.addClass(this.mainDocumentBody, 'landing-animating');
		const editorDocumentBody = this.editorWindow.document.querySelector('.landing-editor');
		if (editorDocumentBody)
		{
			Dom.addClass(editorDocumentBody, 'landing-editor-disable');
		}

		return this;
	}

	enableEditor(): Copilot
	{
		Dom.removeClass(this.mainDocumentBody, 'landing-animating');
		const editorDocumentBody = this.editorWindow.document.querySelector('.landing-editor');
		if (editorDocumentBody)
		{
			Dom.removeClass(editorDocumentBody, 'landing-editor-disable');
		}

		return this;
	}

	animateSite(): Promise<void>
	{
		this.start()
			.then(() => {
				this.stop();

				return this.showConfetti();
			})
			.then(() => {
				this.emit('onSiteFinish', { blocksData: this.blocksData });
				this.enableEditor();
			})
			.catch(() => {});
	}

	animateBlock(blocksData)
	{
		this.setBlocksData(blocksData);
		const reloadPromises = [];
		const pageBlocks = PageObject.getBlocks();
		for (const blockData of this.blocksData)
		{
			const block = pageBlocks.get(blockData.id);
			if (block)
			{
				reloadPromises.push(block.reload());
			}
		}

		Promise.all(reloadPromises)
			.then(() => {
				setTimeout(() => {
					this.init();
					this.hideLoader();
					this.start()
						.then(() => {
							this.stop();

							return this.showConfetti();
						})
						.then(() => {
							this.emit('onBlockFinish', { blocksData: this.blocksData });
							this.enableEditor();
						})
						.catch(() => {});
				}, ANIMATION_DURATION * 6);
			})
			.catch((error) => {
				console.error(error);
			});
	}

	getStatusCreate(): HTMLElement
	{
		const nodeAnimate = Tag.render`<div class="landing-site-copilot-status-image"></div>`;
		const nodeText = Tag.render`
			<div class="landing-site-copilot-content-text">
				${Loc.getMessage('LANDING_BLOCK_COPILOT_CREATED_1')}
			</div>
		`;

		Lottie.loadAnimation({
			container: nodeAnimate,
			renderer: 'svg',
			loop: true,
			autoplay: true,
			animationData: loaderAnimation,
		});

		let i = 2;
		const iMax = 6;
		this.interval = setInterval(() => {
			if (i > iMax)
			{
				i = 1;
			}

			let message = Loc.getMessage(`LANDING_BLOCK_COPILOT_CREATED_${i}`);

			if (!message)
			{
				i = 1;
				message = Loc.getMessage(`LANDING_BLOCK_COPILOT_CREATED_${i}`);
			}

			nodeText.innerText = message;
			i++;
		}, 3000);

		return Tag.render`
			<div class="landing-site-copilot-status-wrapper">
				${nodeAnimate}
				${nodeText}
			</div>
		`;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit