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/main/sidepanel/src/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/main/sidepanel/src/toolbar.js
import {
	Type,
	Tag,
	Text,
	Cache,
	Loc,
	Dom,
	Runtime,
	ZIndexManager,
	Event,
	Reflection,
	ajax as Ajax,
	type JsonObject,
} from 'main.core';

import { EventEmitter } from 'main.core.events';
import { MenuManager, PopupManager, Popup, type Menu } from 'main.popup';

import { ToolbarItem } from './toolbar-item';
import { getInstance } from './get-instance';

import { type ToolbarItemOptions } from './types/toolbar-item-options';
import { type ToolbarOptions } from './types/toolbar-options';

export class Toolbar extends EventEmitter
{
	constructor(toolbarOptions: ToolbarOptions)
	{
		super();
		this.setEventNamespace('BX.Main.SidePanel.Toolbar');

		const options = Type.isPlainObject(toolbarOptions) ? toolbarOptions : {};
		if (!Type.isStringFilled(options.context))
		{
			throw new Error('BX.Main.SidePanel.Toolbar: "context" parameter is required.');
		}

		this.context = options.context;
		this.items = [];
		this.rendered = false;
		this.refs = new Cache.MemoryCache();
		this.container = null;
		this.lsKey = 'bx.sidepanel.toolbar.item';

		this.initialPosition = { right: '5px', bottom: '20px' };
		this.shiftedPosition = { right: '5px', bottom: '20px' };
		if (Type.isPlainObject(options.position))
		{
			this.initialPosition = options.position;
		}

		if (Type.isPlainObject(options.shiftedPosition))
		{
			this.shiftedPosition = options.shiftedPosition;
		}

		this.collapsed = options.collapsed !== false;
		this.muted = false;
		this.shifted = false;

		this.maxVisibleItems = Type.isNumber(options.maxVisibleItems) ? Math.max(options.maxVisibleItems, 1) : 5;

		this.addItems(options.items);

		const item = this.restoreItemFromLocalStorage();
		if (item !== null)
		{
			const { entityType, entityId } = item;
			if (this.getItem(entityType, entityId))
			{
				this.clearLocalStorage();
			}
			else
			{
				this.minimizeItem(item);
			}
		}
	}

	show(): void
	{
		Dom.addClass(this.getContainer(), '--show');
	}

	isShown(): boolean
	{
		return Dom.hasClass(this.getContainer(), '--show');
	}

	hide(): void
	{
		Dom.removeClass(this.getContainer(), '--show');
	}

	mute(): boolean
	{
		if (this.muted)
		{
			return false;
		}

		this.muted = true;
		Dom.addClass(this.getContainer(), '--muted');

		return true;
	}

	unmute(): boolean
	{
		if (!this.muted)
		{
			return false;
		}

		this.muted = false;
		Dom.removeClass(this.getContainer(), '--muted');

		return true;
	}

	isMuted(): boolean
	{
		return this.muted;
	}

	toggleMuteness(): boolean
	{
		if (this.canShowOnTop())
		{
			return this.unmute();
		}

		return this.mute();
	}

	shift(): boolean
	{
		if (this.shifted)
		{
			return false;
		}

		this.shifted = true;
		Dom.addClass(this.getContainer(), '--shifted');
		Dom.style(document.body, '--side-panel-toolbar-shifted', 1);
		this.setPosition(this.getContainer(), this.shiftedPosition);

		return true;
	}

	unshift(): boolean
	{
		if (!this.shifted)
		{
			return false;
		}

		this.shifted = false;
		Dom.removeClass(this.getContainer(), '--shifted');
		Dom.style(document.body, '--side-panel-toolbar-shifted', null);
		this.setPosition(this.getContainer(), this.initialPosition);

		return true;
	}

	isShifted(): boolean
	{
		return this.shifted;
	}

	toggleShift(): boolean
	{
		const sliders = getInstance().getOpenSliders();
		if (sliders.length === 0 || (sliders.length === 1 && !sliders[0].isOpen()))
		{
			return this.unshift();
		}

		return this.shift();
	}

	setPosition(container: HTMLElement, position): boolean
	{
		for (const prop of ['top', 'right', 'bottom', 'left'])
		{
			Dom.style(container, prop, null);
			if (Type.isStringFilled(position[prop]))
			{
				Dom.style(container, prop, position[prop]);
			}
		}
	}

	collapse(immediately: boolean = false): void
	{
		if (this.collapsed)
		{
			return;
		}

		if (immediately === true)
		{
			Dom.addClass(this.getContainer(), '--collapsed');
			Dom.style(this.getContentContainer(), 'width', null);
		}
		else
		{
			const width = this.getContentContainer().scrollWidth;
			Dom.style(this.getContentContainer(), 'width', `${width}px`);

			Event.unbindAll(this.getContentContainer(), 'transitionend');

			requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					Dom.style(this.getContentContainer(), 'width', 0);
					Event.bindOnce(this.getContentContainer(), 'transitionend', () => {
						Dom.addClass(this.getContainer(), '--collapsed');
						Dom.style(this.getContentContainer(), 'width', null);
					});
				});
			});
		}

		this.collapsed = true;
	}

	expand(immediately: boolean = false): void
	{
		if (!this.collapsed)
		{
			return;
		}

		if (immediately === true)
		{
			Dom.removeClass(this.getContainer(), '--collapsed');
			Dom.style(this.getContentContainer(), 'width', null);
		}
		else
		{
			Dom.removeClass(this.getContainer(), '--collapsed');
			const width = this.getContentContainer().scrollWidth;
			Dom.style(this.getContentContainer(), 'width', 0);

			Event.unbindAll(this.getContentContainer(), 'transitionend');

			requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					Dom.style(this.getContentContainer(), 'width', `${width}px`);
					Event.bindOnce(this.getContentContainer(), 'transitionend', () => {
						Dom.style(this.getContentContainer(), 'width', null);
					});
				});
			});
		}

		this.collapsed = false;
	}

	toggle(): void
	{
		if (this.collapsed)
		{
			this.request('expand');
			this.expand();
		}
		else
		{
			this.request('collapse');
			this.collapse();
		}
	}

	isCollapsed(): boolean
	{
		return this.collapsed;
	}

	getItems()
	{
		return this.items;
	}

	getItemsCount(): number
	{
		return this.items.length;
	}

	addItems(itemsOptions)
	{
		if (Type.isArrayFilled(itemsOptions))
		{
			itemsOptions.forEach((itemOptions) => {
				this.addItem(itemOptions);
			});
		}
	}

	addItem(itemOptions: ToolbarItemOptions): ToolbarItem | null
	{
		const item = this.createItem(itemOptions);
		if (item === null)
		{
			return null;
		}

		this.items.push(item);

		if (this.rendered)
		{
			this.redraw();
		}

		return item;
	}

	/**
	 *
	 * @param itemOptions
	 * @returns {ToolbarItem|null}
	 */
	prependItem(itemOptions): ToolbarItem | null
	{
		const item = this.createItem(itemOptions);
		if (item === null)
		{
			return null;
		}

		this.items.unshift(item);

		if (this.rendered)
		{
			this.redraw();
		}

		return item;
	}

	createItem(itemOptions: ToolbarItemOptions): ToolbarItem | null
	{
		const options = Type.isPlainObject(itemOptions) ? itemOptions : {};

		if (
			!Type.isStringFilled(options.entityType)
			|| !(Type.isStringFilled(options.entityId) || Type.isNumber(options.entityId))
			|| !Type.isStringFilled(options.title)
			|| !Type.isStringFilled(options.url)
		)
		{
			return null;
		}

		const item = new ToolbarItem(options);
		if (!Type.isStringFilled(item.getEntityName()))
		{
			const minimizeOptions = getInstance().getMinimizeOptions(item.getUrl());
			if (Type.isPlainObject(minimizeOptions) && Type.isStringFilled(minimizeOptions.entityName))
			{
				item.setEntityName(minimizeOptions.entityName);
			}
		}

		item.subscribe('onRemove', this.handleItemRemove.bind(this));

		return item;
	}

	/**
	 * @private
	 */
	minimizeItem(itemOptions: ToolbarItemOptions): ToolbarItem | null
	{
		const { entityType, entityId } = itemOptions;
		let item = this.getItem(entityType, entityId);
		const itemExists = item !== null;
		if (!itemExists)
		{
			item = this.prependItem(itemOptions);
		}

		if (item !== null)
		{
			if (!itemExists)
			{
				this.saveItemToLocalStorage(item);
			}

			this.request('minimize', item)
				.then((response) => {
					if (response.status === 'success')
					{
						this.clearLocalStorage();
					}
				}).catch(() => {
					this.clearLocalStorage();
					this.removeItem(item);
				})
			;
		}

		return item;
	}

	saveItemToLocalStorage(item): void
	{
		const cache = { item, ttl: Date.now() };
		localStorage.setItem(this.lsKey, JSON.stringify(cache));
	}

	restoreItemFromLocalStorage(): JsonObject | null
	{
		const data = localStorage.getItem(this.lsKey);
		if (Type.isStringFilled(data))
		{
			const { item, ttl } = JSON.parse(data);
			if ((Date.now() - ttl) > 10000)
			{
				this.clearLocalStorage();

				return null;
			}

			if (Type.isPlainObject(item))
			{
				return item;
			}
		}

		return null;
	}

	clearLocalStorage(): void
	{
		localStorage.removeItem(this.lsKey);
	}

	getContext(): string
	{
		return this.context;
	}

	request(action, item, data): Promise
	{
		const additional = Type.isPlainObject(data) ? data : {};

		return Ajax.runAction(`main.api.sidepanel.toolbar.${action}`, {
			json: {
				toolbar: {
					context: this.getContext(),
				},
				item: item ? item.toJSON() : null,
				...additional,
			},
		});
	}

	handleItemRemove(event): void
	{
		const item = event.getTarget();
		item.hideTooltip();
		this.removeItem(item);
	}

	handleMenuItemRemove(event): void
	{
		event.preventDefault();
		event.stopPropagation();

		const itemId = event.currentTarget.dataset.menuItemId;
		const itemToRemove = this.getItemById(itemId);
		if (itemToRemove)
		{
			this.removeItem(itemToRemove);
		}

		const menu = this.getMenu();
		if (menu)
		{
			menu.removeMenuItem(itemId);

			const invisibleItemsCount = this.getItems().reduce((count, item) => {
				return item.isRendered() ? count : count + 1;
			}, 0);

			if (invisibleItemsCount > 0)
			{
				menu.getPopupWindow().adjustPosition();
			}
			else
			{
				menu.close();
			}
		}
	}

	removeItem(itemToRemove): void
	{
		itemToRemove.remove();
		this.items = this.items.filter((item) => {
			return item !== itemToRemove;
		});

		const restored = this.restoreItemFromLocalStorage();
		if (restored !== null)
		{
			const { entityType, entityId } = restored;
			if (itemToRemove.getEntityType() === entityType && itemToRemove.getEntityId() === entityId)
			{
				this.clearLocalStorage();
			}
		}

		if (this.rendered)
		{
			this.redraw();
			this.request('remove', itemToRemove);

			if (this.getItemsCount() === 0)
			{
				this.hide();
			}
		}
	}

	redraw(): void
	{
		let visibleItemsCount = 0;
		for (let i = 0; i < this.getItems().length; i++)
		{
			const item = this.getItems()[i];
			if (visibleItemsCount >= this.maxVisibleItems)
			{
				if (item.isRendered())
				{
					item.remove();
				}
			}
			else
			{
				if (!item.isRendered())
				{
					const previousItem = this.getItems()[i - 1] || null;
					const nextItem = this.getItems()[i + 1] || null;
					if (previousItem)
					{
						item.insertAfter(previousItem.getContainer());
					}
					else if (nextItem)
					{
						// eslint-disable-next-line @bitrix24/bitrix24-rules/no-native-dom-methods
						item.insertBefore(nextItem.getContainer());
					}
					else
					{
						item.appendTo(this.getItemsContainer());
					}
				}

				visibleItemsCount++;
			}
		}
	}

	removeAll(): void
	{
		this.getItemsContainer().innerHTML = '';
		this.items = [];
		this.clearLocalStorage();
	}

	getItem(entityType: string, entityId: string | number): ToolbarItem | null
	{
		return this.items.find((item) => item.getEntityType() === entityType && item.getEntityId() === entityId) || null;
	}

	getItemByUrl(url: string): ToolbarItem | null
	{
		return this.items.find((item) => item.getUrl() === url) || null;
	}

	getItemById(id: string): ToolbarItem | null
	{
		return this.items.find((item) => item.getId() === id) || null;
	}

	getContainer(): HTMLElement
	{
		return this.refs.remember('container', () => {
			const classes = [];
			if (this.collapsed)
			{
				classes.push('--collapsed');
			}

			const container = Tag.render`
				<div class="side-panel-toolbar ${classes.join(' ')}">
					${this.getContentContainer()}
					<div class="side-panel-toolbar-toggle" onclick="${this.handleToggleClick.bind(this)}"></div>
				</div>
			`;

			this.setPosition(container, this.initialPosition);
			Dom.append(container, document.body);
			ZIndexManager.register(container, { alwaysOnTop: true });
			this.rendered = true;

			const toggleMuteness = Runtime.debounce(this.toggleMuteness, 50, this);
			EventEmitter.subscribe('BX.Main.Popup:onShow', toggleMuteness);
			EventEmitter.subscribe('BX.Main.Popup:onClose', toggleMuteness);
			EventEmitter.subscribe('BX.Main.Popup:onDestroy', toggleMuteness);
			EventEmitter.subscribe('onWindowClose', toggleMuteness);
			EventEmitter.subscribe('onWindowRegister', toggleMuteness);

			let forceCollapsed = false;
			const onSliderClose = () => {
				this.toggleMuteness();
				if (this.isMuted())
				{
					return;
				}

				this.toggleShift();
				if (!this.isShifted() && forceCollapsed)
				{
					forceCollapsed = false;
					this.expand();
				}
			};

			EventEmitter.subscribe('SidePanel.Slider:onClosing', onSliderClose);
			EventEmitter.subscribe('SidePanel.Slider:onCloseComplete', onSliderClose);
			EventEmitter.subscribe('SidePanel.Slider:onDestroyComplete', onSliderClose);
			EventEmitter.subscribe('SidePanel.Slider:onOpening', () => {
				this.toggleMuteness();
				if (this.isMuted())
				{
					return;
				}

				if (!this.isCollapsed())
				{
					forceCollapsed = true;
					this.collapse();
				}

				this.toggleShift();
			});

			EventEmitter.subscribe('BX.UI.Viewer.Controller:onBeforeShow', toggleMuteness);
			EventEmitter.subscribe(
				'BX.UI.Viewer.Controller:onClose',
				Runtime.debounce(this.toggleMuteness, 500, this),
			);

			Event.bind(window, 'resize', Runtime.throttle(() => {
				const menu = this.getMenu();
				if (menu !== null)
				{
					menu.close();
				}
			}, 300));

			return container;
		});
	}

	getContentContainer(): HTMLElement
	{
		return this.refs.remember('content-container', () => {
			return Tag.render`
				<div class="side-panel-toolbar-content">
					<div class="side-panel-toolbar-collapse-btn" onclick="${this.handleToggleClick.bind(this)}">
						<div class="ui-icon-set --chevron-right"></div>
					</div>
					${this.getItemsContainer()}
					${this.getMoreButton()}
				</div>
			`;
		});
	}

	getItemsContainer(): HTMLElement
	{
		return this.refs.remember('items-container', () => {
			const container = Tag.render`<div class="side-panel-toolbar-items"></div>`;
			[...this.items].slice(0, this.maxVisibleItems).forEach((item) => {
				item.appendTo(container);
			});

			return container;
		});
	}

	getMoreButton(): HTMLElement
	{
		return this.refs.remember('more-button', () => {
			return Tag.render`
				<div class="side-panel-toolbar-more-btn" onclick="${this.handleMoreBtnClick.bind(this)}">
					<div class="ui-icon-set --more"></div>
				</div>
			`;
		});
	}

	handleMoreBtnClick(event): void
	{
		const targetNode = this.getMoreButton();
		const rect = targetNode.getBoundingClientRect();
		const targetNodeWidth = rect.width;

		const items = [...this.items].filter((item) => !item.isRendered()).map((item) => {
			const title = (
				Type.isStringFilled(item.getEntityName())
					? `${item.getEntityName()}\n${item.getTitle()}`
					: item.getTitle()
			);

			return {
				id: item.getId(),
				html: this.createMenuItemText(item),
				title,
				href: item.getUrl(),
				onclick: () => {
					menu.close();
				},
			};
		});

		if (items.length > 0)
		{
			items.push({
				delimiter: true,
			});
		}

		items.push({
			text: Loc.getMessage('MAIN_SIDEPANEL_REMOVE_ALL'),
			onclick: () => {
				this.removeAll();
				this.hide();
				menu.close();

				this.request('removeAll');
			},
		});

		const menu = MenuManager.create({
			id: 'sidepanel-toolbar-more-btn',
			cacheable: false,
			bindElement: rect,
			bindOptions: {
				forceBindPosition: true,
				forceTop: true,
				position: 'top',
			},
			maxWidth: 260,
			fixed: true,
			offsetTop: 0,
			maxHeight: 305,
			items,
			events: {
				onShow: (event) => {
					const popup = event.getTarget();
					const popupWidth = popup.getPopupContainer().offsetWidth;
					const offsetLeft = (targetNodeWidth / 2) - (popupWidth / 2);
					const angleShift = Popup.getOption('angleLeftOffset') - Popup.getOption('angleMinTop');

					popup.setAngle({ offset: popupWidth / 2 - angleShift });
					popup.setOffset({ offsetLeft: offsetLeft + Popup.getOption('angleLeftOffset') });
				},
			},
		});

		menu.show();
	}

	canShowOnTop(): boolean
	{
		const popups = PopupManager.getPopups();
		for (const popup of popups)
		{
			if (!popup.isShown())
			{
				continue;
			}

			if (
				popup.getId().startsWith('timeman_weekly_report_popup_')
				|| popup.getId().startsWith('timeman_daily_report_popup_')
				|| Dom.hasClass(popup.getPopupContainer(), 'b24-whatsnew__popup')
			)
			{
				return false;
			}
		}

		if (Reflection.getClass('BX.UI.Viewer.Instance') && BX.UI.Viewer.Instance.isOpen())
		{
			return false;
		}

		const sliders = getInstance().getOpenSliders();
		for (const slider of sliders)
		{
			const sliderId = slider.getUrl().toString();
			if (
				slider.shouldHideToolbarOnOpen()
				|| sliderId.startsWith('im:slider')
				|| sliderId.startsWith('release-slider')
				|| sliderId.startsWith('main:helper')
				|| sliderId.startsWith('ui:info_helper')
			)
			{
				return false;
			}
		}

		const stack = ZIndexManager.getStack(document.body);
		const components = stack === null ? [] : stack.getComponents();
		for (const component of components)
		{
			if (component.getOverlay() !== null && component.getOverlay().offsetWidth > 0)
			{
				return false;
			}
		}

		return true;
	}

	getMenu(): Menu | null
	{
		return MenuManager.getMenuById('sidepanel-toolbar-more-btn');
	}

	createMenuItemText(item): HTMLElement
	{
		return Tag.render`
			<span class="side-panel-toolbar-menu-item">${[
				Tag.render`
					<span class="side-panel-toolbar-menu-item-title">${Text.encode(item.getTitle())}</span>
				`,
				Tag.render`
					<span
						class="side-panel-toolbar-menu-item-remove"
						data-slider-ignore-autobinding="true"
						data-menu-item-id="${item.getId()}"
						onclick="${this.handleMenuItemRemove.bind(this)}"
					>
						<span class="ui-icon-set --cross-20" data-slider-ignore-autobinding="true"></span>
					</span>
				`,
			]}</span>
		`;
	}

	handleToggleClick(): void
	{
		this.toggle();
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit