AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 3.144.88.67
Web Server : Apache
System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64
User : bitrix ( 600)
PHP Version : 8.1.27
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/bitrix/www/bitrix/js/ui/stepprocessing/src/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/bitrix/www/bitrix/js/ui/stepprocessing/src/process.js
// @flow
import {Type, Text, Loc} from 'main.core';
import { BaseEvent, EventEmitter } from 'main.core.events';
import type { ProcessOptions, OptionsField, QueueAction, ProcessResult, ControllerResponse } from './process-types';
import { ProcessResultStatus, ProcessState } from './process-types';
import { Dialog } from './dialog';

/**
 * @namespace {BX.UI.StepProcessing}
 */
export const ProcessEvent = {
	StateChanged: 'BX.UI.StepProcessing.StateChanged',
	BeforeRequest: 'BX.UI.StepProcessing.BeforeRequest'
}

/**
 * @namespace {BX.UI.StepProcessing}
 */
export const ProcessCallback = {
	StateChanged: 'StateChanged',
	RequestStart: 'RequestStart',
	RequestStop: 'RequestStop',
	RequestFinalize: 'RequestFinalize',
	StepCompleted: 'StepCompleted'
}

export const ProcessDefaultLabels = {
	AuthError: Loc.getMessage('UI_STEP_PROCESSING_AUTH_ERROR'),
	RequestError: Loc.getMessage('UI_STEP_PROCESSING_REQUEST_ERR'),
	DialogStartButton: Loc.getMessage('UI_STEP_PROCESSING_BTN_START'),
	DialogStopButton: Loc.getMessage('UI_STEP_PROCESSING_BTN_STOP'),
	DialogCloseButton: Loc.getMessage('UI_STEP_PROCESSING_BTN_CLOSE'),
	RequestCanceling: Loc.getMessage('UI_STEP_PROCESSING_CANCELING'),
	RequestCanceled: Loc.getMessage('UI_STEP_PROCESSING_CANCELED'),
	RequestCompleted: Loc.getMessage('UI_STEP_PROCESSING_COMPLETED'),
	DialogExportDownloadButton: Loc.getMessage('UI_STEP_PROCESSING_FILE_DOWNLOAD'),
	DialogExportClearButton: Loc.getMessage('UI_STEP_PROCESSING_FILE_DELETE'),
	WaitingResponse: Loc.getMessage('UI_STEP_PROCESSING_WAITING'),
};

const EndpointType = {
	Controller: 'controller',
	Component: 'component'
};

/**
 * Long running process.
 *
 * @namespace {BX.UI.StepProcessing}
 * @event BX.UI.StepProcessing.StateChanged
 * @event BX.UI.StepProcessing.BeforeRequest
 */
export class Process
{
	options: ProcessOptions;

	id: string;

	// Ajax endpoint
	endpointType: EndpointType.Controller|EndpointType.Component;
	controller: string;
	controllerDefault: string;
	component: string;
	componentMode: 'class'|'ajax';
	hash: string;
	action: string = '';
	method: 'POST'|'GET' = 'POST';
	params: {[name: string]: any} = {};

	/**
	 * @private
	 */
	xhr: ?XMLHttpRequest;
	ajaxPromise: ?Promise;
	isRequestRunning: boolean = false;
	networkErrorCount: 0;

	// Queue
	queue: Array<QueueAction> = [];
	currentStep: number = -1;
	state: $Values<ProcessState> = ProcessState.intermediate;

	// Dialog
	dialog: Dialog;
	initialOptionValues: {[id: string]: any} = {};
	optionsFields: {[id: string]: OptionsField} = {};

	// Events
	handlers: {[event: string]: any => {}} = {};

	// Messages
	messages: Map<string, string> = new Map;

	constructor(options: ProcessOptions)
	{
		this.options = Type.isPlainObject(options) ? options : {};

		this.id = this.getOption('id', '');
		if (!Type.isStringFilled(this.id))
		{
			this.id = 'Process_' + Text.getRandom().toLowerCase();
		}

		const controller = this.getOption('controller', '');
		const component = this.getOption('component', '');
		if (Type.isStringFilled(controller))
		{
			this.controller = controller;
			this.controllerDefault = controller;
			this.endpointType = EndpointType.Controller;
		}
		else if (Type.isStringFilled(component))
		{
			this.component = component;
			this.endpointType = EndpointType.Component;
			this.componentMode = this.getOption('componentMode', 'class');
		}
		if (!Type.isStringFilled(this.controller))
		{
			if (!Type.isStringFilled(this.component))
			{
				throw new TypeError("BX.UI.StepProcessing: There no any ajax endpoint was defined.");
			}
		}

		this
			.setQueue(this.getOption('queue', []))
			.setParams(this.getOption('params', {}))
			.setOptionsFields(this.getOption('optionsFields', {}))
			.setHandlers(this.getOption('handlers', {}))
			.setMessages(ProcessDefaultLabels)
			.setMessages(this.getOption('messages', {}))
		;
	}

	destroy()
	{
		if (this.dialog instanceof Dialog)
		{
			this.dialog.close().destroy();
			this.dialog = null;
		}

		this._closeConnection();
	}

	//region Run

	start(startStep?: number = 1)
	{
		this._refreshHash();

		startStep = startStep || 1;
		if (
			this.state === ProcessState.intermediate ||
			this.state === ProcessState.stopped ||
			this.state === ProcessState.completed
		)
		{
			if (!this.getDialog().checkOptionFields())
			{
				return;
			}

			this.getDialog().clearErrors().clearWarnings();

			this.networkErrorCount = 0;

			if (this.getQueueLength() > 0)
			{
				this.currentStep = 0;
				if (startStep > 1)
				{
					this.currentStep = startStep - 1;
				}

				if (this.endpointType === EndpointType.Controller)
				{
					if (Type.isStringFilled(this.queue[this.currentStep].controller))
					{
						this.setController(this.queue[this.currentStep].controller);
					}
				}

				if (!Type.isStringFilled(this.queue[this.currentStep].action))
				{
					throw new Error("BX.UI.StepProcessing: Could not find controller action at the queue position.");
				}
				this.setAction(this.queue[this.currentStep].action);

				this.startRequest();

				if (this.queue[this.currentStep].title)
				{
					this.getDialog().setSummary(this.queue[this.currentStep].title);
				}
				else
				{
					this.getDialog().setSummary(this.getMessage('WaitingResponse'));
				}
			}
			else
			{
				this.startRequest();
			}
		}
		return this;
	}

	stop()
	{
		if (this.state === ProcessState.running)
		{
			this.stopRequest();
			this.currentStep = -1;
		}
		return this;
	}

	//endregion

	//region Request

	startRequest(): ?Promise
	{
		if (this.isRequestRunning || this.state === ProcessState.canceling)
		{
			return this.ajaxPromise;
		}
		this.isRequestRunning = true;
		this.ajaxPromise = null;

		let actionData = new FormData();

		let appendData = (data, prefix) => {
			if (Type.isPlainObject(data))
			{
				Object.keys(data).forEach(name => {
					let id = name;
					if (prefix)
					{
						id = prefix+'['+name+']';
					}
					if (Type.isArray(data[name]) || Type.isPlainObject(data[name]))
					{
						appendData(data[name], id);
					}
					else
					{
						actionData.append(id, data[name]);
					}
				});
			}
			else if (Type.isArray(data))
			{
				data.forEach(element => actionData.append(prefix+'[]', element));
			}
		};

		appendData(this.params);
		if (this.queue[this.currentStep].params)
		{
			appendData(this.queue[this.currentStep].params);
		}

		let initialOptions = this.getDialog().getOptionFieldValues();
		if (BX.type.isNotEmptyObject(initialOptions))
		{
			appendData(initialOptions);
			this.initialOptionValues = initialOptions;
			this.storeOptionFieldValues(initialOptions);
		}
		else
		{
			Object.keys(this.initialOptionValues).forEach(name => {
				// don't repeat file uploading
				if (this.initialOptionValues[name] instanceof File)
				{
					delete (this.initialOptionValues[name]);
				}
			});
			appendData(this.initialOptionValues);
		}

		this.setState(ProcessState.running);

		if (this.hasActionHandler(ProcessCallback.RequestStart))
		{
			this.callActionHandler(ProcessCallback.RequestStart, [actionData]);
		}
		else if (this.hasHandler(ProcessCallback.RequestStart))
		{
			this.callHandler(ProcessCallback.RequestStart, [actionData]);
		}

		EventEmitter.emit(ProcessEvent.BeforeRequest, new BaseEvent({data: {process: this, actionData: actionData}}));

		let params = {
			data: actionData,
			method: this.method,
			onrequeststart: this._onRequestStart.bind(this)
		};
		if (this.endpointType === EndpointType.Controller)
		{
			this.ajaxPromise =
				BX.ajax.runAction(this.controller + '.' + this.getAction(), params)
					.then(
						this._onRequestSuccess.bind(this),
						this._onRequestFailure.bind(this)
					);
		}
		else if (this.endpointType === EndpointType.Component)
		{
			params.data.mode = this.componentMode;
			if ('signedParameters' in params.data)
			{
				params.signedParameters = params.data.signedParameters;
				delete params.data.signedParameters;
			}
			this.ajaxPromise =
				BX.ajax.runComponentAction(this.component, this.getAction(), params)
					.then(
						this._onRequestSuccess.bind(this),
						this._onRequestFailure.bind(this)
					);
		}

		return this.ajaxPromise;
	}

	stopRequest(): ?Promise
	{
		if (this.state === ProcessState.canceling)
		{
			return this.ajaxPromise;
		}

		this.setState(ProcessState.canceling);

		this._closeConnection();

		let actionData = BX.clone(this.params);

		actionData.cancelingAction = this.getAction();

		this.getDialog().setSummary(this.getMessage("RequestCanceling"));

		let proceedAction = true;
		if (this.hasActionHandler(ProcessCallback.RequestStop))
		{
			proceedAction = false;
			this.callActionHandler(ProcessCallback.RequestStop, [actionData]);
		}
		else if (this.hasHandler(ProcessCallback.RequestStop))
		{
			proceedAction = false;
			this.callHandler(ProcessCallback.RequestStop, [actionData]);
		}

		EventEmitter.emit(ProcessEvent.BeforeRequest, new BaseEvent({data: {process: this, actionData: actionData}}));

		this.ajaxPromise = null;

		if (proceedAction)
		{
			let params = {
				data: actionData,
				method: this.method,
				onrequeststart: this._onRequestStart.bind(this)
			};
			if (this.endpointType === EndpointType.Controller)
			{
				this.setController(this.controllerDefault);
				this.ajaxPromise =
					BX.ajax.runAction(this.controller + '.cancel', params)
						.then(
							this._onRequestSuccess.bind(this),
							this._onRequestFailure.bind(this)
						);
			}
			else if (this.endpointType === EndpointType.Component)
			{
				params.data.mode = this.componentMode;
				if ('signedParameters' in params.data)
				{
					params.signedParameters = params.data.signedParameters;
					delete params.data.signedParameters;
				}
				this.ajaxPromise =
					BX.ajax.runComponentAction(this.component, 'cancel', params)
						.then(
							this._onRequestSuccess.bind(this),
							this._onRequestFailure.bind(this)
						);
			}
		}

		return this.ajaxPromise;
	}

	finalizeRequest(): ?Promise
	{
		if (this.state === ProcessState.canceling)
		{
			return this.ajaxPromise;
		}

		let actionData = BX.clone(this.params);

		let proceedAction = true;
		if (this.hasActionHandler(ProcessCallback.RequestFinalize))
		{
			proceedAction = false;
			this.callActionHandler(ProcessCallback.RequestFinalize, [actionData]);
		}
		else if (this.hasHandler(ProcessCallback.RequestFinalize))
		{
			proceedAction = false;
			this.callHandler(ProcessCallback.RequestFinalize, [actionData]);
		}

		EventEmitter.emit(ProcessEvent.BeforeRequest, new BaseEvent({data: {process: this, actionData: actionData}}));

		this.ajaxPromise = null;

		if (proceedAction)
		{
			let params = {
				data: actionData,
				method: this.method,
				onrequeststart: this._onRequestStart.bind(this)
			};
			if (this.endpointType === EndpointType.Controller)
			{
				this.setController(this.controllerDefault);
				this.ajaxPromise = BX.ajax.runAction(this.controller + '.finalize', params);
			}
			else if (this.endpointType === EndpointType.Component)
			{
				params.data.mode = this.componentMode;
				if ('signedParameters' in params.data)
				{
					params.signedParameters = params.data.signedParameters;
					delete params.data.signedParameters;
				}
				this.ajaxPromise = BX.ajax.runComponentAction(this.component, 'finalize', params);
			}
		}

		return this.ajaxPromise;
	}

	/**
	 * @private
	 */
	_refreshHash()
	{
		this.hash = this.id + Date.now();
		this.setParam("PROCESS_TOKEN", this.hash);
		return this;
	}

	/**
	 * @private
	 */
	_onRequestSuccess(response: ControllerResponse)
	{
		this.isRequestRunning = false;
		this.xhr = null;
		this.ajaxPromise = null;

		if (!response)
		{
			this.getDialog().setError(this.getMessage('RequestError'));
			this.setState(ProcessState.error);
			return;
		}

		if (Type.isArrayFilled(response.errors))
		{
			const errors = response.errors.slice(-10);
			let errMessages = [];
			errors.forEach(err => errMessages.push(err.message));

			this.getDialog().setErrors(errMessages, true);
			this.setState(ProcessState.error);
			return;
		}

		this.networkErrorCount = 0;

		const result = response.data;

		const status = Type.isStringFilled(result.STATUS) ? result.STATUS : "";
		let summary = "";
		if (Type.isStringFilled(result.SUMMARY))
		{
			summary = result.SUMMARY;
		}
		else if (Type.isStringFilled(result.SUMMARY_HTML))
		{
			summary = result.SUMMARY_HTML;
		}
		const processedItems = Type.isNumber(result.PROCESSED_ITEMS) ? result.PROCESSED_ITEMS : 0;
		const totalItems = Type.isNumber(result.TOTAL_ITEMS) ? result.TOTAL_ITEMS : 0;
		let finalize = !!result.FINALIZE;

		if (this.hasActionHandler(ProcessCallback.StepCompleted))
		{
			this.callActionHandler(ProcessCallback.StepCompleted, [status, result]);
		}

		if (Type.isStringFilled(result.WARNING))
		{
			this.getDialog().setWarning(result.WARNING);
		}

		if (status === ProcessResultStatus.progress || status === ProcessResultStatus.completed)
		{
			if (totalItems > 0)
			{
				if (this.queue[this.currentStep].progressBarTitle)
				{
					this.getDialog()
						.setProgressBar(totalItems, processedItems, this.queue[this.currentStep].progressBarTitle);
				}
				else
				{
					this.getDialog()
						.setProgressBar(totalItems, processedItems);
				}
			}
			else
			{
				this.getDialog().hideProgressBar();
			}
		}

		if (status === ProcessResultStatus.progress)
		{
			if (summary !== "")
			{
				this.getDialog().setSummary(summary, true);
			}

			if (this.state === ProcessState.canceling)
			{
				this.setState(ProcessState.stopped);
			}
			else
			{
				if (this.endpointType === EndpointType.Controller)
				{
					const nextController = Type.isStringFilled(result.NEXT_CONTROLLER) ? result.NEXT_CONTROLLER : "";
					if (nextController !== "")
					{
						this.setController(nextController);
					}
					else if (Type.isStringFilled(this.queue[this.currentStep].controller))
					{
						this.setController(this.queue[this.currentStep].controller);
					}
					else
					{
						this.setController(this.controllerDefault);
					}
				}

				const nextAction = Type.isStringFilled(result.NEXT_ACTION) ? result.NEXT_ACTION : "";
				if (nextAction !== "")
				{
					this.setAction(nextAction);
				}

				setTimeout(
					BX.delegate(this.startRequest, this),
					100
				);
			}
			return;
		}

		if (this.state === ProcessState.canceling)
		{
			this.getDialog().setSummary(this.getMessage("RequestCanceled"));
			this.setState(ProcessState.completed);
		}
		else if (status === ProcessResultStatus.completed)
		{
			if (this.getQueueLength() > 0 && this.currentStep + 1 < this.getQueueLength())
			{
				// next
				this.currentStep ++;

				if (this.endpointType === EndpointType.Controller)
				{
					if (Type.isStringFilled(this.queue[this.currentStep].controller))
					{
						this.setController(this.queue[this.currentStep].controller);
					}
					else
					{
						this.setController(this.controllerDefault);
					}
				}

				if (!Type.isStringFilled(this.queue[this.currentStep].action))
				{
					throw new Error("BX.UI.StepProcessing: Could not find controller action at the queue position.");
				}

				if ('finalize' in this.queue[this.currentStep])
				{
					finalize = true;
					this.setAction(this.queue[this.currentStep].action);
				}
				else
				{
					this.setAction(this.queue[this.currentStep].action);

					this.getDialog().setSummary(this.queue[this.currentStep].title);

					setTimeout(
						BX.delegate(this.startRequest, this),
						100
					);
					return;
				}
			}

			if (summary !== "")
			{
				this.getDialog().setSummary(summary, true);
			}
			else
			{
				this.getDialog().setSummary(this.getMessage("RequestCompleted"));
			}

			if (Type.isStringFilled(result.DOWNLOAD_LINK))
			{
				if (Type.isStringFilled(result.DOWNLOAD_LINK_NAME))
				{
					this.getDialog().setMessage('downloadButton', result.DOWNLOAD_LINK_NAME);
				}
				if (Type.isStringFilled(result.CLEAR_LINK_NAME))
				{
					this.getDialog().setMessage('clearButton', result.CLEAR_LINK_NAME);
				}
				this.getDialog().setDownloadButtons(
					result.DOWNLOAD_LINK,
					result.FILE_NAME,
					BX.delegate(function(){
						this.getDialog().resetButtons({stop: true, close: true});
						this.callAction('clear'); //.then
						setTimeout(BX.delegate(function(){
								this.getDialog().resetButtons({close: true});
							}, this), 1000);
					}, this)
				);
			}

			this.setState(ProcessState.completed, result);

			if (finalize)
			{
				setTimeout(
					BX.delegate(this.finalizeRequest, this),
					100
				);
			}
		}
		else
		{
			this.getDialog().setSummary("").setError(this.getMessage("RequestError"));
			this.setState(ProcessState.error);
		}
	}

	/**
	 * @private
	 */
	_onRequestFailure (response: ControllerResponse)
	{
		/*
		// check if it's manual aborting
		if (this.state === ProcessState.canceling)
		{
			return;
		}
		*/
		this.isRequestRunning = false;
		this.ajaxPromise = null;

		// check non auth
		if (
			Type.isPlainObject(response) &&
			('data' in response) && Type.isPlainObject(response.data) &&
			('ajaxRejectData' in response.data) && Type.isPlainObject(response.data.ajaxRejectData) &&
			('reason' in response.data.ajaxRejectData) && (response.data.ajaxRejectData.reason === 'status') &&
			('data' in response.data.ajaxRejectData) && (response.data.ajaxRejectData.data === 401)
		)
		{
			this.getDialog().setError(this.getMessage('AuthError'));
		}
		// check errors
		else if (
			Type.isPlainObject(response) &&
			('errors' in response) &&
			Type.isArrayFilled(response.errors)
		)
		{
			let abortingState = false;
			let networkError = false;
			response.errors.forEach(err => {
				if (err.code === 'NETWORK_ERROR')
				{
					if (this.state === ProcessState.canceling)
					{
						abortingState = true;
					}
					else
					{
						networkError = true;
					}
				}
			});

			// ignoring error of manual aborting
			if (abortingState)
			{
				return;
			}

			if (networkError)
			{
				this.networkErrorCount ++;
				// Let's give it more chance to complete
				if (this.networkErrorCount <= 2)
				{
					setTimeout(
						BX.delegate(this.startRequest, this),
						15000
					);
					return;
				}
			}

			const errors = response.errors.slice(-10);
			let errMessages = [];
			errors.forEach(err => {
				if (err.code === 'NETWORK_ERROR')
				{
					errMessages.push(this.getMessage('RequestError'))
				}
				else
				{
					errMessages.push(err.message)
				}
			});

			this.getDialog().setErrors(errMessages, true);
		}
		else
		{
			this.getDialog().setError(this.getMessage('RequestError'));
		}

		this.xhr = null;
		this.currentStep = -1;

		this.setState(ProcessState.error);
	}

	//endregion

	//region Connection

	/**
	 * @private
	 */
	_closeConnection()
	{
		if (this.xhr instanceof XMLHttpRequest)
		{
			try
			{
				this.xhr.abort();
				this.xhr = null;
			}
			catch (e){}
		}
	}
	/**
	 * @private
	 */
	_onRequestStart(xhr: XMLHttpRequest)
	{
		this.xhr = xhr;
	}

	//endregion

	//region Set & Get

	setId(id: string)
	{
		this.id = id;
		return this;
	}
	getId(): string
	{
		return this.id;
	}

	//region Queue actions

	setQueue(queue: Array<QueueAction>)
	{
		queue.forEach((action: QueueAction) => this.addQueueAction(action));
		return this;
	}
	addQueueAction(action: QueueAction)
	{
		this.queue.push(action);
		return this;
	}
	getQueueLength(): number
	{
		return this.queue.length;
	}

	//endregion

	//region Process options

	setOption(name: $Keys<ProcessOptions>, value: any)
	{
		this.options[name] = value;
		return this;
	}
	getOption(name: $Keys<ProcessOptions>, defaultValue?: any = null): any
	{
		return this.options.hasOwnProperty(name) ? this.options[name] : defaultValue;
	}

	//endregion

	//region Initial fields

	setOptionsFields(optionsFields: {[id: string]: OptionsField})
	{
		Object.keys(optionsFields).forEach(id => this.addOptionsField(id, optionsFields[id]));
		return this;
	}
	addOptionsField(id: string, field: OptionsField)
	{
		this.optionsFields[id] = field;
		return this;
	}
	storeOptionFieldValues(values: {[name: string]: any})
	{
		if ('sessionStorage' in window)
		{
			let valuesToStore = {};
			Object.keys(this.optionsFields).forEach((name: string) => {
				let field: OptionsField = this.optionsFields[name];
				switch (field.type)
				{
					case 'checkbox':
					case 'select':
					case 'radio':
						if (field.name in values)
						{
							valuesToStore[field.name] = values[field.name];
						}
						break;
				}
			});
			window.sessionStorage.setItem('bx.' + this.getId(), JSON.stringify(valuesToStore));
		}
		return this;
	}
	restoreOptionFieldValues()
	{
		let values = {};
		if ('sessionStorage' in window)
		{
			values = JSON.parse(window.sessionStorage.getItem('bx.' + this.getId()));
			if (!Type.isPlainObject(values))
			{
				values = {};
			}
		}
		return values;
	}

	//endregion

	//region Request parameters

	setParams(params: {[name: string]: any})
	{
		this.params = {};
		Object.keys(params).forEach(name => this.setParam(name, params[name]));
		return this;
	}
	getParams(): {[string]:any}
	{
		return this.params;
	}
	setParam(key: string, value: any)
	{
		this.params[key] = value;
		return this;
	}
	getParam(key: string): any | null
	{
		return this.params[key] ? this.params[key] : null;
	}

	//endregion

	//region Process state

	setState(state: $Values<ProcessState>, result?: ProcessResult = {})
	{
		if (this.state === state)
		{
			return this;
		}

		this.state = state;
		if (state === ProcessState.intermediate || state === ProcessState.stopped)
		{
			this.getDialog()
				.lockButton('start', false)
				.lockButton('stop', true)
				.showButton('close', true);
		}
		else if (state === ProcessState.running)
		{
			this.getDialog()
				.lockButton('start', true, true)
				.lockButton('stop', false)
				.showButton('close', false);
		}
		else if (state === ProcessState.canceling)
		{
			this.getDialog()
				.lockButton('start', true)
				.lockButton('stop', true, true)
				.showButton('close', false)
				.hideProgressBar();
		}
		else if (state === ProcessState.error)
		{
			this.getDialog()
				.lockButton('start', true)
				.lockButton('stop', true)
				.showButton('close', true);
		}
		else if (state === ProcessState.completed)
		{
			this.getDialog()
				.lockButton('start', true)
				.lockButton('stop', true)
				.showButton('close', true)
				.hideProgressBar();
		}

		if (this.hasActionHandler(ProcessCallback.StateChanged))
		{
			this.callActionHandler(ProcessCallback.StateChanged, [state, result]);
		}
		else if (this.hasHandler(ProcessCallback.StateChanged))
		{
			this.callHandler(ProcessCallback.StateChanged, [state, result]);
		}

		EventEmitter.emit(ProcessEvent.StateChanged, new BaseEvent({data: {state: state, result: result}}));

		return this;
	}
	getState(): $Values<ProcessState>
	{
		return this.state;
	}

	//endregion

	//region Controller

	setController(controller: string)
	{
		this.controller = controller;
		return this;
	}
	getController(): string
	{
		return this.controller;
	}

	setComponent(component: string, componentMode: 'class'|'ajax' = 'class')
	{
		this.component = component;
		this.componentMode = componentMode;
		return this;
	}
	getComponent(): string
	{
		return this.component;
	}

	setAction(action: string)
	{
		this.action = action;
		return this;
	}
	getAction(): string
	{
		return this.action;
	}
	callAction(action: string): ?Promise
	{
		this.setAction(action)._refreshHash();
		return this.startRequest();
	}

	//endregion

	//region Event handlers

	setHandlers(handlers: {[$Keys<ProcessCallback>]: any => {}})
	{
		Object.keys(handlers).forEach(type => this.setHandler(type, handlers[type]));
		return this;
	}
	setHandler(type: $Keys<ProcessCallback>, handler: any => {})
	{
		if (Type.isFunction(handler))
		{
			this.handlers[type] = handler;
		}
		return this;
	}
	hasHandler(type: $Keys<ProcessCallback>)
	{
		return Type.isFunction(this.handlers[type]);
	}
	callHandler(type: $Keys<ProcessCallback>, args: any)
	{
		if (this.hasHandler(type))
		{
			this.handlers[type].apply(this, args);
		}
	}
	hasActionHandler(type: $Keys<ProcessCallback>)
	{
		if (this.queue[this.currentStep])
		{
			if ('handlers' in this.queue[this.currentStep])
			{
				return Type.isFunction(this.queue[this.currentStep].handlers[type]);
			}
		}
		return false;
	}
	callActionHandler(type: $Keys<ProcessCallback>, args: any)
	{
		if (this.hasActionHandler(type))
		{
			this.queue[this.currentStep].handlers[type].apply(this, args);
		}
	}

	//endregion

	//region lang messages
	setMessages(messages: {[string]: string})
	{
		Object.keys(messages).forEach((id) => this.setMessage(id, messages[id]));
		return this;
	}
	setMessage(id: string, text: string)
	{
		this.messages.set(id, text);
		return this;
	}
	getMessage(id: string, placeholders?: {[string]: string} = null): string
	{
		let phrase = this.messages.has(id) ? this.messages.get(id) : '';
		if (Type.isStringFilled(phrase) && Type.isPlainObject(placeholders))
		{
			Object.keys(placeholders).forEach((placeholder: string) => {
				phrase = phrase.replace('#'+placeholder+'#', placeholders[placeholder]);
			});
		}
		return phrase;
	}

	//endregion
	//endregion

	//region Dialog

	getDialog(): Dialog
	{
		if (!this.dialog)
		{
			this.dialog = new Dialog({
				id: this.id,
				optionsFields: this.getOption('optionsFields', {}),
				minWidth: Number.parseInt(this.getOption('dialogMinWidth', 500)),
				maxWidth: Number.parseInt(this.getOption('dialogMaxWidth', 1000)),
				optionsFieldsValue: this.restoreOptionFieldValues(),
				messages: {
					title: this.getMessage('DialogTitle'),
					summary: this.getMessage('DialogSummary'),
					startButton: this.getMessage('DialogStartButton'),
					stopButton: this.getMessage('DialogStopButton'),
					closeButton: this.getMessage('DialogCloseButton'),
					downloadButton: this.getMessage('DialogExportDownloadButton'),
					clearButton: this.getMessage('DialogExportClearButton')
				},
				showButtons: this.getOption('showButtons'),
				handlers: {
					start: BX.delegate(this.start, this),
					stop: BX.delegate(this.stop, this),
					dialogShown: (typeof(this.handlers.dialogShown) == 'function' ? this.handlers.dialogShown : null),
					dialogClosed: (typeof(this.handlers.dialogClosed) == 'function' ? this.handlers.dialogClosed : null)
				}
			});
		}

		return this.dialog;
	}

	showDialog ()
	{
		this.getDialog()
			.setSetting('optionsFieldsValue', this.restoreOptionFieldValues())
			.resetButtons(this.getOption('optionsFields'))
			.show();

		if (!this.isRequestRunning)
		{
			this.setState(ProcessState.intermediate);
		}

		return this;
	}

	closeDialog ()
	{
		if (this.isRequestRunning)
		{
			this.stop();
		}
		this.getDialog().close();

		return this;
	}

	//endregion
}

Anon7 - 2022
AnonSec Team