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/ilovecveti.ru/bitrix/js/fileman/userfield/address_widget/src/view/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/ilovecveti.ru/bitrix/js/fileman/userfield/address_widget/src/view/editentry.js
import { Address as AddressEntity, AddressStringConverter, AddressType, ControlMode, Format } from 'location.core';
import { Address as AddressWidget, AutocompleteFeature, Factory, State } from 'location.widget';
import { Dom, Event, Loc, Tag, Text } from 'main.core';
import { EventEmitter } from 'main.core.events';
import type { EditEntryProps } from './editentryprops';

export class EditEntry extends EventEmitter
{
	#widget: AddressWidget;
	#nodes: { name: string, node: Element } = {};
	#wrapper: Element;
	#address: AddressEntity = null;
	#fieldName: string;
	#fieldFormName: string;
	#showMap: boolean = true;
	#enableRemoveButton: boolean = false;
	#isCompactMode: boolean = false;
	#initialAddressId: ?number = null;
	#areDetailsShown: boolean = false;
	#isLoading: boolean = false;
	#isDropdownLoading: boolean = false;
	#isDestroyed: boolean = false;
	#showDetailsToggle: boolean = true;

	static onRemoveInputButtonClickedEvent = 'onRemoveInputButtonClicked';

	constructor(props: EditEntryProps)
	{
		super();
		this.setEventNamespace('Fileman.EditEntry');
		this.#wrapper = props.wrapper;
		this.#fieldName = props.fieldName;
		this.#fieldFormName = props.fieldFormName;
		this.#enableRemoveButton = props.enableRemoveButton;
		this.#initialAddressId = props.initialAddressId;
		this.#showMap = props.showMap;
		this.#showDetailsToggle = Boolean(props.showDetailsToggle ?? true);

		if (props.address)
		{
			this.#address = props.address;
		}
		if (props.isCompactMode)
		{
			this.#isCompactMode = props.isCompactMode;
		}
	}

	layout(): Element
	{
		const factory = new Factory();
		this.#widget = factory.createAddressWidget({
			address: this.#address,
			mode: ControlMode.edit,
			popupOptions: {
				offsetLeft: 14,
			},
			popupBindOptions: {
				forceBindPosition: true,
				position: 'right',
			},
			mapBehavior: 'auto',
			useFeatures: {
				fields: true,
				map: this.#showMap,
				autocomplete: true,
			},
		});

		this.#nodes.userInput = Tag.render`<input type="text" class="ui-ctl-element" />`;

		this.#nodes.fieldsContainer = Tag.render`<div class="location-fields-control-block"></div>`;

		if (this.#showDetailsToggle)
		{
			this.#nodes.detailsToggle = Tag.render`<span class="ui-link ui-link-secondary address-control-mode-switch">${Loc.getMessage(
				'ADDRESS_USERFIELD_DETAILS')}</span>`;
			Event.bind(this.#nodes.detailsToggle, 'click', this.onDetailsToggleClick.bind(this));
		}

		let inputValue = this.getInitialAddressFieldValue();
		this.#nodes.fieldValueInput = Tag.render`<input type="hidden" name="${this.#fieldFormName}" value="${inputValue}" />`;

		this.#nodes.inputIcon = Tag.render`<button type="button" class="ui-ctl-after ui-ctl-icon-clear"></button>`;
		Event.bind(this.#nodes.inputIcon, 'click', this.onInputIconClick.bind(this));

		this.#widget.subscribeOnAddressChangedEvent(this.onAddressChanged.bind(this));
		this.#widget.subscribeOnStateChangedEvent(this.onWidgetStateChangedEvent.bind(this));
		this.#widget.subscribeOnFeatureEvent(this.onFeatureEvent.bind(this));

		this.#nodes.entryWrapper = Tag.render`
			<div class="edit-entry-input-wrapper">
				<div class="fields address field-item edit ui-ctl ui-ctl-after-icon ${this.getUserInputSizeClass()}">
					${this.#nodes.userInput}
					${this.#nodes.fieldsContainer}
					${this.#nodes.inputIcon}
				</div>
			</div>
		`;

		// a workaround for bizproc conditionals; their conditionals popup seems to use the topmost <input>'s value
		const hiddenFormattedInputValue = this.#address ? this.getRawValueForHiddenFormattedInput(this.#address) : '';
		this.#nodes.hiddenFormattedAddressInput = Tag.render`<input type="hidden" name="${this.#fieldName}_formatted" value="${hiddenFormattedInputValue}" />`;

		// a flag used to identify values set manually by the user
		const manualEditFlagNode = Tag.render`<input type="hidden" name="${this.#fieldName}_manual_edit" value="Y">`;

		this.#nodes.layout = Tag.render`
			<div class="edit-entry-layout-wrapper ${this.getLayoutSizeClass()}">
				${this.#getAddressControlSwitchContainer()}
				${this.#nodes.hiddenFormattedAddressInput}
				${this.#nodes.entryWrapper}
				${manualEditFlagNode}
			</div>
		`;

		if (this.#enableRemoveButton)
		{
			Dom.append(this.getRemoveInputButton(this.#nodes.layout), this.#nodes.entryWrapper);
		}

		Dom.append(this.#nodes.fieldValueInput, this.#nodes.layout);

		this.#widget.render({
			inputNode: this.#nodes.userInput,
			mapBindElement: this.#wrapper,
			controlWrapper: this.#nodes.layout,
			fieldsContainer: this.#nodes.fieldsContainer,
		});

		return this.#nodes.layout;
	}

	#getAddressControlSwitchContainer(): ?HTMLElement
	{
		if (!this.#showDetailsToggle)
		{
			return null;
		}

		return Tag.render`
			<div class="address-control-mode-switch-wrapper">
				${this.#nodes.detailsToggle}
			</div>
		`;
	}

	getUserInputSizeClass(): string
	{
		return this.#isCompactMode ? 'ui-ctl-wd' : 'ui-ctl-w100';
	}

	getLayoutSizeClass(): string
	{
		return this.#isCompactMode ? 'compact' : '';
	}

	getRemoveInputButton(layout: Element)
	{
		const removeInputButton = Tag.render`
			<span class="uf-address-search-input-remove"></span>
		`;

		Event.bind(removeInputButton, 'click', (event) => {
			this.emit(EditEntry.onRemoveInputButtonClickedEvent);
		});

		return removeInputButton;
	}

	destroy()
	{
		if (!this.#nodes.layout)
		{
			return;
		}

		if (this.#widget)
		{
			this.#widget.destroy();
		}

		if (this.#address && this.#address.id > 0)
		{
			Dom.clean(this.#nodes.layout);
			const input = Tag.render`<input type="hidden" name="${this.#fieldFormName}" value="${this.#address.id}_del" />`;
			Dom.append(input, this.#nodes.layout);
			this.emitFieldChangedEvent();
		}
		else
		{
			Dom.remove(this.#nodes.layout);
		}

		this.#isDestroyed = true;
	}

	isDestroyed(): boolean
	{
		return this.#isDestroyed;
	}

	onAddressChanged(event)
	{
		const initialAddressId = parseInt(this.#initialAddressId);
		/** @type {AddressEntity} */
		const address = event.data.address;
		if (!address)
		{
			return;
		}

		// when we clear the input, the address' id becomes 0, and because of it a new address is created upon
		// saving. we can set the address's id to the old id to edit the old address instead
		if (initialAddressId && parseInt(address.id) !== initialAddressId)
		{
			address.id = initialAddressId;
		}

		this.#nodes.fieldValueInput.value = this.getChangedAddressFieldValue(address);

		this.#nodes.hiddenFormattedAddressInput.value = this.getRawValueForHiddenFormattedInput(address);

		this.emitFieldChangedEvent();
	}

	onWidgetStateChangedEvent(event)
	{
		const state = event.data.state;
		this.#isLoading = (state === State.DATA_LOADING);

		this.refreshInputIcon();
	}

	onFeatureEvent(event)
	{
		if (event.data.feature instanceof AutocompleteFeature)
		{
			this.#isDropdownLoading = (event.data.eventCode === AutocompleteFeature.searchStartedEvent);

			this.refreshInputIcon();
		}
	}

	isInputLoading(): boolean
	{
		return this.#isLoading || this.#isDropdownLoading;
	}

	refreshInputIcon()
	{
		if (!this.#nodes.inputIcon)
		{
			return;
		}

		if (this.isInputLoading())
		{
			Dom.removeClass(this.#nodes.inputIcon, 'ui-ctl-icon-clear');
			Dom.addClass(this.#nodes.inputIcon, 'ui-ctl-icon-loader');
		}
		else
		{
			Dom.removeClass(this.#nodes.inputIcon, 'ui-ctl-icon-loader');
			Dom.addClass(this.#nodes.inputIcon, 'ui-ctl-icon-clear');
		}
	}

	getInitialAddressFieldValue(): string
	{
		let inputValue =  '';

		if (this.#address?.id == 0)
		{
			if (this.#address.location)
			{
				// JSON has probably been passed as the component's value; we need to create a new address
				inputValue = Text.encode(this.#address.toJson());
			}
			else
			{
				// for compatibility with the format used before the switch to location module's addresses
				inputValue = `${this.#address.getFieldValue(AddressType.ADDRESS_LINE_2)}|${this.#address.latitude};${this.#address.longitude}`;
			}
		}
		else if (this.#address?.id > 0)
		{
			inputValue = `${this.getFormattedAddress(this.#address)}|${this.#address.latitude};${this.#address.longitude}|${this.#address.id}`;
		}

		return inputValue;
	}

	getChangedAddressFieldValue(address: AddressEntity): string
	{
		return address.toJson();
	}

	getFormattedAddress(address: AddressEntity): string
	{
		const format = new Format(JSON.parse(BX.message('LOCATION_WIDGET_DEFAULT_FORMAT')));
		return address.toString(format, AddressStringConverter.STRATEGY_TYPE_TEMPLATE_COMMA) ?? '';
	}

	getRawValueForHiddenFormattedInput(address: AddressEntity): string
	{
		const formattedAddress = this.getFormattedAddress(address);
		if (
			(parseInt(address.latitude) !== 0 || parseInt(address.longitude) !== 0)
			&& (address.latitude !== '' && address.longitude !== '')
		)
		{
			return `${formattedAddress}|${address.latitude};${address.longitude}`;
		}
		return formattedAddress;
	}

	onInputIconClick()
	{
		if (this.isInputLoading())
		{
			return;
		}

		this.#nodes.userInput.focus();

		this.#widget.resetView();
		this.#widget.address = null;

		if (this.#address && this.#address.id > 0)
		{
			this.#nodes.fieldValueInput.value = this.#address.id + '_del';
		}
		else
		{
			this.#nodes.fieldValueInput.value = '';
		}

		this.emitFieldChangedEvent();
	}

	onDetailsToggleClick()
	{
		if (!this.#nodes.fieldsContainer || !this.#nodes.detailsToggle)
		{
			return;
		}

		const fieldsContainer = this.#nodes.fieldsContainer;
		const detailsToggle = this.#nodes.detailsToggle;
		if (this.#areDetailsShown && Dom.hasClass(fieldsContainer, 'visible'))
		{
			Dom.removeClass(fieldsContainer, 'visible');
			detailsToggle.innerText = Loc.getMessage('ADDRESS_USERFIELD_DETAILS');
		}
		else
		{
			Dom.addClass(fieldsContainer, 'visible');
			detailsToggle.innerText = Loc.getMessage('ADDRESS_USERFIELD_NO_DETAILS');
		}

		this.#areDetailsShown = !this.#areDetailsShown;
	}

	emitFieldChangedEvent()
	{
		BX.onCustomEvent(window, 'onUIEntityEditorUserFieldExternalChanged', [this.#fieldName]);
		BX.onCustomEvent(window, 'onCrmEntityEditorUserFieldExternalChanged', [this.#fieldName]);
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit