%PDF- %PDF- 403WebShell
403Webshell
Server IP : 37.220.80.31  /  Your IP : 13.58.201.75
Web Server : Apache/2.4.52 (Ubuntu)
System : Linux 3051455-guretool.twc1.net 5.15.0-107-generic #117-Ubuntu SMP Fri Apr 26 12:26:49 UTC 2024 x86_64
User : www-root ( 1010)
PHP Version : 7.4.33
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /var/www/www-root/data/www/dev.artlot24.ru/bitrix/js/main/core/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/www-root/data/www/dev.artlot24.ru/bitrix/js/main/core/core_fileinput.js
;(function() {
window['BXDEBUG']=true;
var BX = window.BX, repo = {}, thumbSize = 200;
BX.namespace("BX.UI");
if (BX["UI"]["FileInput"])
	return;
BX["UI"]["FileInput"] = function(id, uploadParams, elementParams, values, template)
{
	this.id = id;
	this.inited = false;
	this.uploadParams = (uploadParams || {});
	this.uploadParams["urlUpload"] = '/bitrix/tools/upload.php?lang=' + BX.message("LANGUAGE_ID");
	this.uploadParams["urlCloud"] = '/bitrix/admin/clouds_file_search.php?lang=' + BX.message("LANGUAGE_ID") + '&n=';
	this.uploadParams["maxCount"] = parseInt(this.uploadParams["maxCount"]) || 0;
	this.onUploadDoneCounter = parseInt(this.uploadParams["maxIndex"]);
	if (this.onUploadDoneCounter > 0)
		this.onUploadDoneCounter++;
	this.template = template;
	this.elementParams = elementParams;
	this.menu = [];
	this.agent = null;
	this.container = null;
	this.frameFlags = {
		hasNew: false,
		active: false,
		preparing: false,
		ready: false
	};
	this.framedItems = null;
	this.clickCloudPointBound = false;
	this.frameFilesWaitPopup = null;

	BX.ready(BX.proxy(function(){ this.init(values); }, this));
};
BX["UI"].FileInput.prototype = {
	init : function(values)
	{
		this.container = BX(this.id + '_block');
		if (!this.container)
		{
			setTimeout(BX.proxy(function(){ this.init(values); }, this), 1000);
			return;
		}
		if (repo[this.id])
		{
			if (repo[this.id].container === this.container)
				return;
			repo[this.id].destruct();
		}

		if (!this.container["fileInputIsAppended"])
		{
			this.container["fileInputIsAppended"] = 0;
			this.container.appendChild(BX.create("INPUT", {
				attrs : {
					className : "adm-fileinput-drag-area-input",
					type : "file",
					id : this.id + '_input',
					multiple : (this.uploadParams['maxCount'] !== 1),
					"data-fileinput" : "Y"
				}
			}));
		}

		if (!BX(this.id + '_input'))
		{
			this.container["fileInputIsAppended"]++;
			if (this.container["fileInputIsAppended"] < 100)
				setTimeout(BX.proxy(function(){ this.init(values); }, this), 500);
			return;
		}

		this.initFrameCounters();
		this.agent = BX.Uploader.getInstance({
			id : this.id,
			streams : 1,
			uploadMaxFilesize : (this.uploadParams["uploadType"] === "file" ? BX.message("phpUploadMaxFilesize") : this.uploadParams['maxSize']),
			allowUpload : this.uploadParams['allowUpload'],
			allowUploadExt : this.uploadParams['allowUploadExt'],
			uploadFormData : "N",
			uploadMethod : "immediate",
			uploadFileUrl : this.uploadParams["urlUpload"],
			showImage : true,
			sortItems : (this.uploadParams["allowSort"] === "Y"),
			deleteFileOnServer : false,
			pasteFileHashInForm : false,
			input : BX(this.id + '_input'),
			dropZone : BX(this.id + '_block'),
			placeHolder : BX(this.id + '_container'),
			thumb : {
				tagName : "DIV",
				className : "adm-fileinput-item-wrapper"
			},
			fields : {
				thumb : {
					tagName : "DIV",
					template : this.template
				},
				preview : {
					params : { width : thumbSize, height : thumbSize },
					events : { }
				}
			}
		});
		repo[this.id] = this;
		this.fileEvents = {
			onFileIsAttached : this.onFileIsAttached.bind(this),
			onFileIsAppended : this.onFileIsAppended.bind(this),
			onFileIsBound : this.onFileIsBound.bind(this),
			onFileIsReadyToFrame : this.onFileIsReadyToFrame.bind(this),
			onUploadProgress : this.onUploadProgress.bind(this),
			onUploadDone : this.onUploadDone.bind(this),
			onUploadError : this.onUploadError.bind(this),
			onUploadRestore : this.onUploadRestore.bind(this)
		};

		this.agentEvents = {
			onAttachFiles : this.onAttachFiles.bind(this),
			onQueueIsChanged : this.onQueueIsChanged.bind(this),
			onFileIsCreated : this.onFileIsCreated.bind(this),
			onFileIsInited : this.onFileIsInited.bind(this),
			onFileIsReadyToFrame : this.onFileIsReadyToFrame.bind(this),
			onFilesPropsAreModified : this.onFilesPropsAreModified.bind(this),
			onFileIsFramed : this.onFileIsFramed.bind(this),
			onFilesAreFramed : this.onFilesAreFramed.bind(this),
			onBxDragStart : this.onFileIsDragged.bind(this),
			onError : this.onError.bind(this)
		};
		if (this.uploadParams['upload'])
		{
			var signature = this.uploadParams['upload'];
			this.agentEvents["onPackageIsInitialized"] = function(pack, raw)
			{
				pack.post.data['signature'] = signature;
				pack.post.size += ('signature' + signature).length;
				this.onUploadStart(raw);
			}.bind(this);
		}
		for (ii in this.agentEvents)
		{
			if (this.agentEvents.hasOwnProperty(ii))
			{
				BX.addCustomEvent(this.agent, ii, this.agentEvents[ii]);
			}
		}


/*		if (false && this.uploadParams["uploadType"] == "file")
		{
			var isFile = function(file)
			{
				var res = Object.prototype.toString.call(file);
				return (res == '[object File]' || res == '[object Blob]');
			};
			BX.addCustomEvent(this.agent, "onFileIsAfterCreated", BX.proxy(function(item, being, itemStatus) {
				if (isFile(item.file))
				{
					itemStatus.status = false;
				}
			}, this));

			if (this.agent.form)
			{
				this.__onsubmit = BX.delegate(function(e)
				{
					try{
					BX.unbind(this.agent.form, "submit", this.__onsubmit);
					var item, value;
					while((item = this.agent.getItems().getFirst()) && !!item &&
						((value = BX(item.id + 'Value')) && value) &&
						(isFile(item.file)))
					{
						this.agent.form.delete(value.name);
						this.agent.form.append(value.name, item.file);
						this.agent.getItems().removeItem(item.id);
					}
					}catch (e){
					}
					return BX.PreventDefault(e);
				}, this);
				BX.bind(this.agent.form, "submit", this.__onsubmit);
			}
		}

*/		if (values.length > 0)
		{
			var ar1 = [], ar2 = [];
			this.values = {};
			for (var ii = 0; ii < values.length; ii++)
			{
				this.values[values[ii]['id']] = values[ii];
				ar1.push(values[ii]);
				ar2.push(BX(values[ii]['id'] + 'Block'));
			}
			this.agent.onAttach(ar1, ar2, false);
		}
		this.initMenu(BX(this.id + '_add'), this.uploadParams);
		this.checkUploadControl();
		// Adjust all styles
		BX[this.elementParams['delete'] !== true ? "addClass" : "removeClass"](this.container, "adm-fileinput-non-delete");
		this.framedItems = new BX.UploaderUtils.Hash();
		if (BX(this.id + 'ThumbModePreview'))
		{
			BX.bind(BX(this.id + 'ThumbModePreview'), 'click', BX.delegate(function(e){
				BX.removeClass(BX(this.id + '_mode'), 'mode-file');
				BX.removeClass(BX(this.id + '_block'), 'mode-file');
				BX.addClass(BX(this.id + '_mode'), 'mode-pict');
				BX.addClass(BX(this.id + '_block'), 'mode-pict');
				this.saveOptions('mode', 'mode-pict');
				return BX.PreventDefault(e);
			}, this));
		}
		if (BX(this.id + 'ThumbModeNonPreview'))
		{
			BX.bind(BX(this.id + 'ThumbModeNonPreview'), 'click', BX.delegate(function(e){
				BX.removeClass(BX(this.id + '_mode'), 'mode-pict');
				BX.removeClass(BX(this.id + '_block'), 'mode-pict');
				BX.addClass(BX(this.id + '_mode'), 'mode-file');
				BX.addClass(BX(this.id + '_block'), 'mode-file');
				this.saveOptions('mode', 'mode-file');
				return BX.PreventDefault(e);
			}, this));
		}
		this.inited = true;
	},
	destruct : function() {
		var ii;
		for (ii in this.agentEvents)
		{
			if (this.agentEvents.hasOwnProperty(ii))
			{
				BX.removeCustomEvent(this.agent, ii, this.agentEvents[ii]);
			}
		}
		this.agent.destruct();
		this.deinitMenu(BX(this.id + '_add'));

		BX.remove(BX(this.id + '_input'));

		delete this.noticeNode;
		delete this.nativeNoticeMessage;
		delete this.container;
		delete this.framedItems;

		BX.unbindAll(BX(this.id + 'ThumbModePreview'));
		BX.unbindAll(BX(this.id + 'ThumbModeNonPreview'));

		this.agent = null;
		delete this.agent;

		delete repo[this.id];
	},
	checkUploadControl : function() {
		// drag&drop area
		if (!this['noticeNode'])
		{
			this.noticeNode = BX(this.id + 'Notice');
			this.nativeNoticeMessage = BX(this.id + 'Notice').innerHTML;
		}
		var possibleClasses = "adm-fileinput-drag-area " +
			"adm-fileinput-drag-area-error " +
			"adm-fileinput-drag-notification-count " +
			"adm-fileinput-drag-area-error-permission " +
			"adm-fileinput-drag-area-error-count";
		BX.removeClass(this.container, possibleClasses);
		if (!this.uploadParams['upload'])
		{
			this.noticeNode.innerHTML = BX.message("JS_CORE_FI_UPLOAD_DENIED");
			BX.addClass(this.container, "adm-fileinput-drag-area-error adm-fileinput-drag-area-error-permission");
		}
		else if (this.uploadParams["maxCount"] <= 0 || this.uploadParams["maxCount"] > this.agent.getItems().length)
		{
			this.noticeNode.innerHTML = this.nativeNoticeMessage;
			BX.addClass(this.container, "adm-fileinput-drag-area");
		}
		else if (this.uploadParams["maxCount"] === this.agent.getItems().length)
		{
			this.noticeNode.innerHTML = BX.message("JS_CORE_FI_TOO_MANY_FILES2");
			BX.addClass(this.container, "adm-fileinput-drag-area adm-fileinput-drag-notification-count");
		}
		else
		{
			this.noticeNode.innerHTML = BX.message("JS_CORE_FI_TOO_MANY_FILES3");
			BX.addClass(this.container, "adm-fileinput-drag-area-error adm-fileinput-drag-area-error-count");
		}
	},
	saveOptions : function(name, value)
	{
		BX.userOptions.save('main', 'fileinput', name, value);
	},
	initMenu : function(node, uploadParams) {
		node = BX(node);
		if (!node || node.OPENER)
			return;
		this.initMenuCounter = (this.initMenuCounter || 0) + 1;
		var inputID, menu = [];
		if (uploadParams['upload'])
		{
			inputID = this.id + '_menu' + this.initMenuCounter;
			menu.push({
				ID : "upload",
				HTML : BX.message("JS_CORE_FILE_UPLOAD") + '<input type="file" id="' + inputID + '"' + ' class="adm-fileinput-area-input" />',
				GLOBAL_ICON: "adm-menu-upload-pc"});
		}
		if (uploadParams['medialib'] || uploadParams['fileDialog'])
		{
			menu.push({
				TEXT: BX.message('JS_CORE_FILE_INSERT_PATH'),
				ONCLICK: BX.proxy(this.handlerFilePath, this),
				GLOBAL_ICON: "adm-menu-download"
			});
		}
		if (menu.length > 0)
		{
			menu.push({SEPARATOR : true});
		}
		if (uploadParams['medialib'])
		{
			menu.push({
				TEXT : BX.message("JS_CORE_FILE_MEDIALIB"),
				GLOBAL_ICON : "adm-menu-upload-medialib",
				ONCLICK : uploadParams['medialib']['click'] + "()"});
			window[uploadParams['medialib']['handler']] = this.handlerMedialib.bind(this);
		}
		if (uploadParams['fileDialog'])
		{
			menu.push({TEXT : BX.message("JS_CORE_FILE_SITE"), GLOBAL_ICON : "adm-menu-upload-site", ONCLICK : uploadParams['fileDialog']['click'] + "()"});
			window[uploadParams['fileDialog']['handler']] = this.handlerFileDialog.bind(this);
		}
		if (uploadParams['cloud'])
		{
			this.__cloudClick = this.clickCloudPoint.bind(this);
			menu.push({TEXT : BX.message("JS_CORE_FILE_CLOUD"), GLOBAL_ICON : "adm-menu-upload-cloud", ONCLICK : this.__cloudClick});
		}

		if (uploadParams['menu'] && BX.type.isArray(uploadParams['menu']))
		{
			menu.push({SEPARATOR : true});
			for (var ii = 0; ii <= uploadParams['menu'].length; ii++)
			{
				menu.push(uploadParams['menu']);
			}
			menu.push({SEPARATOR : true});
		}
		else if (uploadParams['medialib'] || uploadParams['fileDialog'] || uploadParams['cloud'])
		{
			menu.push({SEPARATOR : true});
		}
		if (menu.length > 0 && this.agent.dialogName == "BX.Uploader")
		{
			menu.push({
				GLOBAL_ICON : "adm-menu-crop",
				TEXT : BX.message("JS_CORE_FI_FRAME_Y"),
				ONCLICK : this.frameFiles.bind(this),
				CHECKED : (this.uploadParams["frameFiles"] == "Y")
			});
		}
		if (this.elementParams["edit"] && this.elementParams["description"] !== false)
		{
			menu.push({
				TEXT : BX.message("JS_CORE_FI_PIN_DESCRIPTION"),
				ONCLICK : this.pinDescription.bind(this),
				CHECKED : (this.uploadParams["pinDescription"] == "Y")
			});
		}
		if (this.elementParams["delete"])
		{
			menu.push({
				TEXT : BX.message("JS_CORE_FI_CLEAR"),
				ONCLICK : this.deleteFiles.bind(this),
				GLOBAL_ICON : "adm-menu-delete"
			});
		}
		if (menu.length > 0)
		{
			node.OPENER = new BX.COpener({
				DIV: node,
				TYPE: 'click',
				MENU: menu,
				ACTIVE_CLASS: 'adm-btn-active'
			});
			if (inputID)
			{
				node.__onOpenerMenuOpen = BX.delegate(function()
				{
					BX.adjust(BX(inputID).parentNode, { style : { position : "relative" } } );
					this.agent.init(BX(inputID));
					BX.removeCustomEvent(node.OPENER, 'onOpenerMenuOpen', node.__onOpenerMenuOpen);
				}, this);
				BX.addCustomEvent(node.OPENER, 'onOpenerMenuOpen', node.__onOpenerMenuOpen);
			}
		}
	},
	deinitMenu : function(node) {
		node = BX(node);
		if (!node || !node.OPENER)
			return;
		BX.removeCustomEvent(node.OPENER, 'onOpenerMenuOpen', node.__onOpenerMenuOpen);
		BX.unbindAll(node.OPENER.DIV);
		delete node.OPENER.DIV;
		delete node.OPENER;
	},
	handlerFilePathPopup : null,
	handlerFilePath : function(data)
	{
		if (BX.type.isArray(data))
		{
			var result = [];

			for (var ii = 0; ii < data.length; ii++)
			{
				if (BX.type.isNotEmptyString(data[ii]))
				{
					result.push({tmp_url : BX.util.htmlspecialchars(data[ii]), real_url : decodeURIComponent(data[ii])});
				}
			}
			this.agent.onAttach(result);
		}
		else
		{
			this.handlerFilePathPopup = (this.handlerFilePathPopup ||
				new filePath(this.id + 'filePath', {
					onApply : this.handlerFilePath.bind(this)
				}, this.uploadParams['maxCount']));
			this.handlerFilePathPopup.show();
		}
	},
	handlerMedialib : function(data)
	{
		if (!BX.type.isArray(data))
			data = [data];
			var result = [];

		for (var ii = 0; ii < data.length; ii++)
		{
			if (data[ii])
			{
				result.push({
					name : BX.UploaderUtils.getFileNameOnly(data[ii]['src']),
					description : data[ii]['description'],
					type : data[ii]['type'] + '/medialib',
					size : data[ii]['file_size'],
					sizeFormatted : data[ii]['file_size'],
					tmp_url : data[ii]['src'],
					real_url : data[ii]['src']
				});
			}
		}
		this.agent.onAttach(result, result);
	},
	handlerFileDialog : function(name, path)
	{
		if (BX.type.isNotEmptyString(name) && BX.type.isNotEmptyString(path))
		{
			var file = {
				name : name,
				type : (BX.UploaderUtils.isImageExt((name || '').lastIndexOf('.') > 0 ? name.substr(name.lastIndexOf('.')+1) : '') ? 'image' : 'notimage') + '/filedialog',
				tmp_url : path + '/' + name,
				real_url : path + '/' + name
			};
			this.agent.onAttach([file], [file]);
		}
	},
	clickCloudPointPath : '/bitrix/admin/clouds_file_search.php?lang=' + BX.message("LANGUAGE_ID") + '&n=undefined',
	clickCloudPoint : function()
	{
		if (this.clickCloudPointBound === false)
		{
			BX.addCustomEvent("onCloudFileIsChosen", this.clickCloudPointChange.bind(this));
			this.clickCloudPointBound = true;
		}
		BX.util.popup(this.clickCloudPointPath, 710, 600);
	},
	clickCloudPointChange : function(path)
	{
		var name = BX.UploaderUtils.getFileNameOnly(path);
		if (BX.type.isNotEmptyString(path))
		{
			var file = {
				name : name,
				type : (BX.UploaderUtils.isImageExt((name || '').lastIndexOf('.') > 0 ? name.substr(name.lastIndexOf('.')+1) : '') ? 'image' : 'notimage') + '/cloudfile',
				tmp_url : path
			};
			this.agent.onAttach([file], [file]);
		}
	},
	frameFiles : function(activeId)
	{
		if (activeId && !BX.type.isNumber(activeId) && activeId["type"] == "click")
		{
			this.uploadParams["frameFiles"] = (this.uploadParams["frameFiles"] == "Y" ? "N" : "Y");
			this.saveOptions("frameFiles", this.uploadParams["frameFiles"]);
			if (this.uploadParams["frameFiles"] == "N")
				return false;
		}
		if (this.frameFlags.active === true &&
			(activeId || this.frameFlags.hasNew))
		{
			try
			{

				if(!this['__frameFiles'])
				{
					this['__frameFiles'] = BX.delegate(function()
					{
						BX.removeCustomEvent(this, "onFilesCanBeFramed", this.__frameFiles);

						if(this.frameFilesWaitPopup)
							this.frameFilesWaitPopup.close();

						this.framedItems = new BX.UploaderUtils.Hash();

						if(!this.frameFilesBound)
						{
							this.frameFilesBound = true;
							BX.addCustomEvent(frameMaster, "onDeleteItem", BX.delegate(function(item) {
								this.deleteFile(item);
							}, this));
						}
						this.frameFlags.hasNew = false;
						var p = BX.clone(this.uploadParams, true);
						p["description"] = this.elementParams["description"];
						frameMaster.start(this.agent, (activeId || this.counters.newItemId), p);

						this['__frameFiles'] = null;
						delete this['__frameFiles'];
					}, this);
				}

				BX.removeCustomEvent(this, "onFilesCanBeFramed", this['__frameFiles']);

				if (this.frameFlags.ready === true)
				{
					this.__frameFiles();
				}
				else
				{
					this.frameFilesWait();
					BX.addCustomEvent(this, "onFilesCanBeFramed", this.__frameFiles);
				}
			}
			catch(e)
			{
				console.log(e);
			}
		}
		return true;
	},
	pinDescription : function(e)
	{
		this.uploadParams["pinDescription"] = (this.uploadParams["pinDescription"] == "Y" ? "N" : "Y");
		this.saveOptions("pinDescription", this.uploadParams["pinDescription"]);

		if (this.uploadParams["pinDescription"] == "Y")
		{
			if (!BX.hasClass(BX(this.id + "_block"), "mode-with-description"))
				BX.addClass(BX(this.id + "_block"), "mode-with-description");
		}
		else
		{
			BX.removeClass(BX(this.id + "_block"), "mode-with-description");
		}
		return BX.PreventDefault(e);
	},
	frameFilesWait : function()
	{
		if (this.frameFilesWaitPopup == null)
		{
			this.frameFilesWaitPopup = BX.PopupWindowManager.create(
				'popup-frame-wait' + this.id,
				null,
				{
					autoHide : true,
					titleBar: BX.message("JS_CORE_LOADING"),
					contentColor : 'white',
					closeIcon : true,
					closeByEsc : true,
					content : '<span class="adm-photoeditor-popup-frame-wait"><span></span>' + BX.message("JS_CORE_FI_FRAME_IS_LOADING") + '</span>',
					overlay : {},
					events : {
						onPopupClose : BX.proxy(function() { BX.removeCustomEvent(this, "onFilesCanBeFramed", this.__frameFiles)}, this)
					},
					buttons : [
						new BX.PopupWindowButtonLink( {
							text : BX.message('JS_CORE_WINDOW_CANCEL'),
							className : "popup-window-button-link-cancel",
							events : { click : BX.delegate(function()
							{
								this.frameFilesWaitPopup.close();
							}, this) } } )
					]
				}
			);
		}
		this.frameFilesWaitPopup.show();
	},
	initFrameCounters : function()
	{
		this.counters = {
			images : {
				created : new BX.UploaderUtils.Hash(),
				ready : new BX.UploaderUtils.Hash()
			},
			uploaded : new BX.UploaderUtils.Hash(),
//			files : { created : [], ready : [] },
			newItemOrder : 0,
			newItemId : 0
		};
	},
	incrementFrameCounter : function(id, item)
	{
		if (item.dialogName == "BX.UploaderImage")
		{
			if (this.values && this.values[item.id])
			{
				this.counters.uploaded.setItem(id, item.dialogName);
			}
			else if (!this.counters.uploaded.hasItem(id) || this.counters.uploaded.getItem(id) !== item.dialogName)
			{
				if (this.frameFlags.hasNew !== true)
				{
					this.frameFlags.hasNew = true;
					this.counters.newItemOrder = this.counters.images.created.length;
					this.counters.newItemId = id;
				}
			}
			item.canvasIsReady = false;
			this.frameFlags.active = true;
			this.frameFlags.ready = false;
			this.counters.images.created.setItem(id, id);
		}
	},
	decrementFrameCounters : function(id, item)
	{
		if (item.dialogName == "BX.UploaderImage")
		{
			this.counters.images.created.removeItem(id);
			this.counters.images.ready.removeItem(id);
			//this.counters.uploaded.removeItem(id);
			this.frameFlags.active = (this.counters.images.created.length > 0);
		}
	},
	amountFrameCounter : function(id, item)
	{
		if (item.dialogName == "BX.UploaderImage")
		{
			item.canvasIsReady = true;
			if (this.counters.uploaded.hasItem(id))
			{
				BX.onCustomEvent(this.agent, "onFileIsReadyToFrame", [id, item]);
			}
		}
	},
	onFileIsReadyToFrame : function(id, item)
	{
		if (item.dialogName == "BX.UploaderImage" && this.counters.images.created.hasItem(id))
		{
			this.counters.images.ready.setItem(id, id);

			this.frameFlags.ready = (this.counters.images.created.length == this.counters.images.ready.length);

			if (this.frameFlags.ready)
			{
				BX.onCustomEvent(this, "onFilesCanBeFramed", [this]);
			}
		}
	},
	deleteFiles : function()
	{
		var items = this.agent.getItems(),
			item;

		while ((item = items.getFirst()) && item)
		{
			this.deleteFile(item, true);
		}
	},
	onQueueIsChanged : function()
	{
		if (this.inited === true && this.uploadParams['maxCount'] > 0)
		{
			this.checkUploadControl();
		}
	},
	onAttachFiles : function(files)
	{
		var error = false;
		if(files && this.inited === true && this.uploadParams['maxCount'] > 0)
		{
			if (this.uploadParams['maxCount'] == 1 && files.length > 0)
			{
				while (this.agent.getItems().length > 0)
					this.deleteFile(this.agent.getItems().getFirst(), true);
				while (files.length > 1)
					files.pop();
			}
			var acceptableL = this.uploadParams['maxCount'] - this.agent.getItems().length;
			acceptableL = (acceptableL > 0 ? acceptableL : 0);
			while (files.length > acceptableL)
			{
				files.pop();
				error = true;
			}
		}
		if (error)
		{
			this.onError(BX.message('JS_CORE_FI_TOO_MANY_FILES').replace('#amount#', BX.util.htmlspecialchars(this.uploadParams['maxCount'])), true);
		}
		return files;
	},
	onFileIsCreated : function(id, item)
	{
		if (this.inited === true)
			item.IND = this.onUploadDoneCounter++;

		if (item.file["preview_url"] && item.file["width"] > 0 && item.file["height"] > 0)
		{
			BX.addCustomEvent(item, "onFileCanvasIsLoaded", BX.proxy(function(/*id, it, ag, image*/) {
				item.file["tmp_url"] = item.file["~tmp_url"];
				delete item.file["~tmp_url"];
				item.file["width"] = item.file["~width"];
				item.file["height"] = item.file["~height"];
				delete item.file["~width"];
				delete item.file["~height"];
				item.canvasIsLoaded = true;
				this.replaceHint(item);
			}, this));
			item.file["~tmp_url"] = item.file["tmp_url"];
			item.file["tmp_url"] = item.file["preview_url"];
			item.file["~width"] = item.file["width"];
			item.file["~height"] = item.file["height"];
			delete item.file["preview_url"];
		}
		else if (item.dialogName == "BX.UploaderImage")
		{
			BX.addCustomEvent(item, "onFileCanvasIsLoaded", BX.proxy(function(/*id, it, ag, image*/) {
				item.canvasIsLoaded = true;
				this.replaceHint(item);
			}, this));
		}
		item.description = (BX.type.isNotEmptyString(item.file["description"]) ? item.file["description"] : "");
		this.incrementFrameCounter(id, item);
		for (var ii in this['fileEvents'])
		{
			if (this['fileEvents'].hasOwnProperty(ii))
			{
				if (this['fileEvents'][ii])
				{
					BX.addCustomEvent(item, ii, this.fileEvents[ii]);
				}
			}
		}
	},
	onFileIsInited : function(id, item)
	{
		this.amountFrameCounter(id, item);
	},
	onFileIsAppended : function(id, item)
	{
		this.bindFile(item);
	},
	onFileIsBound : function(id, item)
	{
		this.bindFile(item);
	},
	onFileIsAttached : function(id, item)
	{
		if (item.file["sizeFormatted"])
		{
			item.size = item.file["sizeFormatted"];
			delete item.file["sizeFormatted"];
		}
		this.bindFile(item);
		if (item.file['tmp_url'])
			this.onUploadDone(item, {file : {uploadId : item.file.tmp_url}});
	},
	onFilesPropsAreModified : function(id, item, props)
	{
		if (props)
		{
			item.description = props.description;
			if (BX(item.id + 'Description'))
				BX(item.id + 'Description').value = item.description;
		}
	},
	onFileIsFramed : function(id, item, canvas, file)
	{
		if (canvas.width != item.canvas.width || canvas.height != item.canvas.height)
		{
			BX.adjust(item.canvas, { props : { width : canvas.width, height : canvas.height} });
		}
		else
		{
			BX.adjust(item.canvas, { props : { width : item.canvas.width - 1} });
			BX.adjust(item.canvas, { props : { width : item.canvas.width + 1} });
		}
		item.canvas.getContext("2d").drawImage(canvas,
			0, 0, canvas.width, canvas.height,
			0, 0, item.canvas.width, item.canvas.height
		);
		item.file = file;
		this.framedItems.setItem(item.id, item);
	},
	onFilesAreFramed : function()
	{
		if (this.framedItems && this.framedItems.length > 0)
		{
			if (this.uploadParams["uploadType"] == "file")
			{

			}
			else
			{
				this.agent.restoreItems(this.framedItems, true, true);
				this.agent.submit();
			}
		}
	},
	onFileIsDragged : function(item, node)
	{
		if (BX.hasClass(BX(this.id + '_block'), 'mode-file'))
		{
			BX.addClass(node, 'mode-file');
		}
	},
	onUploadStart : function(raw)
	{
		if (raw.length > 0)
		{
			var node, item, ii;
			for (ii in raw["items"])
			{
				if (raw["items"].hasOwnProperty(ii))
				{
					item = raw["items"][ii];
					node = (this.agent.getItem(item.id) || {node: false}).node;
					if (node && !BX.hasClass(node, "adm-fileinput-item-uploading"))
						BX.addClass(node, "adm-fileinput-item-uploading");
				}
			}
		}
	},
	onUploadProgress : function(item, progress)
	{
		var node = this.agent.getItem(item.id).node;
		if (!node.hasAttribute("bx-progress-bound"))
		{
			node.setAttribute("bx-progress-bound", "Y");
			BX.addClass(node, "adm-fileinput-item-uploading");
		}
		progress = (progress < 5 ? 5 : (progress > 100 ? 100 : progress));
		progress = Math.ceil(progress);
		if(BX(item.id + 'Progress', true))
		{
			BX(item.id + 'Progress', true).style.width = progress + '%';
		}
	},
	onUploadDoneCounter : 0,
	replaceInput : function(item, data)
	{
		var node = this.agent.getItem(item.id).node,
			input_name = node["__replaceInputName"],
			id = item.id + 'Value',
			input = BX.findChild(node, {tagName : "INPUT", attr : {id : id}}, true),
			tmp,
			file = (data && data['file'] && data['file']['files'] && data['file']['files']['default'] ? data['file']['files']['default'] : false);

		if (!input_name)
			input_name = node["__replaceInputName"] = input.name;

		if (file)
		{
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name + '[name]', id : input.id, value : item.name }}), input);
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name + '[type]', value : file['type'] }}), input);
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name + '[tmp_name]', value : file['path'] }}), input);
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name + '[size]', value : file['size'] }}), input);
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name + '[error]', value : 0 }}), input);
		}
		else
		{
			input.parentNode.insertBefore(BX.create("INPUT", { attrs : { type : "hidden", name : input_name, id : input.id, value : data['file']['uploadId'] }}), input);
		}

		while (BX(input) && input.name.indexOf(input_name) === 0)
		{
			tmp = input.nextSibling;
			BX.remove(input);
			input = tmp;
		}

		if (this.uploadParams["maxCount"] <= 1)
		{
			var n = BX.findChild(this.container, {tagName : "INPUT", attr : {name : (input_name)}}, false);
			if (n)
			{
				BX.adjust(n, { attrs : { disabled : true }});
				var nDelName = input_name + '_del';
				if (input_name.indexOf('[') > 0)
					nDelName = input_name.substr(0, input_name.indexOf('[')) + '_del' + input_name.substr(input_name.indexOf('['));
				n = BX.findChild(this.container, {tagName : "INPUT", attr : {name : nDelName}}, false);
				if (n)
					BX.adjust(n, { attrs : { disabled : true } } );
			}
		}
	},
	onUploadDone : function(it, data)
	{
		var pointer = this.agent.getItem(it.id),
			item = pointer.item,
			node = pointer.node;

		if (item && BX(node))
		{
			var file = (data && data['file'] && data['file']['files'] && data['file']['files']['default'] ? data['file']['files']['default'] : false);

			this.counters.uploaded.setItem(item.id, item.dialogName);

			if (file && (file["wasChangedOnServer"] === true || (item.dialogName == "BX.UploaderImage") !== BX.UploaderUtils.isImage(file["name"], file["type"], file["size"])))
			{
				this.replaceItem(item, file, node);
			}
			else if (item.dialogName == "BX.UploaderImage")
			{
				this.amountFrameCounter(item.id, item);
			}

			this.replaceInput(item, data);
			if (node.firstChild && BX.hasClass(node.firstChild, "adm-fileinput-item-saved"))
				BX.removeClass(node.firstChild, "adm-fileinput-item-saved");

			node.removeAttribute("bx-progress-bound");
			BX.removeClass(node, "adm-fileinput-item-uploading");

			if (this.uploadParams["frameFiles"] == "Y")
				this.frameFiles();
		}
	},
	onUploadError : function(item, data)
	{
		var node = this.agent.getItem(item.id).node;
		node.removeAttribute("bx-progress-bound");
		BX.removeClass(node, "adm-fileinput-item-uploading adm-fileinput-item-image");
		BX.addClass(node, "adm-fileinput-item-error");
		if (data && data["error"])
		{
			node = BX(item.id + 'ErrorText');
			if (!node)
			{
				node = BX.create('SPAN', { attrs : { id : item.id + 'ErrorText' , className : 'container-doc-error' } } );
				BX(item.id + 'Name').parentNode.appendChild(node);
			}
			node.innerHTML = data["error"];
		}
	},
	onUploadRestore : function () {

	},
	onError : function(errorText, errorModal)
	{
		BX.addClass(this.agent.placeHolder, "adm-fileinput-drag-area-error");
		if (errorModal === true)
		{
			alert(errorText);
		}
		else
		{
			BX.debug(errorText);
		}
	},
	bindFile : function(item)
	{
		var id = item.id,
			node = this.agent.getItem(item.id).node;
		if (item.dialogName == "BX.UploaderImage")
		{
			BX.removeClass(node, "adm-fileinput-item-file");
			BX.addClass(node, "adm-fileinput-item-image");
		}
		else
		{
			BX.removeClass(node, "adm-fileinput-item-image");
			BX.addClass(node, "adm-fileinput-item-file");
		}
		if (node && !node.hasAttribute("bx-bound-editor"))
		{
			node.setAttribute("bx-bound-editor", "Y");
			if (this.agent.dialogName == "BX.UploaderSimple")
			{
				BX.bind(node, "dblclick", BX.delegate(function(e){
					var pointer = this.agent.getItem(item.id);
					if (pointer && pointer.item.dialogName == "BX.UploaderImage")
					{
						var url = (pointer.item.file['real_url'] || pointer.item.file['tmp_url']);
						if (url)
							BX.util.popup(url);
					}
					return BX.PreventDefault(e);
				}, this));
			}
			else if (this.elementParams["edit"])
			{
				BX.bind(node, "dblclick", BX.delegate(function(e){
					BX.PreventDefault(e);
					var pointer = this.agent.getItem(item.id);
					if (pointer && pointer.item.dialogName == "BX.UploaderImage")
					{
						this.frameFiles(item.id);
					}
				}, this));
			}
		}
		if (BX(id + 'Edit') && !BX(id + 'Edit').hasAttribute("bx-bound"))
		{
			BX(id + 'Edit').setAttribute("bx-bound", "Y");
			if (this.elementParams["edit"])
			{
				BX.bind(BX(id + 'Edit'), "click", BX.delegate(function(e){
					BX.PreventDefault(e);
					var pointer = this.agent.getItem(item.id);
					if (pointer && pointer.item.dialogName == "BX.UploaderImage")
					{
						this.frameFiles(item.id);
					}
				}, this));
			}
			else
			{
				BX.hide(BX(id + 'Edit'));
			}
		}
		if (BX(id + 'Del') && !BX(id + 'Del').hasAttribute("bx-bound"))
		{
			BX(id + 'Del').setAttribute("bx-bound", "Y");
			BX.bind(BX(id + 'Del'), "click", BX.delegate(function(e){
				BX.PreventDefault(e);
				this.deleteFile(item);
			}, this));
		}
		if (BX(id + 'Description') && !BX(id + 'Description').hasAttribute("bx-bound"))
		{
			BX(id + 'Description').setAttribute("bx-bound", "Y");
			BX.bind(BX(id + 'Description'), "click", BX.delegate(function(e){
				BX.defer_proxy(function(){
					BX.focus(BX(id + 'Description'));
				})();
				return BX.PreventDefault(e);
			}, this));
			BX.bind(BX(id + 'Description'), "blur", function(){ item.description = BX(id + 'Description').value; } );
		}
		this.replaceHint(item);
	},
	replaceHint : function(item)
	{
		var id = item.id,
			node = this.agent.getItem(item.id).node;
		if (node.hint)
			node.hint.Destroy();
		var hint = '<span class="adm-fileinput-drag-area-popup-title">' + BX.util.htmlspecialchars(item.name) + '</span>';
		if (item.size)
			hint += '<span class="adm-fileinput-drag-area-popup-param">' + BX.message('JS_CORE_FILE_INFO_SIZE') + ':&nbsp;<span>' + item.size + '</span></span>';
		if (item.dialogName == "BX.UploaderImage")
		{
			var text = '' ;
			if (item.file["width"] > 0 && item.file["height"] > 0)
			{
				text = item.file.width + 'x' + item.file.height;
			}
			else if (item.canvasIsLoaded && item.canvas)
			{
				text = item.canvas.width + 'x' + item.canvas.height;
			}
			if (text != '')
				hint += '<span class="adm-fileinput-drag-area-popup-param">' + BX.message('JS_CORE_FILE_INFO_DIM') + ':&nbsp;<span>' + text + '</span></span>';
		}

		if (item.description == undefined)
			item.description = (BX(id + 'Description') && BX(id + 'Description').value ? BX(id + 'Description').value : '');
		if (item.description)
		{
			hint +=  '<span class="adm-fileinput-drag-area-popup-param">' +
				BX.message('JS_CORE_FILE_DESCRIPTION') +
				':&nbsp;<span>' +
					BX.util.htmlspecialchars(String(item.description).replace(/\&quot\;/gi, "\"")) +
				'</span></span>';
		}
		var path = item["file"] ? (item["file"]["real_url"] || item["file"]["tmp_url"]) : '';
		if (path)
		{
			path = BX.util.htmlspecialchars(path);
			hint += '<span class="adm-fileinput-drag-area-popup-param">' + BX.message('JS_CORE_FILE_INFO_LINK') + ':&nbsp;<span><a target="_blank" href="' + path.replace(/[%]/g, "%25") + '">' + path + '</a></span></span>';
		}

		node.hint = new BX.CHint({
				parent: node,
				show_timeout: 10,
				hide_timeout: 200,
				dx: -10,
				preventHide: true,
				min_width: 165,
				hint: hint
			});
	},
	replaceItem : function(item, file, thumb)
	{
		file['id'] = item['id'];
		item.replaced = true;
		this.deleteFile(item, true);
		thumb.removeAttribute("bx-bound-editor");
		this.agent.onAttach([file], [thumb]);
	},
	deleteFile : function(item, immediate)
	{
		var pointer = (item ? this.agent.getItem(item.id) : false);
		if (!pointer)
			return;
		item = pointer.item;
		this.decrementFrameCounters(item.id, item);
		for (var ii in this['fileEvents'])
		{
			if (this['fileEvents'].hasOwnProperty(ii))
				BX.removeCustomEvent(item, ii, this['fileEvents'][ii]);
		}
		var node = pointer.node;
		if (node)
		{
			if (node.hint)
				node.hint.Destroy();
			if (item.replaced !== true)
				BX.addClass(node, "adm-fileinput-item-remove");
		}

		var name = (item['file']['input_name'] || node["__replaceInputName"]),
			nDelName = name + '_del';
		if (name && name.indexOf('[') > 0)
			nDelName = name.substr(0, name.indexOf('[')) + '_del' + name.substr(name.indexOf('['));

		if (item['file']['input_name'])
		{
			node = BX.create("INPUT", { props : {
				name : name,
				type : "hidden",
				value : item['file']['input_value']}});
			this.container.appendChild(node);
			node = BX.create("INPUT", { props : {
				name : nDelName,
				type : "hidden",
				value : "Y"}});
			this.agent.fileInput.parentNode.appendChild(node);
		}
		else
		{
			var n = BX.findChild(this.container, {tagName : "INPUT", attr : { name : name, disabled : true }}, false);
			if (n)
			{
				BX.adjust(n, { attrs : { disabled : false } } );
				BX.adjust(BX.findChild(this.container, {tagName : "INPUT", attr : { name : nDelName, disabled : true }}, false), { attrs : { disabled : false } } );
			}
		}

		if (immediate !== true)
			setTimeout(function(){ item.deleteFile(); }, 500);
		else
			item.deleteFile();
	},
	destroy : function() {
		this.deleteFiles();
	}
};
BX["UI"].FileInput.getInstance = function(id) {
	return repo[id];
};
var filePath = function(id, events, maxCount)
{
	this.single = parseInt(maxCount) === 1;
	this.id = id;
	this.number = 0;
	this.templateNode = [
			'<div class="adm-fileinput-item-panel"', (this.single ? ' style="display: none;"' : ''), '>',
				'<span class="adm-fileinput-item-panel-btn adm-btn-del" id="#id#_#number#_del">&nbsp;</span>',
			'</div>',
			'<div class="adm-fileinput-urls-item">',
				'<label for="#id#_#number#_path">', BX.message("JS_CORE_FI_LINK"),'</label>',
				'<input id="#id#_#number#_path" type="text" value="" />',
			'</div>'
		].join("").replace(/#id#/gi, BX.util.htmlspecialchars(this.id));
	var v1 = (this.number++);
	this.number++;
	this.template = [
		'<div class="adm-fileinput-urls-container" id="#id#_container">',
			'<ol class="adm-fileinput-list adm-fileinput-urls" id="#id#_list">',
				'<li>',
					this.templateNode.replace(/#number#/gi, v1 + ''),
				'</li>',
			'</ol>',
			'<a href="#" id="#id#_add_point" class="adm-fileinput-item-add"', (this.single ? ' style="display:none"' : ''),'>', BX.message("JS_CORE_FI_ADD_LINK"), '</a>',
			'<div style="clear:both;"></div>',
		'</div>'].join("").replace(/#id#/gi, BX.util.htmlspecialchars(this.id));

	if (events)
	{
		for (var ii in events)
		{
			if (events["hasOwnProperty"] && events.hasOwnProperty(ii))
			{
				BX.addCustomEvent(this, ii, events[ii]);
			}
		}
	}
	this._onAfterShow = this.onAfterShow.bind(this);
	this._onApply = this.onApply.bind(this);
	this._onCancel = this.onCancel.bind(this);
	this._addRow = this.addRow.bind(this);
	this._delRow = this.delRow.bind(this);
};
filePath.prototype = {
	popup : null,
	number : 0,
	addRow : function(e)
	{
		BX.PreventDefault(e);
		var list = BX(this.id + '_list'), v2 = (this.number++);
		if (list)
		{
			list.appendChild(BX.create('LI', {html :  this.templateNode.replace(/#number#/gi, v2 + '')}));
			BX.defer_proxy(function(){
				BX.bind(BX(this.id + '_' + v2 + '_del'), "click", this._delRow);
				this.bindFocus();
			}, this)();
		}
		return false;
	},
	bindFocus : function()
	{
		var list = BX(this.id + '_list');
		if (list && !this.single)
		{
			for (var ii = 0; ii < this.number; ii++)
			{
				if (BX(this.id + '_' + ii + '_path'))
				{
					BX.unbind(BX(this.id + '_' + ii + '_path'), "focus", this._addRow);
				}
			}
			for (ii = this.number; ii >= 0; ii--)
			{
				if (BX(this.id + '_' + ii + '_path'))
				{
					BX.bind(BX(this.id + '_' + ii + '_path'), "focus", this._addRow);
					break;
				}
			}
		}
	},
	delRow : function(e)
	{
		var node = e['currentTarget'] || e['target'];
		if (BX(node))
		{
			var li = BX.findParent(node, {tagName : "LI"});
			if (BX(li))
			{
				var rebuild = (li == li.parentNode.lastChild);
				BX.remove(li);
				if (rebuild)
					this.bindFocus();
			}
		}
	},
	onApply : function()
	{
		var list = BX(this.id + '_list'), result = [], v;
		if (list)
		{
			for (var ii = 0; ii <= this.number; ii++)
			{
				v = (BX(this.id + '_' + ii + '_path') ? BX(this.id + '_' + ii + '_path').value : '');
				if (v && v.length > 0)
					result.push(v);
			}
		}
		BX.onCustomEvent(this, "onApply", [result, this]);
		this.onCancel();
	},
	onCancel : function()
	{
		this.popup.close();
		this.popup.destroy();
		this.popup = null;
		this.number = 0;
	},
	onAfterShow : function()
	{
		BX.bind(BX(this.id + '_add_point'), "click", this._addRow);
		for (var ii = 0; ii <= this.number; ii++)
		{
			if (BX(this.id + '_' + ii + '_del'))
			{
				BX.bind(BX(this.id + '_' + ii + '_del'), "click", this._delRow);
			}
		}
		this.bindFocus();
		BX.removeCustomEvent(this.popup, "onAfterPopupShow", this._onAfterShow);
	},
	show : function()
	{
		if (this.popup === null)
		{
			var editorNode = BX.create("DIV", {
				attrs : {id : this.id + 'Proper'},
				style : { display : "none" },
				html : this.template
			});
			this.popup = BX.PopupWindowManager.create(
				'popup' + this.id,
				null,
				{
					autoHide : true,
					lightShadow : true,
					closeIcon : false,
					closeByEsc : true,
					content : editorNode,
					overlay : {},
					events : {
						onAfterPopupShow : this._onAfterShow
					},
					buttons : [
						new BX.PopupWindowButton( {text : BX.message("JS_CORE_FI_ADD"), className : "popup-window-button-accept", events : { click : this._onApply } } ),
						new BX.PopupWindowButtonLink( {text : BX.message("JS_CORE_FI_CANCEL"), className : "popup-window-button-link-cancel", events : { click : this._onCancel } } )
					]
				}
			);
		}
		this.popup.show();
		this.popup.adjustPosition();
	}
};
var
FramePresetID = 0,
FramePreset = function() {
	var d = function(params) {
		this.id = 'framePreset' + (FramePresetID++);
		this.onAfterShow = this.onAfterShow.bind(this);
		this.addRow = this.addRow.bind(this);
		this.delRow = this.delRow.bind(this);
		if (params)
		{
			this.init(params);
		}
	};
	d.prototype = {
		active : null,
		id : 'framePreset0',
		values : [],
		valuesInner : [],
		popup : null,
		number : 0,
		maxLength : 10,
		getTemplateNode : function()
		{
			return [
				'<li>',
				'<div class="adm-fileinput-item-panel">',
				'<span class="adm-fileinput-item-panel-btn adm-btn-del" id="#classId##id#_del">&nbsp;</span>',
				'</div>',
				'<div class="adm-fileinput-presets-item">',
				'<input class="adm-fileinput-presets-title" id="presets_#id#__title_" name="presets[#id#][title]" type="text" value="#title#" placeholder="', BX.message("JS_CORE_FI_TITLE"), '" />',
				'<input class="adm-fileinput-presets-width" name="presets[#id#][width]" type="text" value="#width#" placeholder="', BX.message("JS_CORE_FI_WIDTH"), '" />',
				'<input class="adm-fileinput-presets-hight" name="presets[#id#][height]" type="text" value="#height#" placeholder="', BX.message("JS_CORE_FI_HEIGHT"), '" />',
				'</div>',
				'</li>'
			].join("").replace(/#classId#/gi, BX.util.htmlspecialchars(this.id));
		},
		getTemplate : function()
		{
			return [
				'<div class="adm-fileinput-presets-container" id="#classId#_container">',
				'<form id="#classId#_form">',
				'<ol class="adm-fileinput-list adm-fileinput-presets" id="#classId#_list">',
				'#nodes#',
				'</ol>',
				'</form>',
				'<a href="#" id="#classId#_add_point" class="adm-fileinput-item-add">', BX.message("JS_CORE_FI_ADD_PRESET"), '</a>',
				'<div style="clear:both;"></div>',
				'</div>'
			].join("");
		},
		init : function(params)
		{
			this.values = [];
			this.length = 0;
			if (BX.type.isArray(params["presets"]))
			{
				var id;
				for (var ii = 0; ii < params["presets"].length; ii++)
				{
					id = this.values.length;
					this.values.push({id : id,
						title : params["presets"][ii]["title"],
						width : params["presets"][ii]["width"],
						height : params["presets"][ii]["height"]});
				}

				this.length = this.values.length;
				this.setActive(params["presetActive"]);
			}
		},
		setActive : function(id)
		{
			id = parseInt(id);
			this.activeId = this.values[id] ? id : 0;
			this.active = (this.values[this.activeId] || null);
		},
		edit : function(params)
		{
			var
				node = BX.proxy_context,
				html = '',
				values = this.values;
			for (var ii = 0; ii < values.length; ii ++)
			{
				html += this.getTemplateNode()
					.replace(/#id#/gi, BX.util.htmlspecialchars(values[ii]["id"]))
					.replace(/#title#/gi, BX.util.htmlspecialchars(values[ii]["title"]))
					.replace(/#width#/gi, BX.util.htmlspecialchars(values[ii]["width"]))
					.replace(/#height#/gi, BX.util.htmlspecialchars(values[ii]["height"]));
			}

			if (this.values.length < this.maxLength && params && params["width"] && params["height"])
			{
				html += this.getTemplateNode()
					.replace(/#id#/gi, BX.util.htmlspecialchars(ii))
					.replace(/#title#/gi, "")
					.replace(/#width#/gi, BX.util.htmlspecialchars(params["width"]))
					.replace(/#height#/gi, BX.util.htmlspecialchars(params["height"]));
			}

			if (!!this.popup)
				this.popup.close();
			var res = BX.pos(node);

			this.popup = new BX.PopupWindow('bx-preset-popup-' + node.id, node, {
				lightShadow : true,
				offsetTop: -3,
				className : "bxu-poster-popup",
				offsetLeft: Math.ceil(res.width / 2),
				autoHide: true,
				closeByEsc: true,
				bindOptions: {position: "top"},
				overlay : false,
				events : {
					onAfterPopupShow : this.onAfterShow,
					onPopupClose : function() { this.destroy() },
					onPopupDestroy : BX.proxy(function() { this.popup = null; }, this)
				},
				buttons : [
					new BX.PopupWindowButton( {text : BX.message('CANVAS_OK'), className : "popup-window-button-accept", events : { click : BX.delegate(function() {
						var data = BX.UploaderUtils.FormToArray(BX(this.id + '_form'));
						this.onApply(data.data);
						this.popup.close();
					}, this) } } ),
					new BX.PopupWindowButtonLink( {text : BX.message('CANVAS_CANCEL'), className : "popup-window-button-link-cancel", events : { click : function(){this.popup.close();}.bind(this) } } )
				],
				content : this.getTemplate().replace(/#classId#/gi, BX.util.htmlspecialchars(this.id)).replace(/#nodes#/i, html)
			});
			this.popup.show();
			this.popup.setAngle({position:'bottom'});
			this.popup.bindOptions.forceBindPosition = true;
			this.popup.adjustPosition();
			BX.focus(BX('popupText' + node.id));
			this.popup.bindOptions.forceBindPosition = false;
		},
		onAfterShow : function()
		{
			BX.bind(BX(this.id + "_add_point"), "click", this.addRow);
			var node, notEmpty = false;
			for (var ii = 0; ii < this.length; ii++)
			{
				node = BX(this.id + ii + "_del");
				if (node)
				{
					BX.bind(node, "click", this.delRow);
					notEmpty = (notEmpty===false ? 0 : notEmpty) + 1;
				}
			}
			if (notEmpty === false)
				this.addRow();
			this.checkAddButton();
			if (BX('presets_' + ii + '__title_'))
				BX.focus(BX('presets_' + ii + '__title_'))

		},
		checkAddButton : function()
		{
			var list = BX(this.id + '_list'),
				active = (this.maxLength > list.childNodes.length);
			if (active)
				BX.removeClass(BX(this.id + "_add_point"), "disabled");
			else
				BX.addClass(BX(this.id + "_add_point"), "disabled");
			return active;
		},
		addRow : function(e)
		{
			BX.PreventDefault(e);
			var list = BX(this.id + '_list'), id;
			if (list && this.checkAddButton())
			{
				id = this.length++;
				list.appendChild(BX.create('LI', {html :  this.getTemplateNode()
					.replace(/^<li(.*?)>/gi, "")
					.replace(/<\/li(.*?)>$/gi, "")
					.replace(/#id#/gi, BX.util.htmlspecialchars(id))
					.replace(/#title#/gi, "")
					.replace(/#width#/gi, "")
					.replace(/#height#/gi, "") }));
				BX.defer_proxy(function(){
					BX.bind(BX(this.id + id + "_del"), "click", this.delRow);
				}, this)();
			}
			this.checkAddButton();
			return false;
		},
		delRow : function(e)
		{
			var node = BX.proxy_context || e['currentTarget'] || e['target'];
			if (BX(node))
			{
				var li = BX.findParent(node, {tagName : "LI"});
				if (li)
				{
					BX.remove(li);
				}
			}
			this.checkAddButton();
		},
		onApply : function(data)
		{
			this.values = [];
			if (data && data["presets"])
			{
				data["presets"] = BX.util.array_values(data["presets"]);
				for (var id, ii = 0; ii < data["presets"].length; ii++)
				{
					if (data["presets"][ii] && data["presets"][ii]["width"] > 0 && data["presets"][ii]["height"] > 0)
					{
						id = this.values.length;
						this.values.push({
							id : id,
							width : data["presets"][ii]["width"],
							height : data["presets"][ii]["height"],
							title : (data["presets"][ii]["title"] ||
							(data["presets"][ii]["width"] + 'x' + data["presets"][ii]["height"]))
						});
					}
				}
			}
			this.save();
			BX.onCustomEvent(this, "onApply", [this.values, this]);
		},
		savedLastTime : '',
		save : function()
		{
			var sParam = '';
			sParam += '&p[0][c]=main&p[0][n]=fileinput';
			if (this.values.length > 0)
			{
				for (var jj, ii = 0; ii < this.values.length; ii++)
				{
					for (jj in this.values[ii])
					{
						if (this.values[ii].hasOwnProperty(jj))
						{
							if (jj != "id")
								sParam += "&p[0][v][presets][" + ii + "][" + jj + "]=" + BX.util.urlencode(this.values[ii][jj]);
						}
					}
				}
			}
			else
			{
				sParam += "&p[0][v][presets]=";
			}
			if (this.savedLastTime != sParam)
			{
				BX.ajax({
					'method': 'GET',
					'dataType': 'html',
					'processData': false,
					'cache': false,
					'url': BX.userOptions.path+sParam+'&sessid='+BX.bitrix_sessid()
				});
			}
		},
		getActive : function()
		{
			this.activeId = (this.values[this.activeId] ? this.activeId : 0);
			var res = this.values[this.activeId];
			if (res)
				res["id"] = this.activeId;
			return (res || null);
		}
	};
	return d;
} ();

var preset, cnv,
	FrameMaster = function(params)
{
	this.params = params;
	this.preset = preset = (preset || new FramePreset(params));
	BX.addCustomEvent(this.preset, "onApply", this.onPresetsApply.bind(this));
	this.id = 'FM'; // TODO Generate some ID
	this.handlers = {
		show : this.onShow.bind(this),
		afterShow : this.onAfterShow.bind(this),
		close : this.onClose.bind(this),
		cancel : this.onCancel.bind(this),
		apply : this.onApply.bind(this)
	}
};
FrameMaster.prototype = {
	id : '',
	canvas : null,
	description : null,
	agent : null,
	items : null,
	activeItem: null,
	popup : null,
	init : function(params)
	{
		if (params)
		{
			this.params = params;
			this.preset.init(params);
		}
		this.params = (this.params || {});
		this.params["description"] = (this.params["description"] !== false);
	},
	start : function(agent, activeId, params)
	{
		this.init(params);
		this.agent = agent;
		this.items = new BX.UploaderUtils.Hash();
		cnv = (cnv || new BX.UploaderFileCnvConstr());
		var items = this.agent.getItems(),
			item, id;
		items.reset();
		while ((item = items.getNext()) && item)
		{
			if (item.dialogName == "BX.UploaderImage")
			{
				id = this.id + '_' + item.id;
				this.items.setItem(id , {
					id : id,
					item : item,
					canvas : item.canvas.cloneNode(true),
					file : null,
					props : {
						description : item.description
					}
				} );
				if (activeId == item.id)
					activeId = id;
			}
		}
		if (this.items.length > 0)
		{
			this.activeItem = (this.items.hasItem(activeId) ? this.items.getItem(activeId) : this.items.getFirst());
			this.showEditor();
		}
	},
	finish : function(save)
	{
		if (this.items !== null && this.agent !== null)
		{
			var item;
			this.items.reset();
			while ((item = this.items.getNext()) && item)
			{
				if (save && item.props)
				{
					BX.onCustomEvent(this.agent, "onFilesPropsAreModified", [item.item.id, item.item, item.props]);
				}
				if (save && item.file)
				{
					BX.onCustomEvent(this.agent, "onFileIsFramed", [item.item.id, item.item, item.canvas, item.file]);
				}
				BX.remove(item.canvas);
				delete item.canvas;
				delete item.file;
				delete item.item;
				delete item.props;

			}
			BX.onCustomEvent(this.agent, "onFilesAreFramed", []);
		}
		this.agent = null;
		this.items = null;
		this.activeItem = null;
	},
	onShow : function() { },
	bindThumbItem : function(item) {
		var node = BX(item.id + 'EditorItemCanvas');
		node.parentNode.replaceChild(item.canvas, node);

		BX.addClass(item.canvas, "adm-photoeditor-preview-panel-container-img");

		item.canvas.setAttribute("id", item.id + 'EditorItemCanvas');
		var i = 0,
			f = function(id) {
			i++;
			if (id)
			{
				if (i > 1)
					BX.adjust(item.canvas, { props : { width : item.item.canvas.width, height : item.item.canvas.height} });
				BX.removeCustomEvent(item.item, "onFileIsInited", f);
			}
			item.canvas.getContext("2d").drawImage(item.item.canvas, 0, 0);
		};
		BX.addCustomEvent(item.item, "onFileIsInited", f);
		f();

		BX.removeClass(BX(item.id + 'EditorItem'), "adm-photoeditor-preview-panel-container-wait");
		BX.bind(BX(item.id + 'EditorItem'), "click", BX.proxy(function() { if (this.activeItem !== item) { this.setActiveItem(item); } }, this));
		BX.bind(BX(item.id + 'EditorItemDelete'), "click", BX.proxy(function(e){
			BX.PreventDefault(e); this.deleteItem(item);}, this));
	},
	deleteItem : function(item) {
		if (this.activeItem == item)
		{
			this.items.setPointer(item.id);
			var active = this.items.getNext();
			if (active)
				this.setActiveItem(active);
			else
				this.clearActiveItem();
		}
		BX.remove(BX(item.id + 'EditorItem'));
		item = this.items.removeItem(item.id);
		BX.onCustomEvent(this, "onDeleteItem", [item.item]);
	},
	saveActiveItem : function(canvas) {
		if (this.activeItem !== null)
		{
			var item = this.activeItem,
				res = BX.UploaderUtils.scaleImage(canvas, {width : thumbSize, height : thumbSize});
			if (res.destin.width != item.canvas.width || res.destin.height != item.canvas.height)
			{
				BX.adjust(item.canvas, { props : res.destin });
			}
			else
			{
				BX.adjust(item.canvas, { props : { width : item.canvas.width - 1} });
				BX.adjust(item.canvas, { props : { width : item.canvas.width + 1} });
			}
			item.canvas.getContext("2d").drawImage(canvas,
				0, 0, canvas.width, canvas.height,
				0, 0, item.canvas.width, item.canvas.height
			);
			var dataURI = canvas.toDataURL(item.item.file.type, 0.75);
			item.file = BX.UploaderUtils.dataURLToBlob(dataURI);
			item.file.width = canvas.width;
			item.file.height = canvas.height;
		}
	},
	saveActiveItemChanges : function()
	{
		if (this.activeItem != null)
		{
			this.activeItem.props.description = this.description.value;
			if (this.canvas.changed === true)
			{
				BX.onCustomEvent(this.canvas, "onChange", [this.canvas.getCanvas(), this.canvas]);
			}
		}
		return null;
	},
	clearActiveItem : function() {
		this.activeItem = this.saveActiveItemChanges();
		this.canvas.set(BX.create('CANVAS'), { props : { width : 100, height : 100 } });
		this.description.value = '';
	},
	setActiveItem : function(item) {
		if (this.activeItem != item)
		{
			BX.onCustomEvent(this, "onActiveItemIsChanged", [item, this.activeItem]);
			BX.onCustomEvent(this.canvas, "onCropHasToBeHidden", []);
			BX.onCustomEvent(this.canvas, "onActiveItemIsChanged", []);
			if (this.activeItem !== null)
			{
				BX.removeClass(BX(this.activeItem.id + 'EditorItem'), "active");
			}
			this.saveActiveItemChanges();
		}

		if (!BX.hasClass(BX(item.id + 'EditorItem'), "active"))
		{
			BX.addClass(BX(item.id + 'EditorItem'), "active");
		}

		this.activeItem = item;
		this.description.value = String(item.props.description).replace(/\&quot\;/gi, "\"");
		var file = (item.file || item.item.file);
		this.canvas.set(item.canvas, { props : { width : file.width, height : file.height } });
		if (item.canvas.width != file.width || item.canvas.height != file.height)
		{
			item.__onload = BX.proxy(function(image)
			{
				if (this.activeItem == item)
				{
					BX.adjust(cnv.getCanvas(), { props : { width : image.width, height : image.height } } );
					cnv.getContext().drawImage(image, 0, 0);
					this.canvas.set(cnv.getCanvas(), false);
					item.__onerror();
				}
			}, this);
			item.__onerror = BX.proxy(function()
			{
				if (this.activeItem == item)
				{
					item.__onload = null;
					delete item.__onload;
					item.__onerror = null;
					delete item.__onerror;
				}
			}, this);
			BX.defer_proxy(function(){cnv.push(file, item.__onload, item.__onerror);})();
		}
	},
	onAfterShow : function() {
		try
		{
			this.bindTemplate();
		}
		catch(e)
		{
			this['bindTemplateCounter'] = (this['bindTemplateCounter'] || 0) + 1;
			if (this['bindTemplateCounter'] < 10)
			{
				setTimeout(BX.proxy(this.onAfterShow, this), 500);
			}
		}

		var item;
		this.items.reset();
		while ((item = this.items.getNext()) && item)
		{
			this.bindThumbItem(item);
		}

		this.canvas.setCancelNode(this.id + "cancel");
		this.canvas.setMapCollapsed(this.id + "MapCollapsed");
		this.setActiveItem(this.activeItem);

		BX.bind(BX(this.id + 'turn-l'), "click", BX.proxy(function(){ this.rotate(false); }, this.canvas));
		BX.bind(BX(this.id + 'turn-r'), "click", BX.proxy(function(){ this.rotate(true); }, this.canvas));
		BX.bind(BX(this.id + 'flip-v'), "click", BX.proxy(function(){ this.flip(false); }, this.canvas));
		BX.bind(BX(this.id + 'flip-h'), "click", BX.proxy(function(){ this.flip(true); }, this.canvas));
		BX.bind(BX(this.id + 'crop'), "click", BX.proxy(function(){ this.crop(BX.proxy_context); }, this.canvas));
		BX.bind(BX(this.id + 'grayscale'), "click", BX.proxy(function(){ this.blackAndWhite(); }, this.canvas));
		BX.bind(BX(this.id + 'sign'), "click", BX.proxy(function(){ this.poster(BX.proxy_context); }, this.canvas));

		BX.bind(BX(this.id + 'scaleIndicator'), "mousedown", BX.proxy(this.canvas.scale, this.canvas));

		BX.bind(BX(this.id + 'scaleWidthPlus'), "mousedown", BX.proxy(function(){ this.increaseScale("width", true)}, this));
		BX.bind(BX(this.id + 'scaleWidthPlus'), "mouseup", BX.proxy(function(){ this.scaleChange("width", true)}, this));
		BX.bind(BX(this.id + 'scaleWidth'), "focus", BX.proxy(function(){ this.startTraceScale("width"); }, this));
		BX.bind(BX(this.id + 'scaleWidth'), "blur", BX.proxy(function(){ this.stopTraceScale("width"); }, this));
		BX.bind(BX(this.id + 'scaleWidthMinus'), "mousedown", BX.proxy(function(){ this.increaseScale("width", false)}, this));
		BX.bind(BX(this.id + 'scaleWidthMinus'), "mouseup", BX.proxy(function(){ this.scaleChange("width", false)}, this));

		BX.bind(BX(this.id + 'scaleHeightPlus'), "mousedown", BX.proxy(function(){ this.increaseScale("height", true)}, this));
		BX.bind(BX(this.id + 'scaleHeightPlus'), "mouseup", BX.proxy(function(){ this.scaleChange("height", true)}, this));
		BX.bind(BX(this.id + 'scaleHeight'), "focus", BX.proxy(function(){ this.startTraceScale("height"); }, this));
		BX.bind(BX(this.id + 'scaleHeight'), "blur", BX.proxy(function(){ this.stopTraceScale("height"); }, this));
		BX.bind(BX(this.id + 'scaleHeightMinus'), "mousedown", BX.proxy(function(){ this.increaseScale("height", false)}, this));
		BX.bind(BX(this.id + 'scaleHeightMinus'), "mouseup", BX.proxy(function(){ this.scaleChange("height", false)}, this));

		this.onPresetsApply();
		BX.bind(BX(this.id + 'presetsValues'), "change", BX.proxy(function()
			{
				this.preset.setActive(BX.proxy_context.value);
			}, this));
		BX.bind(BX(this.id + 'presetInsert'), "click", BX.proxy(function()
		{
			BX(this.id + 'scaleChained').checked = true;
			var preset = this.preset.getActive();

			if (preset)
			{
				this.canvas.scaleInit(BX(this.id + 'scaleIndicator'));
				this.canvas.cropInsert(preset, BX(this.id + 'crop'));
			}
		}, this));
		BX.bind(BX(this.id + 'presetEdit'), "click", BX.proxy(this.preset.edit, this.preset));
		BX.addCustomEvent(this.canvas, "onCropStart", function(){ BX.removeClass(BX(this.id + 'presetSave'), "disabled"); }.bind(this));
		BX.addCustomEvent(this.canvas, "onCropFinish", function(){ BX.addClass(BX(this.id + 'presetSave'), "disabled"); }.bind(this));

		BX.bind(BX(this.id + 'presetSave'), "click", BX.proxy(function() {
				if (this.canvas.busy === true && this.canvas.cropObj)
				{
					this.preset.edit(this.canvas.cropObj.cropParams);
				}
		}, this));

		BX.bind(BX(this.id + 'editorQueueUp'), "click", BX.proxy(this.up, this));
		BX.bind(BX(this.id + 'editorQueueDown'), "click", BX.proxy(this.down, this));
	},
	onPresetsApply : function() {
		var node = BX(this.id + 'presetsValues');
		if (node)
		{
			var presets = '', activePreset = this.preset.getActive(), ii;
			for (ii = 0; ii < this.preset.values.length; ii++)
			{
				presets += '<option value="' + ii + '" bx-width="' + BX.util.htmlspecialchars(this.preset.values[ii]['width']) + '" bx-height="' +
					BX.util.htmlspecialchars(this.preset.values[ii]['height']) + '"' + (ii == activePreset["id"] ? ' selected="selected"' : '') +
				'>' + BX.util.htmlspecialchars(preset.values[ii]['title'] + '(' + this.preset.values[ii]['width'] + 'x' + this.preset.values[ii]['height']) + ')</option>';
			}
			node.innerHTML = presets;
		}
	},
	onApply : function() {
		this.saveActiveItemChanges();
		this.popup['onApplyFlag'] = true;
		this.popup.close();
	},
	onCancel : function() {
		this.popup.close();
	},
	onClose : function() {
		this.finish((this.popup['onApplyFlag'] === true));

		BX.removeCustomEvent(this.popup, "onPopupShow", this.handlers.show);
		BX.removeCustomEvent(this.popup, "onAfterPopupShow", this.handlers.afterShow);
		BX.removeCustomEvent(this.popup, "onPopupClose", this.handlers.close);

		BX.onCustomEvent(this.canvas, "onPopupClose", []);

		this.popup.destroy();
		this.popup = null;
		this.slider = null;
	},
	bindTemplate : function() {
		this.canvas = new CanvasMaster(
			this.id + 'editorActive',
			{
				block : BX(this.id + 'editorActiveBlock'),
				canvas : BX(this.id + 'editorActiveImageCanvas'),
				canvasBlock : BX(this.id + 'editorActiveImageBlock')
			}
		);
		BX.addCustomEvent(this.canvas, "onChange", this.saveActiveItem.bind(this));
		BX.addCustomEvent(this.canvas, "onSetCanvas", this.scaleChangeSize.bind(this));
		BX.addCustomEvent(this.canvas, "onScaleCanvas", this.scaleChangeSize.bind(this));
		this.canvas.registerWheel(BX(this.id  + 'editorActiveBlock'));
		this.description = BX(this.id + 'editorDescription');
	},
	getTemplate : function() {
		var thumbs='';
		var iTemplate = [
			/*jshint multistr: true */
			'<div class="adm-photoeditor-preview-panel-container adm-photoeditor-preview-panel-container-wait" id="#id#EditorItem"> \
				<div class="adm-photoeditor-preview-panel-container-sub"> \
					<img src="', '/bitrix/images/1.gif', '" class="adm-photoeditor-preview-panel-container-space" /> \
					<span id="#id#EditorItemCanvas"></span> \
					<span id="#id#EditorItemDelete" class="adm-photoeditor-preview-panel-container-close">&nbsp;</span> \
				</div> \
			</div>'
		].join(''),
			item;
		this.items.reset();
		while ((item = this.items.getNext()) && item)
		{
			thumbs += iTemplate.replace(/#id#/gi, BX.util.htmlspecialchars(item.id));
		}
		return [
			/*jshint multistr: true */
			'<div class="adm-photoeditor-container"> \
			<div class="adm-photoeditor-buttons-panel"> \
			<div class="adm-photoeditor-buttons-panel-save disabled" id="', this.id, 'presetSave', '"><span>', BX.message("JS_CORE_FI_SAVE_PRESET"),'</span></div> \
			<div class="adm-photoeditor-buttons-panel-cancel disabled" id="', this.id, 'cancel', '"><span>', BX.message("JS_CORE_FI_CANCEL_PRESET"),'</span></div> \
			<div class="adm-photoeditor-btn-wrap"> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-turn-l" id="', this.id, 'turn-l', '" title="', BX.message("CANVAS_TURN_L"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-turn-r" id="', this.id, 'turn-r', '"title="', BX.message("CANVAS_TURN_R"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-flip-v" id="', this.id, 'flip-v', '" title="', BX.message("CANVAS_FLIP_V"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-flip-h" id="', this.id, 'flip-h', '" title="', BX.message("CANVAS_FLIP_H"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-crop" id="', this.id, 'crop', '" title="', BX.message("CANVAS_CROP"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-grayscale" id="', this.id, 'grayscale', '" title="', BX.message("CANVAS_GRAYSCALE"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-sign" id="', this.id, 'sign', '" title="', BX.message("CANVAS_SIGN"),'"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
			</div> \
			<div id="', this.id, 'cropPresets"> \
				<div class="adm-photoeditor-buttons-panel-cropping"> \
					<span class="crop">', BX.message("JS_CORE_FI_FRAMING"), '</span> \
					<span class="adm-select-wrap"> \
						<select class="adm-select" id="', this.id, 'presetsValues"></select> \
						</span> \
				</div> \
				<div class="adm-photoeditor-btn-wrap"> \
					<span class="adm-photoeditor-btn adm-photoeditor-btn-cut" id="', this.id, 'presetInsert" title="', BX.message("JS_CORE_FI_USE_PRESET"),'"> \
						<span class="adm-photoeditor-btn-icon"></span> \
					</span> \
					<span class="adm-photoeditor-btn adm-photoeditor-btn-edit" id="', this.id, 'presetEdit" title="', BX.message("JS_CORE_FI_EDIT_PRESET"),'"> \
						<span class="adm-photoeditor-btn-icon"></span> \
					</span> \
				</div> \
				<span class="adm-photoeditor-btn adm-photoeditor-btn-ph disabled" id="', this.id, 'MapCollapsed"> \
					<span class="adm-photoeditor-btn-icon"></span> \
				</span> \
			</div> \
		</div> \
		<div class="adm-photoeditor-sidebar"> \
			<div class="adm-photoeditor-sidebar-options"> \
				<span class="adm-photoeditor-sidebar-options-title">', BX.message("JS_CORE_FI_WIDTH"), '</span> \
				<span class="adm-photoeditor-plus" id="', this.id , 'scaleWidthPlus">&nbsp;</span> \
				<input type="number" value="0" id="', this.id , 'scaleWidth" /> \
				<span class="adm-photoeditor-minus" id="', this.id , 'scaleWidthMinus">&nbsp;</span> \
			</div> \
			<div class="adm-photoeditor-sidebar-options"> \
				<div class="sidebar-options-checkbox-container"> \
					<input type="checkbox" value="Y" id="', this.id , 'scaleChained" checked /> \
					<label for="', this.id , 'scaleChained"><span class="label-icon"></span></label> \
				</div> \
			</div> \
			<div class="adm-photoeditor-sidebar-options"> \
				<span class="adm-photoeditor-sidebar-options-title">', BX.message("JS_CORE_FI_HEIGHT"), '</span> \
				<span class="adm-photoeditor-plus" id="', this.id , 'scaleHeightPlus">&nbsp;</span> \
				<input type="number" value="0" id="', this.id , 'scaleHeight" /> \
				<span class="adm-photoeditor-minus" id="', this.id , 'scaleHeightMinus">&nbsp;</span> \
			</div> \
			<div class="adm-photoeditor-sidebar-scale"> \
				<span class="adm-photoeditor-sidebar-scale-value" style="top: 0">+100%</span> \
				<span class="adm-photoeditor-sidebar-scale-value" style="top: 25%">+50%</span> \
				<span class="adm-photoeditor-sidebar-scale-value" style="top: 50%">0%</span> \
				<span class="adm-photoeditor-sidebar-scale-value" style="top: 75%">-50%</span> \
				<span class="adm-photoeditor-sidebar-scale-value" style="top: 100%">-100%</span> \
				<div class="adm-photoeditor-scale-indicator" style="top: 50%" id="', this.id, 'scaleIndicator">0%</div> \
			</div> \
		</div> \
		<div class="adm-photoeditor"> \
			<div class="adm-photoeditor-preview-panel"> \
				<span class="adm-photoeditor-preview-panel-arrow-top" id="', this.id, 'editorQueueUp"></span> \
				<div class="adm-photoeditor-preview-panel-previews" id="', this.id, 'editorQueue"> \
					<div class="adm-photoeditor-preview-panel-previews-inner" id="', this.id, 'editorQueueInner">', thumbs, '</div> \
				</div> \
				<span class="adm-photoeditor-preview-panel-arrow-bottom" id="', this.id, 'editorQueueDown"></span> \
			</div> \
			<div class="adm-photoeditor-active-block-outer"> \
				<div class="adm-photoeditor-active-block" id="', this.id, 'editorActiveBlock"> \
					<div class="adm-photoeditor-active-image-block" id="', this.id, 'editorActiveImageBlockOuter"> \
						<div class="adm-photoeditor-active-image" id="', this.id, 'editorActiveImageBlock"> \
							<div class="adm-photoeditor-crop" id="', this.id, 'editorActiveCrop"></div> \
							<canvas id="', this.id, 'editorActiveImageCanvas"></canvas> \
						</div> \
						<div class="adm-photoeditor-active-cursor"></div> \
						<div class="adm-photoeditor-active-move-cursor"></div> \
					</div> \
				</div> \
				<div class="adm-photoeditor-desc"><input type="text" id="', this.id, 'editorDescription" placeholder="', BX.message("JS_CORE_FILE_DESCRIPTION"),'"></div> \
			</div> \
		</div> \
	</div>'].join('').replace(/[\n\t]/gi, '').replace(/>\s</gi, '><');
	},
	showEditor : function() {
		if (!this.popup || this.popup === null)
		{
			var editorNode = BX.create("DIV", {
				attrs : {
					id : this.id + 'Proper',
					className : "bxu-edit-popup"
				},
				style : { display : "none" },
				html : this.getTemplate()
			});
			this.popup = BX.PopupWindowManager.create(
				'popup' + this.id,
				null,
				{
					className : "bxu-popup" + (this.params["description"] !== false ? "" : " bxu-popup-nondescription"),
					autoHide : true,
					lightShadow : true,
					closeIcon : false,
					closeByEsc : true,
					content : editorNode,
					overlay : {},
					events : {
						onPopupShow : this.handlers.show,
						onAfterPopupShow : this.handlers.afterShow,
						onPopupClose : this.handlers.close
					},
					buttons : [
						new BX.PopupWindowButton( {text : BX.message('JS_CORE_WINDOW_SAVE'), className : "popup-window-button-accept", events : { click : this.handlers.apply } } ),
						new BX.PopupWindowButtonLink( {text : BX.message('JS_CORE_WINDOW_CANCEL'), className : "popup-window-button-link-cancel", events : { click : this.handlers.cancel } } )
					]
				}
			);
		}
		this.popup.show();
		this.popup.adjustPosition();
	},
	slider : null,
	initSliderParams : function() {
		if (this.slider == null)
		{
			this.slider = {
				top : 0,
				step : 30,
				outer : BX(this.id + 'editorQueue'),
				outerPos : BX.pos(BX(this.id + 'editorQueue')),
				inner : BX(this.id + 'editorQueueInner'),
				innerPos : BX.pos(BX(this.id + 'editorQueueInner'))
			};
			this.slider.maxHeight = Math.min((this.slider.outerPos.height - this.slider.innerPos.height), 0);
		}
		return this.slider;
	},
	up : function(e) {
		if (this.initSliderParams())
		{
			this.slider.top = Math.min((this.slider.top + this.slider.step), 0);
			this.slider.inner.style.top = this.slider.top + 'px';
		}
		return BX.PreventDefault(e);
	},
	down : function(e) {
		if (this.initSliderParams())
		{
			this.slider.top = Math.max(this.slider.maxHeight, (this.slider.top - this.slider.step));
			this.slider.inner.style.top = this.slider.top + 'px';
		}
		return BX.PreventDefault(e);
	},
	lastProportion : {
		width : 0,
		height : 0,
		"~width" : 0,
		"~height" : 0,
		timeout : null
	},
	increaseScale : function(name, direction) {
		if (this.increaseScaleTimeout > 0)
			clearTimeout(this.increaseScaleTimeout);

		name = (name == "width" ? "width" : "height");
		var node = (name == "width" ? BX(this.id + 'scaleWidth') : BX(this.id + 'scaleHeight')),
			_this = this,
			f = function() {
			if (direction === false)
			{
				node.value = (++_this.lastProportion[name]);
			}
			else if (direction === true)
			{
				node.value = Math.max((--_this.lastProportion[name]), 1);
			}

			_this.lastProportion[name]= parseInt(node.value);
			_this.increaseScaleTimeoutCounter++;
			_this.increaseScaleTimeout = setTimeout(f, (150 - Math.min(100, _this.increaseScaleTimeoutCounter*_this.increaseScaleTimeoutCounter)));
		};
		this.increaseScaleTimeoutCounter = 0;
		this.increaseScaleTimeout = setTimeout(f, 50);
	},
	startTraceScale : function(name) {
		if (this.traceScaleTimeout > 0)
			clearTimeout(this.traceScaleTimeout);
		this.traceScaleTimeout = (this.traceScaleTimeout > 0 ? this.traceScaleTimeout : 0);

		name = (name == "width" ? "width" : "height");

		var node = (name == "width" ? BX(this.id + 'scaleWidth') : BX(this.id + 'scaleHeight'));
		if (this.lastProportion[name] === parseInt(node.value))
		{
			this.traceScaleTimeout++;
			if (this.traceScaleTimeout > 5)
			{
				this.traceScaleTimeout = 0;
				this.scaleAdjust(name, null);
			}
		}
		else
		{
			this.traceScaleTimeout = 0;
			this.lastProportion[name] = parseInt(node.value);
		}
		this.traceScaleTimeout = setTimeout(BX.proxy(function(){ this.startTraceScale(name); }, this), 500);
	},
	stopTraceScale : function(name) {
		if (this.traceScaleTimeout > 0)
			clearTimeout(this.traceScaleTimeout);
		this.traceScaleTimeout = 0;
		this.scaleAdjust(name, null);
	},
	scaleChange : function(name, direction) {
		if (this.increaseScaleTimeout > 0)
			clearTimeout(this.increaseScaleTimeout);
		if (this.traceScaleTimeout > 0)
			clearTimeout(this.traceScaleTimeout);
		if (this.lastProportion.timeout === null)
			this.lastProportion.timeout = setTimeout(BX.proxy(this.scaleAdjust, this), 500);
	},
	scaleAdjust : function(width) {
		this.lastProportion.timeout = null;
		if (width > 0)
		{
			this.lastProportion["~width"] = this.lastProportion.width = width;
			this.canvas.scaleWidth(this.lastProportion.width, true, BX(this.id + 'scaleIndicator'));
		}
		else
		{
			if (this.lastProportion["~width"] != this.lastProportion.width)
			{
				this.lastProportion["~width"] = this.lastProportion.width;
				this.canvas.scaleWidth(this.lastProportion.width, this.scaleIsChained(), BX(this.id + 'scaleIndicator'));
			}
			if (this.lastProportion["~height"] != this.lastProportion.height)
			{
				this.lastProportion["~height"] = this.lastProportion.height;
				this.canvas.scaleHeight(this.lastProportion.height, this.scaleIsChained(), BX(this.id + 'scaleIndicator'));
			}
		}
	},
	scaleIsChained : function() {
		return (!!BX(this.id + 'scaleChained').checked);
	},
	scaleChained : function() {
		BX(this.id + 'scaleWidth').value = this.lastProportion.width;
	},
	scaleChangeSize : function(canvas) {
		this.lastProportion["~width"] =
			this.lastProportion.width =
				BX(this.id + 'scaleWidth').value = (canvas.width || 0);
		this.lastProportion["~height"] =
			this.lastProportion.height =
				BX(this.id + 'scaleHeight').value = (canvas.height || 0);
	}
};
var CanvasStack = function(depth)
{
	this.depth = depth;
};
CanvasStack.prototype = {
	id : 'CanvasStack',
	depth : 3,
	stack : [],
	number : 0,
	init : function()
	{
		var canvas;
		while ((canvas = this.stack.shift()) && canvas)
		{
			BX.remove(canvas);
			canvas = null;
		}
		this.stack = [];
		BX.onCustomEvent(this, "onChange", [this.stack.length, this.stack]);
	},
	add : function(canvas)
	{
		this.number++;
		var res = BX.create("CANVAS", {
			attrs : { id : this.id +  this.number},
			props : { width : canvas.width, height : canvas.height },
			style : { display : "none" }
		});
		res.getContext("2d").drawImage(canvas, 0, 0);
		this.stack.push(res);
		while (this.stack.length > this.depth)
		{
			res = this.stack.shift();
			BX.remove(res);
			res = null;
		}
		BX.onCustomEvent(this, "onChange", [this.stack.length, this.stack]);
	},
	restore : function()
	{
		var res = this.stack.pop();
		if (res)
		{
			BX.onCustomEvent(this, "onRestore", [res]);
			BX.remove(res);
			BX.onCustomEvent(this, "onChange", [this.stack.length, this.stack]);
		}
	}
};
var CanvasMaster = function(id, nodes) {
	this.block = nodes.block;
	this.block.pos = BX.pos(this.block);
	this.canvas = nodes.canvas;
	this.ctx = this.canvas.getContext("2d");

	this.canvasBlock = nodes.canvasBlock;
	var pos = BX.UploaderUtils.scaleImage(this.block.pos, {width : this.mapSize, height : this.mapSize});
	this.canvasMap = new CanvasMapMaster(
		nodes.block,
		{
			width : pos.destin.width,
			height : pos.destin.height,
			scale : pos.coeff
		}
	);
	BX.addCustomEvent(this.canvasMap, "onMapPointerIsMoved", this.move.bind(this));
	BX.addCustomEvent(this.canvasMap, "onMapIsInitialised", function(){ this.registerWheel(this.canvasMap.root); }.bind(this));

	this.id = id;
	BX.bind(window, "resize", BX.proxy(this.onResizeWindow, this));
	this.stack = new CanvasStack(5);
	BX.addCustomEvent(this.stack, "onChange", this.changeCancelNode.bind(this));
	BX.addCustomEvent(this.stack, "onRestore", this.restore.bind(this));
	BX.addCustomEvent(this, "onActiveItemIsChanged", BX.delegate(this.stack.init, this.stack));
	BX.addCustomEvent(this, "onPopupClose", BX.delegate(function(){
		if(this.cropObj) { this.cropObj.finish(); }
		this.canvasMap.hide(false);
	}, this));
};
CanvasMaster.prototype = {
	registerWheel : function(node)
	{
		BX.bind(node, this.onWheelEvent, this.onWheel.bind(this));
		if (this.onWheelEvent == "DOMMouseScroll")
			BX.bind(node, "MozMousePixelScroll", this.onWheel.bind(this));
	},
	onWheelEvent : ("onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
		document['onmousewheel'] !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
		"DOMMouseScroll"// let's assume that remaining browsers are older Firefox
	),
	onWheelMaxSpeed : 50,
	onWheelLastEvent : 0,
	onWheel : function(e)
	{
		// calculate deltaY (and deltaX) according to the event
		var delta, time = (new Date()).getTime();
		if ((time - this.onWheelLastEvent) < this.onWheelMaxSpeed)
			return BX.PreventDefault(e);
		this.onWheelLastEvent = time;

		if (e['deltaY'])
		{
			delta = e['deltaY'];
		}
		else if ( this.onWheelEvent == "mousewheel" )
		{
			delta = - 1/40 * e.wheelDelta;
		}
		else
		{
			delta = e.detail;
		}
		if (delta != undefined)
		{
			this.zoom(delta * (-1), e);
		}

		return BX.PreventDefault(e);
	},
	startScale : 1,
	mapSize : 300,
	onResizeWindow : function()
	{
	},
	setHack : {width : 0, height : 0},
	set : function(canvas, props)
	{
		var settings = (props || {props : { width : canvas.width , height : canvas.height } }),
			pos = this.block.pos,
			k, kReal,
			style;

		if ((this.setHack.width + '') == (settings.props.width + '') &&
			(this.setHack.height + '') == (settings.props.height + ''))
		{
			this.setHack.width--;
			BX.adjust(this.canvas, { props : this.setHack });
		}

		this.setHack.width = settings.props.width;
		this.setHack.height = settings.props.height;

		BX.adjust(this.canvas, settings);

		kReal = k = Math.min(
			( this.canvas.width > 0 ? pos["width"] / this.canvas.width : 1 ),
			( this.canvas.height > 0 ? pos["height"] / this.canvas.height : 1 )
		);
		k = (0 < k && k < 1 ? k : 1);
		style = {
			top : Math.ceil((pos["height"] - this.canvas.height * k) / 2),
			left : Math.ceil((pos["width"] - this.canvas.width * k) / 2),
			width : this.canvas.width,
			height : this.canvas.height,
			transform : 'translate3d(0, 0, 0) scale(' + k + ', ' + k + ')'
		};
		this.zoomCounter = 0;
		this.canvas.startScale = k;
		this.canvas.maxVisibleScale = kReal;
		this.canvas.scale = k;
		this.canvas.pos = {
			absoluteLeft : style.left + pos.left,
			absoluteTop : style.top + pos.top,
			absoluteWidth : pos.width,
			absoluteHeight : pos.height,
			startedWidth : Math.ceil(this.canvas.width * k),
			startedHeight : Math.ceil(this.canvas.height * k),
			left : style.left,
			top : style.top,
			shiftX : 0, // ceil
			'~shiftX' : 0, // float
			shiftY : 0, // ceil
			'~shiftY' : 0 // float
		};

		this.canvas.visiblePart = {
			left : 0,
			'~left' : 0,
			top : 0,
			'~top' : 0,
			"~width" : canvas.width,
			width : canvas.width,
			"~height" : canvas.height,
			height : canvas.height,
			leftGap : this.canvas.pos.left,
			topGap : this.canvas.pos.top,
			rightGap : this.canvas.pos.absoluteWidth - this.canvas.pos.startedWidth - this.canvas.pos.left,
			bottomGap : this.canvas.pos.absoluteHeight - this.canvas.pos.startedHeight - this.canvas.pos.top
		};

		for (var ii in style)
		{
			if (style.hasOwnProperty(ii))
			{
				if (style[ii] > 0)
				{
					style[ii] = style[ii] + 'px';
				}
			}
		}
		BX.adjust(
			BX(this.canvasBlock), {
				style : style
			}
		);

		this.ctx.drawImage(canvas,
			0, 0, canvas.width, canvas.height,
			0, 0, this.canvas.width, this.canvas.height
		);
		this.canvasMap.init(this.canvas, (props ? props.props : false));
		BX.onCustomEvent(this, "onSetCanvas", [this.canvas, canvas]);
		this.changed = false;
	},
	setMapCollapsed : function(node)
	{
		if (this.canvasMap)
			this.canvasMap.registerCollapsedNode(node);
	},
	setCancelNode : function(node)
	{
		this.cancelNode = BX(node);
		if (!BX.hasClass(this.cancelNode, "disabled"))
			BX.addClass(this.cancelNode, "disabled");
		BX.bind(this.cancelNode, "click", BX.delegate(this.stack.restore, this.stack));
	},
	changeCancelNode : function(available)
	{
		if (available)
			BX.removeClass(this.cancelNode, "disabled");
		else if (!BX.hasClass(this.cancelNode, "disabled"))
			BX.addClass(this.cancelNode, "disabled");
	},
	restore : function(canvas)
	{
		this.set(canvas, { props : { width : canvas.width, height : canvas.height } } );
		BX.onCustomEvent(this, "onCropHasToBeHidden", []);
	},
	cursor : {
		x : 0,
		y : 0
	},
	zoomEdge : 2,
	zoomCounter : 0,
	busy : false,
	zoom : function(zoomIn, e)
	{
		if (this.busy !== false)
			return;

		var delta = 0;

		if (zoomIn > 0 && this.zoomCounter < this.zoomEdge)
			delta = 0.1;
		else if (zoomIn < 0 && this.zoomCounter > 0)
			delta = -0.1;

		if (delta !== 0)
		{
			this.zoomCounter += delta;
			var newScale = this.canvas.startScale + this.zoomCounter;
			if (e)
			{
				BX.fixEventPageXY(e);
				this.cursor = { // Cursor position above the picture
					x : Math.max(0, Math.min((e.pageX - this.canvas.pos.absoluteLeft), this.canvas.pos.absoluteWidth)),
					y : Math.max(0, Math.min((e.pageY - this.canvas.pos.absoluteTop), this.canvas.pos.absoluteHeight))
				};
			}
			this.zoomProcess(newScale);
		}
	},
	zoomProcess : function(newScale)
	{
		this.zoomCanvas(newScale);
		if (this.canvas.scale > this.canvas.maxVisibleScale)
		{
			this.canvasMap.show();
			this.canvasMap.zoom(this.canvas.visiblePart);
		}
		else
		{
			this.canvasMap.zoom(this.canvas.visiblePart);
			this.canvasMap.hide(true);
		}
	},
	zoomCanvas : function(newScale, e)
	{
		if (e === true || newScale <= this.canvas.startScale)
		{
			this.canvas.visiblePart = {
				left : 0,
				'~left' : 0,
				top : 0,
				'~top' : 0,
				"~width" : this.canvas.width,
				width : this.canvas.width,
				"~height" : this.canvas.height,
				height : this.canvas.height,
				leftGap : this.canvas.pos.left,
				topGap : this.canvas.pos.top,
				rightGap : this.canvas.pos.absoluteWidth - this.canvas.pos.startedWidth - this.canvas.pos.left,
				bottomGap : this.canvas.pos.absoluteHeight - this.canvas.pos.startedHeight - this.canvas.pos.top
			};

			this.canvas.pos['~shiftX'] = 0;
			this.canvas.pos['~shiftY'] = 0;
			this.canvas.pos.shiftX = 0;
			this.canvas.pos.shiftY = 0;
			this.canvas.scale = this.canvas.startScale;
		}
		else
		{
			var
				x = this.cursor.x,
				y = this.cursor.y,

				left = (((-1) * (this.canvas.pos['~shiftX'] || 0) + x) / this.canvas.scale),
				top = (((-1) * (this.canvas.pos['~shiftY'] || 0) + y) / this.canvas.scale),

				newLeft = left * newScale,
				newTop = top * newScale,

				xShift = Math.max(newLeft - x, 0),
				yShift = Math.max(newTop - y, 0);

			this.formVisiblePart(newScale, xShift, yShift);

			xShift *= (-1);
			yShift *= (-1);

			this.canvas.pos["~shiftX"] = xShift;
			this.canvas.pos["~shiftY"] = yShift;

			this.canvas.pos.shiftX = Math.ceil(xShift);
			this.canvas.pos.shiftY = Math.ceil(yShift);
			this.canvas.scale = newScale;
		}

		this.canvasBlock.style.transform =
			'translate3d(' + this.canvas.pos.shiftX + 'px, ' + this.canvas.pos.shiftY + 'px, 0) ' +
			'scale(' + this.canvas.scale + ', ' + this.canvas.scale + ')';
		BX.onCustomEvent(this, "onCanvasPositionHasChanged", []);
	},
	formVisiblePart : function(newScale, xShift, yShift)
	{
		var
			visibleLeft = (xShift - this.canvas.pos.left),
			visibleTop = (yShift - this.canvas.pos.top);

		this.canvas.visiblePart["~left"] = (visibleLeft > 0 ? visibleLeft : 0) / newScale;
		this.canvas.visiblePart["left"] = Math.ceil(this.canvas.visiblePart["~left"]);
		this.canvas.visiblePart["~top"] = (visibleTop > 0 ? visibleTop : 0) / newScale;
		this.canvas.visiblePart["top"] = Math.ceil(this.canvas.visiblePart["~top"]);

		var
			newWidth = this.canvas.width * newScale,
			leftGap = (-1) * visibleLeft,
			rightGap = this.canvas.pos.absoluteWidth - (newWidth + leftGap);

		if (this.canvas.pos.absoluteWidth > newWidth)
		{
			if (leftGap < 0)
				newWidth += leftGap;
			else if (rightGap < 0)
				newWidth += rightGap;
		}
		else
		{
			newWidth = this.canvas.pos.absoluteWidth;
			if (leftGap > 0)
				newWidth -= leftGap;
			else if (rightGap > 0)
				newWidth -= rightGap;
		}
		this.canvas.visiblePart["leftGap"] = leftGap;
		this.canvas.visiblePart["rightGap"] = rightGap;

		this.canvas.visiblePart["~etalonWidth"] = (this.canvas.pos.absoluteWidth / newScale);
		this.canvas.visiblePart["~width"] = (newWidth / newScale);
		this.canvas.visiblePart["width"] = Math.ceil(this.canvas.visiblePart["~width"]);

		var
			newHeight = this.canvas.height * newScale,
			topGap = (-1) * visibleTop,
			bottomGap = this.canvas.pos.absoluteHeight - (newHeight + topGap);

		if (this.canvas.pos.absoluteHeight > newHeight)
		{
			if (topGap < 0)
				newHeight += topGap;
			else if (bottomGap < 0)
				newHeight += bottomGap;
		}
		else
		{
			newHeight = this.canvas.pos.absoluteHeight;
			if (topGap > 0)
				newHeight -= topGap;
			else if (bottomGap > 0)
				newHeight -= bottomGap;
		}

		this.canvas.visiblePart["topGap"] = topGap;
		this.canvas.visiblePart["bottomGap"] = bottomGap;

		this.canvas.visiblePart["~etalonHeight"] = (this.canvas.pos.absoluteHeight / newScale);
		this.canvas.visiblePart["~height"] = (newHeight / newScale);
		this.canvas.visiblePart["height"] = Math.ceil(this.canvas.visiblePart["~height"]);
	},
	move : function(pos)
	{
		var
			width = Math.round(this.canvas.width * this.canvas.scale),
			height = Math.round(this.canvas.height * this.canvas.scale),
			xShift = (-1) * Math.ceil(pos['left'] * this.canvas.scale),
			yShift = (-1) * Math.ceil(pos['top'] * this.canvas.scale);

		if (pos.right === true || (width + xShift) < this.canvas.pos.absoluteWidth)
			xShift = this.canvas.pos.absoluteWidth - width;
		if (pos.bottom === true || (height + yShift) < this.canvas.pos.absoluteHeight)
			yShift = this.canvas.pos.absoluteHeight - height;

		xShift -=  this.canvas.pos.left;
		yShift -=  this.canvas.pos.top;

		this.formVisiblePart(this.canvas.scale, (-1) * xShift, (-1) * yShift);


		this.canvas.pos['~shiftX'] = xShift;
		this.canvas.pos['~shiftY'] = yShift;
		this.canvas.pos.shiftX = xShift;
		this.canvas.pos.shiftY = yShift;

		this.canvasBlock.style.transform =
			'translate3d(' + this.canvas.pos.shiftX + 'px, ' + this.canvas.pos.shiftY + 'px, 0) ' +
			'scale(' + this.canvas.scale + ', ' + this.canvas.scale + ')';
		BX.onCustomEvent(this, "onCanvasPositionHasChanged", []);
	},
	canvasesID : "canvasForEdit",
	copyCanvas : null,
	workCanvas : null,
	getCopyCanvas : function()
	{
		if (this.copyCanvas === null)
		{
			this.copyCanvas = BX.create("CANVAS", { attrs : { id : this.id + 'Copy' }, style : { display : "none" }});
		}
		BX.adjust(this.copyCanvas, { props : { width : this.canvas.width, height : this.canvas.height } });
		this.copyCanvas.getContext("2d").drawImage(this.canvas, 0, 0);
		return this.copyCanvas;
	},
	getCanvas : function()
	{
		return this.canvas;
	},
	getWorkCanvas : function(draw)
	{
		if (this.workCanvas === null)
		{
			this.workCanvas = BX.create("CANVAS", { attrs : { id : this.id + 'Work' }, style : { display : "none" }});
		}
		BX.adjust(this.workCanvas, { props : { width : this.canvas.width, height : this.canvas.height } });
		if (draw)
			this.workCanvas.getContext("2d").drawImage(this.canvas, 0, 0);
		return this.workCanvas;
	},
	rotate : function(clockwise)
	{
		if (this.busy === true)
			return;
		var rad = Math.PI / 2 * (clockwise ? 1 : -1),
			tmpCanvas1 = this.getCopyCanvas(),
			tmpCanvas2 = this.getWorkCanvas(false),
			w = this.canvas.height,
			h = this.canvas.width;

		BX.adjust(tmpCanvas2, { props : { width : w, height : h } });

		var ctx = tmpCanvas2.getContext("2d");

		ctx.save();

		if (clockwise)
			ctx.translate(tmpCanvas1.height, 0);
		else
			ctx.translate(0, tmpCanvas1.width);

		ctx.rotate(rad);
		ctx.drawImage(tmpCanvas1, 0, 0);
		ctx.restore();

		this.set(tmpCanvas2, { props : { width : w, height : h } } );
		this.stack.add(tmpCanvas1);
		this.changed = true;
	},
	poster : function(node)
	{
		if (this.busy === true)
			return;
		if (!!this.posterPopup)
			this.posterPopup.close();
		var res = BX.pos(node);

		this.posterPopup = new BX.PopupWindow('bx-poster-popup-' + node.id, node, {
			lightShadow : true,
			offsetTop: -3,
			className : "bxu-poster-popup",
			offsetLeft: Math.ceil(res.width / 2),
			autoHide: true,
			closeByEsc: true,
			bindOptions: {position: "top"},
			overlay : false,
			events : {
				onPopupClose : function() { this.destroy() },
				onPopupDestroy : BX.proxy(function() { this.posterPopup = null; }, this)
			},
			buttons : [
				new BX.PopupWindowButton( {text : BX.message('CANVAS_OK'), events : { click : BX.delegate(function() {
					var msg = BX('posterPopupText' + node.id);
					if (!!msg && msg.value.length > 0)
						this.posterApply(msg.value);
					this.posterPopup.close();
				}, this) } } ),
				new BX.PopupWindowButtonLink( {text : BX.message('CANVAS_CANCEL'), events : { click : function(){this.posterPopup.close();}.bind(this) } } )
			],

			content : [/*jshint multistr: true */'<div class="bxu-poster-popup-dt">', BX.message("CANVAS_POSTER_SIGN"), '</div> \
				<input type="text" id="posterPopupText', node.id,'" maxlength="255" value="" />'].join("")
		});
		this.posterPopup.show();
		this.posterPopup.setAngle({position:'bottom'});
		this.posterPopup.bindOptions.forceBindPosition = true;
		this.posterPopup.adjustPosition();
		BX.focus(BX('posterPopupText' + node.id));
		this.posterPopup.bindOptions.forceBindPosition = false;
	},
	posterApply : function(msg)
	{
		if (msg)
		{
			this.stack.add(this.getCopyCanvas());
			var cnv = this.getWorkCanvas(true),
				ctx = cnv.getContext("2d"),
				size = Math.min(cnv.width, cnv.height) / 10;
			ctx.fillStyle = "black";
			ctx.fillRect(0, 0, cnv.width, size);
			ctx.fillRect(0, cnv.height - 2 * size, cnv.width, 2 * size);
			ctx.fillRect(0, 0, size, cnv.height);
			ctx.fillRect(cnv.width - size, 0, size, cnv.height);
			ctx.strokeStyle = "white";
			var border = 5;
			ctx.strokeRect(size - border, size - border,
				cnv.width - (size * 2) + 2 * border,
				cnv.height - (size * 3) + 2 * border);
			ctx.fillStyle = "white";
			ctx.textAlign = "center";
			ctx.textBaseline = "middle";
			ctx.font = size + "px marketing";
			ctx.fillText(msg, cnv.width / 2, cnv.height - size, cnv.width);
			this.set(cnv, false);
			this.changed = true;
		}
	},
	blackAndWhite : function()
	{
		if (this.busy === true)
			return;
		this.stack.add(this.getCopyCanvas());

		var cnv = this.getWorkCanvas(true),
			ctx = cnv.getContext("2d"),
			frame = ctx.getImageData(0, 0, cnv.width, cnv.height),
			v, i;

		for (i = 0; i < frame.data.length; i += 4)
		{
			v = (frame.data[i] + frame.data[i + 1] + frame.data[i + 2]) / 3;
			frame.data[i] = v;
			frame.data[i + 1] = v;
			frame.data[i + 2] = v;
		}
		ctx.putImageData(frame, 0, 0);
		this.set(cnv, false);
		this.changed = true;
	},
	flip : function(verticaly)
	{
		if (this.busy === true)
			return;
		this.stack.add(this.getCopyCanvas());
		var cnv = this.getWorkCanvas(true),
			ctx = cnv.getContext("2d");
		ctx.save();
		if (verticaly)
		{
			ctx.scale(1, -1);
			ctx.translate(0, -cnv.height);
		}
		else
		{
			ctx.scale(-1, 1);
			ctx.translate(-cnv.width, 0);
		}
		ctx.drawImage(cnv, 0, 0);
		ctx.restore();
		this.set(cnv, false);
		this.changed = true;
	},
	cropObj : null,
	cropStatus : false,
	cropInit : function(button)
	{
		if (this.cropObj === null)
		{
			this.cropObj = new CanvasCrop(
				this.id,
				this.canvas,
				BX(this.id + 'Crop')
			);
			this.cropObj.cropParams.topShift = parseInt(this.canvasBlock.style.top ? this.canvasBlock.style.top.replace(/[a-z]+$/, '') : 0);
			this.cropObj.cropParams.leftShift = parseInt(this.canvasBlock.style.left ? this.canvasBlock.style.left.replace(/[a-z]+$/, '') : 0);

			BX.addCustomEvent(this, "onCropHasToBeHidden", BX.delegate(this.cropObj.finish, this.cropObj));

			BX.addCustomEvent(this.cropObj, "onCropApply", this.cropApply.bind(this));
			BX.addCustomEvent(this.cropObj, "onCropTooBig", this.onCropTooBig.bind(this));
			BX.addCustomEvent(this.cropObj, "onCropStart", BX.delegate(function(){
				BX.addClass(button, "bxu-edit-btn-active");
				this.cropStatus = true;
				this.busy = true;
				this.canvasMap.occupy();
				BX.onCustomEvent(this, "onCropStart", [this.cropObj, this.cropObj.cropParams]);
			}, this));
			BX.addCustomEvent(this.cropObj, "onCropFinish", BX.delegate(function(){
				BX.removeClass(button, "bxu-edit-btn-active");
				this.busy = false;
				this.cropStatus = false;
				this.canvasMap.release();
				BX.onCustomEvent(this, "onCropFinish", [this.cropObj, this.cropObj.cropParams]);
			}, this));
			BX.addCustomEvent(this, "onCanvasPositionHasChanged", BX.delegate(this.cropObj.scale, this.cropObj));
		}
		return true;
	},
	crop : function(button)
	{
		if (this.cropInit(button))
			this.cropObj.turn();
	},
	onCropTooBigPopup : null,
	onCropTooBig : function(width, heigth)
	{
		if (this.onCropTooBigPopup === null)
		{
			this.onCropTooBigPopupApply = BX.delegate(function() {
				var res = BX.UploaderUtils.scaleImage(
					this.canvas,
					this.onCropTooBigPopup.presetParams,
					"circumscribed"
				);
				if (res.bNeedCreatePicture)
				{
					this.scaleZoom = false;
					this.scaleWidth(res.destin.width, true, BX(this.id + 'scaleIndicator'));
					this.scaleZoom = true;
				}
				this.cropInsert(this.onCropTooBigPopup.presetParams);
				this.onCropTooBigPopup.close();
			}, this);

			this.onCropTooBigPopupCancel = BX.delegate(function() {
				this.onCropTooBigPopup.close();
			}, this);

			this.onCropTooBigPopup = new BX.PopupWindow(
				'popupCropTooBigPopup' + this.id,
				null,
				{
					lightShadow : true,
					autoHide: true,
					closeByEsc: true,
					overlay : {},
					buttons : [
						new BX.PopupWindowButton( {text : BX.message("JS_CORE_FI_SCALE"), className : "popup-window-button-accept", events : { click : this.onCropTooBigPopupApply } } ),
						new BX.PopupWindowButtonLink( {text : BX.message("JS_CORE_FI_CANCEL"), className : "popup-window-button-link-cancel", events : { click : this.onCropTooBigPopupCancel } } )
					],
					content : '<div class="adm-photoeditor-buttons-panel-cropping-too-big">' + BX.message("JS_CORE_FI_PRESET_IS_TOO_BIG") + '</div>'
				}
			);
		}
		this.onCropTooBigPopup.presetParams = {width : width, height : heigth};
		this.onCropTooBigPopup.show();
	},
	cropApply : function(cropParams)
	{
		this.stack.add(this.getCopyCanvas());
		var tmpCanvas2 = this.getWorkCanvas(false);
		BX.adjust(tmpCanvas2, { props : { width : cropParams.width, height : cropParams.height } });
		tmpCanvas2.getContext("2d").drawImage(this.canvas, (-1) * cropParams.left, (-1) * cropParams.top);
		this.set(tmpCanvas2, { props : { width : cropParams.width, height : cropParams.height } } );
		this.changed = true;
	},
	cropInsert : function(preset, button)
	{
		if (preset && this.cropInit(button))
		{
			this.cropObj.insert(preset.width, preset.height);
		}
	},
	scaleObj : null,
	scaleInit : function(pointer)
	{
		if (this.scaleObj === null)
		{
			this.scaleObj = new CanvasScale(
				this.id,
				pointer,
				this.canvas
			);
			BX.addCustomEvent(this.scaleObj, "onScaleApply", BX.proxy(this.scaleApply, this));
			BX.addCustomEvent(this.scaleObj, "onScaleRestore", BX.proxy(this.scaleRestore, this));
			BX.addCustomEvent(this.scaleObj, "onScaleSuggest", BX.proxy(this.scaleSuggest, this));
			BX.addCustomEvent(this, "onSetCanvas", BX.proxy(function(){
				this.scaleObj.restore();
			}, this));
		}
		return true;
	},
	scale : function(e)
	{
		if (this.scaleInit(BX.proxy_context))
		{
			this.scaleObj.start(e, this.canvas);
		}
	},
	scaleWidth : function(width, prop, pointer)
	{
		if (this.scaleInit(pointer))
		{
			this.scaleObj.scaleWidth(width, this.canvas, prop);
		}
	},
	scaleHeight : function(height, prop, pointer)
	{
		if (this.scaleInit(pointer))
		{
			this.scaleObj.scaleHeight(height, this.canvas, prop);
		}
	},
	scaleApply : function(params)
	{
		this.stack.add(this.getCopyCanvas());
		BX.show(this.scaleObj.getPreventer());
		var
			tmpCanvas2 = this.getWorkCanvas(false),
			paramsOld = {
				maxVisibleScale : this.canvas.maxVisibleScale,
				scale : this.canvas.scale,
				width : this.canvas.width,
				height : this.canvas.height,
				"~shiftX" : this.canvas.pos['~shiftX'],
				"~shiftY" : this.canvas.pos['~shiftY']
			};

		BX.adjust(tmpCanvas2, { props : { width : params.width, height : params.height } });

		tmpCanvas2.getContext("2d").drawImage(this.canvas,
			0, 0, this.canvas.width, this.canvas.height,
			0, 0, params.width, params.height
		);

		this.set(tmpCanvas2, { props : { width : params.width, height : params.height } } );

		this.cursor.x = 0;
		this.cursor.y = 0;

		if (this.scaleZoom !== false)
		{
			var
				deltaWidth = (params.width - paramsOld.width) * paramsOld.scale,
				deltaHeight = (params.height - paramsOld.height) * paramsOld.scale,
				xShift = Math.max(
					((-1) * paramsOld["~shiftX"] + (deltaWidth / 2)),
					0),
				yShift = Math.max(
					((-1) * paramsOld['~shiftY'] + (deltaHeight / 2)),
					0);
			this.canvas.pos['~shiftX'] = (-1) * xShift / paramsOld.scale * this.canvas.scale;
			this.canvas.pos['~shiftY'] = (-1) * yShift / paramsOld.scale * this.canvas.scale;

			BX.addClass(this.canvasBlock, "adm-photoeditor-active-image-ascetic");
			this.zoomProcess(paramsOld.scale);
			BX.removeClass(this.canvasBlock, "adm-photoeditor-active-image-ascetic");
		}

		if (this.cropObj)
		{
			this.cropObj.scale();
		}

		BX.hide(this.scaleObj.getPreventer());
		this.changed = true;
	},
	scaleRestore : function()
	{
		BX.onCustomEvent(this, "onScaleCanvas", arguments);
	},
	scaleSuggest : function()
	{
		BX.onCustomEvent(this, "onScaleCanvas", arguments);
	}
};
var CanvasScale = function(id, pointer, canvas) {
	this.id = id;
	this.pointer = pointer;
	var pos = BX.pos(this.pointer.parentNode);
	this.divisionValue = pos.height / 200;
	this.process = this.process.bind(this);
	this.finish = this.finish.bind(this);

	if (canvas)
	{
		this.init(canvas);
	}
};
CanvasScale.prototype = {
	canvas : null,
	divisionValue : 0,
	value: 0,
	minValue : 0,
	maxValue : 100,
	cursor : {
		start : 0,
		end : 0
	},
	preventer : null,
	params : {
		scaleX : 1,
		scaleY : 1,
		"~scaleX" : 1,
		"~scaleY" : 1
	},
	init : function(canvas)
	{
		this.canvas = canvas;
		this.value = 0;
		this.minValue = (Math.max(1 / canvas.width, 1 / canvas.height) - 1) * 100;

		this.params.width = this.canvas.width;
		this.params.height = this.canvas.height;
		this.params["~scaleX"] = 1;
		this.params["~scaleY"] = 1;

		return this.block;
	},
	getPreventer : function()
	{
		var preventer = null;
		if (this.preventer === null)
		{
			preventer = BX.create('DIV', {
				attrs : {
					className : "adm-photoeditor-scale-area"
				}
			});
			if (BX(this.canvas))
			{
				this.canvas.parentNode.parentNode.insertBefore(preventer, this.canvas.parentNode);
				this.preventer = preventer;
			}
		}
		return this.preventer;
	},
	start : function(e, canvas)
	{
		this.init(canvas);
		BX.fixEventPageY(e);
		this.cursor.start = e.pageY;
		BX.bind(document, "mousemove", this.process);
		BX.bind(document, "mouseup", this.finish);
		return BX.PreventDefault(e);
	},
	process : function(e)
	{
		BX.fixEventPageY(e);
		this.cursor.end = e.pageY;
		this.value = (this.cursor.start - this.cursor.end) / this.divisionValue;
		if (this.value > this.maxValue)
			this.value = this.maxValue;
		else if (this.value < this.minValue)
			this.value = this.minValue;

		this.setVis();

		return BX.PreventDefault(e);
	},
	finish : function(e)
	{
		BX.unbind(document, "mousemove", this.process);
		BX.unbind(document, "mouseup", this.finish);
		if (parseInt(this.value) !== 0)
		{
			this.change();
		}
		else
		{
			this.restore();
		}
		return BX.PreventDefault(e);
	},
	setVis : function()
	{
		this.pointer.style.top = (50 + parseInt((-1) * this.value * 0.5)) + '%';
		this.pointer.innerHTML = Math.ceil(this.value) + '%';
		this.params.scaleX = (1 + this.value / 100) * this.params["~scaleX"];
		this.params.scaleY = (1 + this.value / 100) * this.params["~scaleY"];
		this.params.width = Math.ceil(this.canvas.width * this.params.scaleX);
		this.params.height = Math.ceil(this.canvas.height * this.params.scaleY);
		this.canvas.style.transform = "scale(" + this.params.scaleX + ", " + this.params.scaleY + ")";

		BX.onCustomEvent(this, "onScaleSuggest", [this.params, this.value])
	},
	scaleWidth : function(width, canvas, prop)
	{
		width = Math.ceil(width);
		if (width <= 0)
			return;

		this.init(canvas);
		var delta = this.params["~scaleX"];
		this.params["~scaleX"] = width / this.params.width;
		delta = this.params["~scaleX"] - delta;
		if (prop)
		{
			this.params["~scaleY"] += delta;
		}
		this.setVis();
		this.change();
	},
	scaleHeight : function(height, canvas, prop)
	{
		height = Math.ceil(height);
		if (height <= 0)
			return;
		this.init(canvas);
		var delta = this.params["~scaleX"];
		this.params["~scaleY"] = height / this.params.height;
		delta = this.params["~scaleY"] - delta;
		if (prop)
		{
			this.params["~scaleX"] += delta;
		}
		this.setVis();
		this.change();
	},
	change : function()
	{
		BX.onCustomEvent(this, "onScaleApply", [this.params, this.value]);
	},
	restore : function()
	{
		this.value = 0;
		this.params["~scaleX"] = 1;
		this.params["~scaleY"] = 1;
		this.setVis();
		this.canvas.style.transform = '';
		BX.onCustomEvent(this, "onScaleRestore", [this.canvas]);
	}
};
var CanvasCrop = function(id, canvas, pointerOnCanvas) {
	this.id = id;
	this.canvas = canvas;
	this.block = pointerOnCanvas;
};
CanvasCrop.prototype = {
	active : false,

	canvas : null,
	root : null,
	preventer : null,
	projection : null,
	pointer : null, // pointer on projection
	block : null, // pointer on canvas

	turn : function()
	{
		if (this.init())
		{
			if (this.active === false)
			{
				this.start();
			}
			else
			{
				this.finish();
			}
		}
	},
	cursor : {
		pageX : 0,
		pageY : 0
	},
	cropParams : {
		width : 0,
		height : 0,
		top : 0,
		left : 0,
		topShift : 0,
		leftShift : 0,
		defaultWidth : 0,
		defaultHeight : 0
	},
	init : function()
	{
		if (this.preventer === null)
		{
			this.root = BX.create('DIV', {
				attrs : {
					className : "adm-photoeditor-crop-area-root"
				},
				style : {
					position : "absolute",
					boxSizing: "border-box",
					"-webkit-user-select" : "none"
				}
			});
			document.body.appendChild(this.root);
			BX.ZIndexManager.register(this.root);
			this.preventer = BX.create('DIV', {
				attrs : {
					className : "adm-photoeditor-crop-area"
				}
			});

			this.apply = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-apply" },
				style : { zIndex : 3 },
				events : {
					click : this.cropApply.bind(this)
				}
			});
			this.cancel = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-cancel" },
				style : { zIndex : 3 },
				events : {
					click : this.cropCancel.bind(this)
				}
			});
			this.proportion = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-proportion" },
				style : { zIndex : 3 },
				events : {
					click : this.cropProportionActivate.bind(this)
				}
			});

			this.width = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-width" } } );
			this.height = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-height" } } );
			this.stretchStart = this.stretchStart.bind(this);
			this.stretch = this.stretch.bind(this);
			this.stretchEnd = this.stretchEnd.bind(this);
			this.leftBottom = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-left-bottom" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.left = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-left" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.leftTop = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-left-top" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.top = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-top" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.rightTop = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-right-top" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.right = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-right" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.rightBottom = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-right-bottom" },
				events : {
					mousedown : this.stretchStart
				}
			} );
			this.bottom = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-bottom" },
				events : {
					mousedown : this.stretchStart
				}
			} );

			this.pointer = BX.create('DIV', {
				attrs : { className : "adm-photoeditor-crop-pointer" },
				style : {
					position : "absolute",
					top : 0, left : 0, bottom : 0, right: 0,
					boxSizing : "border-box",
					zIndex : 3,
					cursor : "pointer"
				},
				children : [
					this.apply,
					this.cancel,
					this.proportion,
					BX.create('DIV',
						{
							attrs : {
								className : "adm-photoeditor-crop-measures"
							},
							children : [
								this.width,
								this.height
							]
						}),
					this.leftBottom,
					this.left,
					this.leftTop,
					this.top,
					this.rightTop,
					this.right,
					this.rightBottom,
					this.bottom
				]
			});
			this.overlayTop = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-overlay-top" } } );
			this.overlayRight = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-overlay-right" } } );
			this.overlayBottom = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-overlay-bottom" } } );
			this.overlayLeft = BX.create('DIV', { attrs : { className : "adm-photoeditor-crop-overlay-left" } } );

			this.projection = BX.create('DIV', {
				attrs : {id : 'projection'},
				style : { position : "absolute", top : 0, left : 0, bottom : 0, right: 0, zIndex : 1001, display : 'none', boxSizing: "border-box" },
				children : [
					this.overlayTop,
					this.overlayRight,
					this.overlayBottom,
					this.overlayLeft,
					this.pointer
				]
			} );
			this.projection.appendChild(this.pointer);

			this.root.appendChild(this.projection);
			this.root.appendChild(this.preventer);

			this.drawStart = this.drawStart.bind(this);
			this.draw = this.draw.bind(this);
			this.drawEnd = this.drawEnd.bind(this);

			this.stop = this.stop.bind(this);

			this.moveStart = this.moveStart.bind(this);
			this.move = this.move.bind(this);
			this.moveEnd = this.moveEnd.bind(this);

			BX.bind(this.pointer, "mousedown", this.moveStart);
		}
		return this.block;
	},
	scale : function()
	{
		if (this.canvas.width < this.cropParams.width || this.canvas.height < this.cropParams.height)
		{
			this.finish()
		}
		else if (this.active === true)
		{
			var
				left = 0, leftAfter = 0,
				top = 0, topAfter = 0;

			if (this.projection)
			{
				left = Math.ceil(parseInt(this.projection.style.left ? this.projection.style.left.replace("px", "") : 0));
				top = Math.ceil(parseInt(this.projection.style.top ? this.projection.style.top.replace("px", "") : 0));
			}

			this.showProjection();

			if (this.projection)
			{
				leftAfter = Math.ceil((this.projection.style.left ? parseInt(this.projection.style.left.replace("px", "")) : 0));
				topAfter = Math.ceil((this.projection.style.top ? parseInt(this.projection.style.top.replace("px", "")) : 0));

				if (left != leftAfter)
					this.pointer.left = Math.max(0, (this.pointer.left + left - leftAfter));
				if (topAfter != top)
					this.pointer.top = Math.max(0, (this.pointer.top + top - topAfter));
			}

			this.cropParams.left = Math.ceil(this.pointer.left / this.canvas.scale + this.canvas.visiblePart.left);
			this.cropParams.top = Math.ceil(this.pointer.top / this.canvas.scale + this.canvas.visiblePart.top);

			if (this.canvas.scale < 1)
			{
				this.cropParams.width = Math.ceil(this.pointer.width / this.canvas.scale);
				this.cropParams.height = Math.ceil(this.pointer.height / this.canvas.scale);
			}
			else
			{
				this.pointer.width = Math.ceil(this.cropParams.width * this.canvas.scale);
				this.pointer.height = Math.ceil(this.cropParams.height * this.canvas.scale);
			}

			this.setLeft(this.pointer.left, this.cropParams.left);
			this.setTop(this.pointer.top, this.cropParams.top);

			this.setWidth(this.pointer.width, this.cropParams.width);
			this.setHeight(this.pointer.height, this.cropParams.height);
		}
	},
	drawStart : function(e)
	{
		var left = 0,
			top = 0;
		if (e)
		{
			BX.fixEventPageXY(e);
			left = e.pageX - this.projection.pos.left;
			top = e.pageY - this.projection.pos.top;
			if (left > this.projection.pos.width || top > this.projection.pos.height)
				return this.finish();

			this.cursor = {
				pageX : e.pageX,
				pageY : e.pageY
			};
			BX.PreventDefault(e);
		}

		left = (left > 0 ? left : 0);
		top = (top > 0 ? top : 0);

		this.cropParams.left = Math.ceil(left / this.canvas.scale + this.canvas.visiblePart.left);
		this.cropParams.top = Math.ceil(top / this.canvas.scale + this.canvas.visiblePart.top);

		this.cropParams.maxWidth = this.canvas.width - this.cropParams.left;
		this.cropParams.maxHeight = this.canvas.height - this.cropParams.top;
		this.cropParams.savePropotions = (this.proportion.active === true);

		this.setLeft(left, this.cropParams.left);
		this.setWidth(0, 0);
		this.setTop(top, this.cropParams.top);
		this.setHeight(0, 0);

		this.showPointer();

		BX.bind(document, "mousemove", this.draw);
		return false;
	},
	draw : function(e)
	{
		var width = 0, height = 0;
		if (e)
		{
			BX.fixEventPageXY(e);
			width = e.pageX - this.cursor.pageX;
			height = e.pageY - this.cursor.pageY;
			BX.PreventDefault(e)
		}

		this.cropParams.width = Math.ceil(width / this.canvas.scale);
		if (width <= 0)
		{
			width = 0;
			this.cropParams.width = 0;
		}
		else if (width >= (this.projection.pos.width - this.pointer.left))
		{
			width = (this.projection.pos.width - this.pointer.left);
			this.cropParams.width = this.cropParams.maxWidth;
		}
		this.setWidth(width, this.cropParams.width);

		this.cropParams.height = Math.ceil(height / this.canvas.scale);
		if (height <= 0)
		{
			height = 0;
			this.cropParams.height = 0;
		}
		else if (height >= (this.projection.pos.height - this.pointer.top))
		{
			height = (this.projection.pos.height - this.pointer.top);
			this.cropParams.height = this.cropParams.maxHeight;
		}
		this.setHeight(height, this.cropParams.height);

		return false;
	},
	drawEnd : function(e)
	{
		if (e)
			BX.PreventDefault(e);
		this.stop();
		return false;
	},
	insert : function(width, height)
	{
		if (!this.init())
		{
			BX.DoNothing();
		}
		else if (this.canvas.width < width || this.canvas.height < height)
		{
			BX.onCustomEvent(this, "onCropTooBig", [width, height]);
		}
		else
		{
			this.start();

			this.cropProportionActivate(true);
			BX.onCustomEvent(this, "onCropStart", []);

			this.drawStart();

			this.cropParams.width = width;
			width = Math.ceil(width * this.canvas.scale);
			if (width <= 0)
			{
				width = 0;
				this.cropParams.width = 0;
			}
			else if (width >= (this.projection.pos.width - this.pointer.left))
			{
				width = (this.projection.pos.width - this.pointer.left);
				this.cropParams.width = this.cropParams.maxWidth;
			}
			this.setWidth(width, this.cropParams.width);

			this.cropParams.height = height;
			height = Math.ceil(height * this.canvas.scale);
			if (height <= 0)
			{
				height = 0;
				this.cropParams.height = 0;
			}
			else if (height >= (this.projection.pos.height - this.pointer.top))
			{
				height = (this.projection.pos.height - this.pointer.top);
				this.cropParams.height = this.cropParams.maxHeight;
			}
			this.setHeight(height, this.cropParams.height);

			this.drawEnd();
		}
	},
	moveStart : function(e)
	{
		this.cursor = null;
		if (e.target == this.pointer)
		{
			BX.fixEventPageXY(e);
			this.showPreventer();

			this.cursor = {
				pageX : e.pageX,
				pageY : e.pageY,
				x : e.pageX - this.projection.pos.left - this.pointer.left,
				y : e.pageY - this.projection.pos.top - this.pointer.top
			};

			BX.bind(document, "mousemove", this.move);
			BX.bind(document, "mouseup", this.moveEnd);
		}
	},
	move : function(e)
	{
		if (this.cursor != null)
		{
			BX.fixEventPageXY(e);
			var deltaX = e.pageX - this.cursor.pageX,
				deltaY = e.pageY - this.cursor.pageY,

				left = this.pointer.left + deltaX,
				top = this.pointer.top + deltaY;

			this.cursor.pageX = e.pageX;
			this.cursor.pageY = e.pageY;

			if (
				(left + this.pointer.width) >= this.projection.pos.width &&
				(this.canvas.visiblePart.left + this.canvas.visiblePart.width) >= this.canvas.width
			)
			{
				left = (this.projection.pos.width - this.pointer.width);
				this.cropParams.left = this.canvas.width - this.cropParams.width;
			}
			else
			{
				if (left <= 0)
					left = 0;
				else if ((left + this.pointer.width) >= this.projection.pos.width)
					left = (this.projection.pos.width - this.pointer.width);
				this.cropParams.left = Math.ceil(left / this.canvas.scale + this.canvas.visiblePart.left);
			}

			if (
				(top + this.pointer.height) >= this.projection.pos.height &&
				(this.canvas.visiblePart.top + this.canvas.visiblePart.height) >= this.canvas.height
			)
			{
				top = (this.projection.pos.height - this.pointer.height);
				this.cropParams.top = this.canvas.height - this.cropParams.height;
			}
			else
			{
				if (top <= 0)
					top = 0;
				else if ((top + this.pointer.height) >= this.projection.pos.height)
					top = (this.projection.pos.height - this.pointer.height);
				this.cropParams.top = Math.ceil(top / this.canvas.scale + this.canvas.visiblePart.top);
			}
			this.setLeft(left, this.cropParams.left);
			this.setTop(top, this.cropParams.top);
		}
	},
	moveEnd : function()
	{
		this.hidePreventer();
		BX.unbind(document, "mousemove", this.move);
		BX.unbind(document, "mouseup", this.moveEnd);
	},
	showPointer : function()
	{
		this.block.style.display = "block";
		this.pointer.style.display = "block";
		this.overlayTop.style.display = "block";
		this.overlayRight.style.display = "block";
		this.overlayBottom.style.display = "block";
		this.overlayLeft.style.display = "block";
	},
	hidePointer : function()
	{
		this.block.style.top = 0;
		this.block.style.bottom = 0;
		this.block.style.width = 0;
		this.block.style.height = 0;

		this.block.style.display = "none";

		this.pointer.style.display = "none";
		this.overlayTop.style.display = "none";
		this.overlayRight.style.display = "none";
		this.overlayBottom.style.display = "none";
		this.overlayLeft.style.display = "none";
	},
	showProjection : function()
	{
		var pos = BX.pos(this.block.parentNode.parentNode, false);

		BX.adjust(this.root, {style : {
			left : pos.left + 'px',
			top : pos.top + 'px',
			width : pos.width + 'px',
			height : pos.height + 'px',
			display : "block"
		}});
		BX.ZIndexManager.bringToFront(this.root);

		var projection = { left : 0, top : 0, right : 0, bottom : 0, display : "block" };
		if (this.canvas.visiblePart.topGap > 0)
			projection.top = Math.ceil(this.canvas.visiblePart.topGap) + 'px';
		if (this.canvas.visiblePart.leftGap > 0)
			projection.left = Math.ceil(this.canvas.visiblePart.leftGap) + 'px';
		if (this.canvas.visiblePart.bottomGap > 0)
			projection.bottom = Math.ceil(this.canvas.visiblePart.bottomGap) + 'px';
		if (this.canvas.visiblePart.rightGap > 0)
			projection.right = Math.ceil(this.canvas.visiblePart.rightGap) + 'px';
		BX.adjust(this.projection, {style : projection});

		this.projection.pos = BX.pos(this.projection);
	},
	hideProjection : function()
	{
		BX.hide(this.root);
		BX.hide(this.projection);
		BX.hide(this.preventer);
	},
	showPreventer : function()
	{
		BX.show(this.preventer);
	},
	hidePreventer : function()
	{
		BX.hide(this.preventer);
	},
	start : function()
	{
		this.active = true;

		BX.bind(document, "mousedown", this.drawStart);
		BX.bind(document, "mouseup", this.drawEnd);

		this.hidePointer();
		this.showProjection();
		this.showPreventer();

		BX.onCustomEvent(this, "onCropStart", []);
	},
	stop : function()
	{
		this.hidePreventer();

		BX.unbind(document, "mousedown", this.drawStart);
		BX.unbind(document, "mousemove", this.draw);
		BX.unbind(document, "mouseup", this.drawEnd);

		BX.onCustomEvent(this, "onCropStop", []);
	},
	finish : function()
	{

		this.active = false;
		this.stop();
		this.hidePointer();
		this.hideProjection();
		BX.onCustomEvent(this, "onCropFinish", [this.cropParams]);
	},
	cropApply : function()
	{
		this.finish();
		if (this.cropParams.width > 0 && this.cropParams.height > 0)
		{
			BX.onCustomEvent(this, "onCropApply", [this.cropParams]);
		}
	},
	cropCancel : function()
	{
		BX.onCustomEvent(this, "onCropCancel", []);
		this.finish();
	},
	cropProportion : {
		active : false,
		"w/h" : 0,
		"h/w" : 0
	},
	cropProportionActivate : function(active)
	{
		if (!(active === true || active === false))
		{
			active = this.cropProportion.active !== true;
		}
		this.cropProportion.active = active;
		if (this.cropProportion.active)
		{
			if (!BX.hasClass(this.proportion, "active"))
			{
				BX.addClass(this.proportion, "active");
			}
			BX.hide(this.left);
			BX.hide(this.top);
			BX.hide(this.right);
			BX.hide(this.bottom);
		}
		else
		{
			BX.removeClass(this.proportion, "active");
			BX.show(this.left);
			BX.show(this.top);
			BX.show(this.right);
			BX.show(this.bottom);
		}
	},
	cropProportionInit : function()
	{
		if (this.cropProportion.active !== true)
		{
			this.cropProportion["w/h"] = 0;
			this.cropProportion["h/w"] = 0;
		}
		else if (this.pointer.width > 0 && this.pointer.height > 0)
		{
			this.cropProportion["w/h"] = this.pointer.width / this.pointer.height;
			this.cropProportion["h/w"] = this.pointer.height / this.pointer.width;
		}
		else
		{
			this.cropProportion["w/h"] = 1;
			this.cropProportion["h/w"] = 1;
		}
	},
	stretchPosition : null,
	stretchStart : function(e)
	{
		BX.PreventDefault(e);
		var div = e['currentTarget'] || e['target'];
		this.stretchPosition = div.className.replace("adm-photoeditor-crop-", "");
		this.cursor = null;
		if (this.stretchPosition == "left-bottom" ||
			this.stretchPosition == "left" ||
			this.stretchPosition == "left-top" ||
			this.stretchPosition == "top" ||
			this.stretchPosition == "right-top" ||
			this.stretchPosition == "right" ||
			this.stretchPosition == "right-bottom" ||
			this.stretchPosition == "bottom"
		)
		{
			BX.fixEventPageXY(e);
			this.cropProportionInit();
			this.showPreventer();

			this.cursor = {
				pageX : e.pageX,
				pageY : e.pageY,
				left : this.projection.pos.left + this.pointer.left,
				right : (this.projection.pos.left + this.pointer.left + this.pointer.width),
				top : this.projection.pos.top + this.pointer.top,
				bottom : this.projection.pos.top + this.pointer.top + this.pointer.height
			};

			BX.bind(document, "mousemove", this.stretch);
			BX.bind(document, "mouseup", this.stretchEnd);
		}


		return false;
	},
	stretch : function(e)
	{
		if (this.cursor != null)
		{
			BX.fixEventPageXY(e);

			var
				tmp,
				left = null, width = null,
				top = null, height = null;

			this.cursor.pageX = e.pageX;
			this.cursor.pageY = e.pageY;

			if (this.stretchPosition.indexOf("left") >= 0)
			{
				tmp = (this.pointer.left + this.pointer.width);

				width = Math.max((this.cursor.right - e.pageX), 0);
				width = Math.min(width, tmp);

				left = tmp - width;
			}
			else if (this.stretchPosition.indexOf("right") >= 0)
			{
				width = Math.max((e.pageX - this.cursor.left), 0);
			}

			if (width !== null)
			{
				left = (left === null ? this.pointer.left : left);

				if (width <= 0)
				{
					width = 0;
				}
				else if (width >= (this.projection.pos.width - left))
				{
					width = (this.projection.pos.width - left);
				}
			}

			if (this.stretchPosition.indexOf("top") >= 0)
			{
				tmp = this.pointer.top + this.pointer.height;

				height = Math.max((this.cursor.bottom - e.pageY), 0);
				height = Math.min(height, tmp);

				top = tmp - height;
			}
			else if (this.stretchPosition.indexOf("bottom") >= 0)
			{
				height = Math.max((e.pageY - this.cursor.top), 0);
			}

			if (height !== null)
			{
				top = (top === null ? this.pointer.top : top);

				if (height <= 0)
				{
					height = 0;
				}
				else if (height >= (this.projection.pos.height - top))
				{
					height = (this.projection.pos.height - top);
				}
			}

			var strictMax = false;
			if (this.cropProportion.active === true)
			{
				if (width && height)
				{
					var oldValue;
					if (this.cropProportion["h/w"] * width > height)
					{
						oldValue = width;
						width = this.cropProportion["w/h"] * height;

						if (this.stretchPosition.indexOf("left") >= 0)
						{
							left += (oldValue - width);
						}
					}
					else
					{
						oldValue = height;
						height = this.cropProportion["h/w"] * width;
						if (this.stretchPosition.indexOf("top") >= 0)
						{
							top += (oldValue - height);
						}
					}

					if ((this.cropProportion["h/w"] == 1))
					{
						this.cropParams.left = Math.ceil(left / this.canvas.scale + this.canvas.visiblePart.left);
						this.cropParams.top = Math.ceil(top / this.canvas.scale + this.canvas.visiblePart.top);
						strictMax = Math.min(
							(this.canvas.width - this.cropParams.left),
							(this.canvas.height - this.cropParams.top)
						);
					}
				}
				else
				{
					width = null;
					height = null;
				}
			}

			if (width !== null)
			{
				this.cropParams.left = Math.ceil(left / this.canvas.scale + this.canvas.visiblePart.left);
				this.cropParams.maxWidth = (strictMax || (this.canvas.width - this.cropParams.left));
				this.cropParams.width = Math.ceil(width / this.canvas.scale);

				if (width <= 0)
				{
					this.cropParams.width = 0;
				}
				else if (this.cropParams.width >= this.cropParams.maxWidth)
				{
					this.cropParams.width = this.cropParams.maxWidth
				}
				this.setLeft(left, this.cropParams.left);
				this.setWidth(width, this.cropParams.width);
			}

			if (height !== null)
			{
				this.cropParams.top = Math.ceil(top / this.canvas.scale + this.canvas.visiblePart.top);
				this.cropParams.maxHeight = (strictMax || (this.canvas.height - this.cropParams.top));
				this.cropParams.height = Math.ceil(height / this.canvas.scale);

				if (height <= 0)
				{
					this.cropParams.height = 0;
				}
				else if (this.cropParams.height >= this.cropParams.maxHeight)
				{
					this.cropParams.height = this.cropParams.maxHeight;
				}

				this.setTop(top, this.cropParams.top);
				this.setHeight(height, this.cropParams.height);
			}
		}
	},
	stretchEnd : function()
	{
		this.hidePreventer();
		BX.unbind(document, "mousemove", this.stretch);
		BX.unbind(document, "mouseup", this.stretchEnd);
	},
	setWidth : function(visible, real)
	{
		this.pointer.width = visible;
		this.pointer.style.width = (visible + 'px');
		this.block.width =
		this.width.innerHTML = real;
		this.block.style.width = (real + 'px');

		this.overlayTop.style.width = (this.pointer.left + this.pointer.width) + 'px';
		this.overlayRight.style.left = (this.pointer.left + this.pointer.width) + 'px';

	},
	setLeft : function(visible, real)
	{
		this.pointer.style.left = visible + 'px';
		this.pointer.left = visible;
		this.block.style.left = real + 'px';

		this.overlayTop.style.width = (this.pointer.left + this.pointer.width) + 'px';
		this.overlayRight.style.left = (this.pointer.left + this.pointer.width) + 'px';
		this.overlayBottom.style.left = this.pointer.left + 'px';
		this.overlayLeft.style.width = this.pointer.left + 'px';
	},
	setHeight : function(visible, real)
	{
		this.pointer.height = visible;
		this.pointer.style.height = (visible+ 'px');
		this.block.height =
		this.height.innerHTML = real;
		this.block.style.height = (real + 'px');

		this.overlayRight.style.height = (this.pointer.top + visible) + 'px';
		this.overlayBottom.style.top = (this.pointer.top + visible) + 'px';
	},
	setTop : function(visible, real)
	{
		this.pointer.style.top = visible + 'px';
		this.pointer.top = visible;
		this.block.style.top = real + 'px';

		this.overlayTop.style.height = this.pointer.top + 'px';
		this.overlayRight.style.height = (this.pointer.top + this.pointer.height) + 'px';
		this.overlayBottom.style.top = (this.pointer.top + this.pointer.height) + 'px';
		this.overlayLeft.style.top = this.pointer.top + 'px';
	}
};
var CanvasMapMaster = function(block, params) {
	this.block = block;
	this.id = this.block.id + 'Map';
	if (BX(this.id + 'Root'))
	{
		this.root = BX(this.id + 'Root')
	}
	else
	{
		this.root = BX.create('DIV', {
			style : {
				display : "none",
				position : "absolute"
			},
			attrs : {
				id : this.id + 'Root',
				className : "adm-photoeditor-active-map-root"
			},
			html : [
				/*jshint multistr: true */
				'<div class="adm-photoeditor-active-map-block" id="', this.id, 'Block"> \
					<div class="adm-photoeditor-active-map"> \
						<div class="adm-photoeditor-active-map-image"> \
							<canvas id="', this.id, 'Canvas"></canvas> \
						</div> \
						<div class="adm-photoeditor-active-map-area"></div> \
						<div class="adm-photoeditor-active-map-pointer"></div> \
					</div> \
					<div class="adm-photoeditor-active-map-handle" id="', this.id, 'Resizer"> \
				</div> \
				</div>'
			].join("").replace(/[\n\t]/gi, '').replace(/>\s</gi, '><')
		} );
		document.body.appendChild(this.root);
		BX.ZIndexManager.register(this.root);
	}
	this.moveStart = this.moveStart.bind(this);
	this.move = this.move.bind(this);
	this.moveEnd = this.moveEnd.bind(this);

	this._bindNodes = this.bindNodes.bind(this);
	BX.defer_proxy(this._bindNodes)(params);
	BX.bind(window, "resize", BX.proxy(this.onResizeWindow, this));
	return this;
};
CanvasMapMaster.prototype = {
	bindNodesCounter : 0,
	bindNodes : function(params) {
		this.bindNodesCounter++;
		if (this.bindNodesCounter > 100 || !BX(this.id + 'Canvas'))
		{
			if (this.bindNodesCounter <= 100)
				BX.defer_proxy(this._bindNodes)(params);
			return;
		}

		this.canvasMapBlock = BX(this.id + 'Block');
		this.canvasMapResizer = BX(this.id + 'Resizer');
		BX.hide(this.canvasMapResizer); // TODO map-resize
		this.canvasMapCover = this.canvasMapBlock.firstChild;
		this.canvasMap = BX(this.id + 'Canvas');

		this.canvasMapPointer = this.canvasMapBlock.firstChild.childNodes[2];
		this.canvasMapPointer.params = params;

		BX.addClass(this.canvasMapPointer, "adm-photoeditor-active-map-pointer-draggable");
		BX.bind(this.canvasMapPointer, "mousedown", this.moveStart);
		BX.bind(this.canvasMapResizer, "mousedown", this.stretchStart);

		BX.onCustomEvent(this, "onMapIsInitialised", [this]);
	},
	init : function(canvas, canvasParams) {
		if (canvasParams)
		{
			var res = BX.UploaderUtils.scaleImage(canvasParams, this.canvasMapPointer.params);
			this.root.style.display = 'none';
			this.collapsedNode.style.display = 'none';
			BX.adjust(
				this.canvasMap, {
					props : res.destin
				}
			);
			this.canvasMap.pos = null;
			this.options.visible = false;

			BX.adjust(
				this.canvasMapCover, {
					style : {
						width : res.destin.width + 'px',
						height : res.destin.height + 'px'
					}
				}
			);
			BX.adjust(
				this.canvasMapPointer, {
					props : {
						width : res.destin.width,
						height : res.destin.height
					},
					style : {
						width : res.destin.width + 'px',
						height : res.destin.height + 'px'
					}
				}
			);
			this.canvasMap.canvasProp = res.coeff;
		}
		this.canvasMap.canvasWidth = canvas.width;
		this.canvasMap.canvasHeight = canvas.height;

		this.canvasMap.getContext("2d").drawImage(canvas,
			0, 0, canvas.width, canvas.height,
			0, 0, this.canvasMap.width, this.canvasMap.height
		);
	},
	options : {
		collapsed : false,
		collapsing : null,
		visible : false,
		busy : false,
		mode : "slow"
	},
	animationV: null,
	animationC: null,
	show : function() {
		if (this.options.visible)
			return;
		else if (this.animationV)
			this.animationV.stop();

		this.options.visible = true;

		var pos = BX.pos(this.block, false);

		this.root.pos = {
			top : pos.top,
			left : (pos.right - this.canvasMap.width),
			width : this.canvasMap.width,
			height : this.canvasMap.height
		};
		this.root.style.top = this.root.pos.top + 'px';
		this.root.style.left = this.root.pos.left + 'px';
		this.root.style.overflow = "hidden";
		this.root.style.width = this.root.pos.width + 'px';
		this.root.style.height = this.root.pos.height + 'px';
		this.root.style.display = (this.options.collapsed ? 'none' : 'block');
		this.collapsedNode.style.display = '';

		BX.ZIndexManager.bringToFront(this.root);

		this.canvasMapBlock.style.top = "-" + (this.canvasMap.height + 1) + 'px';
		this.canvasMapBlock.style.left = 0;
		this.canvasMapBlock.style.width = this.canvasMap.width + 'px';
		this.canvasMapBlock.style.height = this.canvasMap.height + 'px';

		this.canvasMapBlock.y = (this.canvasMapBlock.y || this.canvasMap.height);
		this.canvasMapBlock.x = (this.canvasMapBlock.x || this.canvasMap.width);

		this.animationV = new BX.easing({
			duration : 200,
			start : { y : this.canvasMapBlock.y, x : this.canvasMapBlock.x},
			finish : { y : 0, x : 0},
			step : BX.delegate(function(state){
				this.canvasMapBlock.y = state.y;
				this.canvasMapBlock.style.top = "-" + state.y + "px";
			}, this),
			complete : BX.delegate(function(){
				this.canvasMapBlock.y = false;
				this.canvasMapBlock.x = false;
				this.canvasMapBlock.style.top = 0;
				this.canvasMapBlock.style.left = 0;
				this.root.style.overflow = "visible";
				this.animationV = null;
			}, this)
		});
		this.animationV.animate();
	},
	hide : function(slow) {
		var func = BX.delegate(function(){
			this.root.style.display = 'none';
			this.collapsedNode.style.display = 'none';
			delete this.root.pos;
			this.canvasMapBlock.y = false;
			this.canvasMapBlock.x = false;
			this.animationV = null;
		}, this);
		if (slow !== true)
		{
			if (this.animationV)
				this.animationV.stop();
			this.options.visible = false;
			func();
		}
		else if (this.options.visible)
		{
			if (this.animationV)
				this.animationV.stop();
			this.options.visible = false;

			this.root.style.overflow = "hidden";

			this.canvasMapBlock.y = (this.canvasMapBlock.y || 0);
			this.canvasMapBlock.x = (this.canvasMapBlock.x || 0);

			this.animationV = new BX.easing({
				duration : 200,
				start : { y : this.canvasMapBlock.y, x : this.canvasMapBlock.x},
				finish : { y : this.canvasMap.height, x : this.canvasMap.width},
				step : BX.delegate(function(state){
					this.canvasMapBlock.y = state.y;
					this.canvasMapBlock.style.top = "-" + state.y + "px";
					this.canvasMapBlock.x = state.x;
				}, this),
				complete : func
			});
			this.animationV.animate();
		}
		else
		{
			func();
		}
	},
	zoom : function(visPart) {
		var show = (visPart && (
			visPart["~top"] > 0 ||
			visPart["~left"] > 0 ||
			visPart["~width"] < this.canvasMap.canvasWidth ||
			visPart["~height"] < this.canvasMap.canvasHeight));
		if (show)
		{
			var shiftX = Math.ceil(visPart["~left"] * this.canvasMap.canvasProp),
				shiftY = Math.ceil(visPart["~top"] * this.canvasMap.canvasProp),
				width = Math.min(Math.ceil(visPart["~width"] * this.canvasMap.canvasProp), (this.canvasMap.width - shiftX)),
				height = Math.min(Math.ceil(visPart["~height"] * this.canvasMap.canvasProp), (this.canvasMap.height - shiftY));
			BX.adjust(
				this.canvasMapPointer, {
					props : {
						etalonWidth : Math.ceil(visPart["~etalonWidth"] * this.canvasMap.canvasProp),
						etalonHeight : Math.ceil(visPart["~etalonHeight"] * this.canvasMap.canvasProp),
						'bx-left' : shiftX,
						'bx-top' : shiftY,
						width : width,
						height : height
					},
					style : {
						width : width + 'px',
						height : height + 'px',
						transform : 'translate3d(' + shiftX + 'px, ' + shiftY + 'px, 0)'
					}
				}
			);
		}
		else
		{
			BX.adjust(
				this.canvasMapPointer, {
					props : {
						etalonWidth : this.canvasMap.width,
						etalonHeight : this.canvasMap.height,
						'bx-left' : 0,
						'bx-top' : 0,
						width : this.canvasMap.width,
						height : this.canvasMap.height
					},
					style : {
						width : this.canvasMap.width + 'px',
						height : this.canvasMap.height + 'px',
						transform : 'translate3d(0, 0, 0)'
					}
				}
			);
		}
	},
	shiftX : 0,
	shiftY : 0,
	moveEventTimeout : 0,
	moveEventParams : {top : 0, left : 0},
	moveCursor : null,
	wSize : null,
	moveStart : function(e) {
		this.moveCursor = null;

		if (e.target == this.canvasMapPointer)
		{
			if (this.canvasMap.pos === null)
				this.canvasMap.pos = BX.pos(this.canvasMap);

			this.wSize = BX.GetWindowSize();

			this.moveCursor = {
				deltaX : this.canvasMap.pos['left'] + (this.canvasMapPointer['bx-left'] || 0),
				deltaY : this.canvasMap.pos['top'] + (this.canvasMapPointer['bx-top'] || 0)
			};
			this.moveCursor.x = e.clientX + this.wSize.scrollLeft - this.moveCursor.deltaX;
			this.moveCursor.y = e.clientY + this.wSize.scrollTop - this.moveCursor.deltaY;

			BX.bind(document, "mousemove", this.move);
			BX.bind(document, "mouseup", this.moveEnd);
		}
	},
	move : function(e)
	{
		if (this.moveCursor === null)
			return;

		var deltaX = e.clientX + this.wSize.scrollLeft,
			deltaY = e.clientY + this.wSize.scrollTop,
			width = parseInt(this.canvasMapPointer.etalonWidth),
			height = parseInt(this.canvasMapPointer.etalonHeight),
			right = false,
			bottom = false;

		deltaX -= (this.canvasMap.pos['left'] + this.moveCursor.x);
		if (deltaX <= 0)
			deltaX = 0;
		else if (deltaX > (this.canvasMap.width - this.canvasMapPointer.width))
		{
			deltaX = (this.canvasMap.width - this.canvasMapPointer.width);
			right = true;
		}

		deltaY -= (this.canvasMap.pos['top'] + this.moveCursor.y);
		if (deltaY <= 0)
			deltaY = 0;
		else if (deltaY > (this.canvasMap.height - this.canvasMapPointer.height))
		{
			deltaY = (this.canvasMap.height - this.canvasMapPointer.height);
			bottom = true;
		}

		if (deltaX != this.shiftX || deltaY != this.shiftY)
		{
			this.shiftX = deltaX;
			this.shiftY = deltaY;
			if (width > this.canvasMapPointer.width && (width + deltaX) > this.canvasMap.width)
				width = this.canvasMap.width - deltaX;
			if (height > this.canvasMapPointer.height && (height + deltaY) > this.canvasMap.height)
				height = this.canvasMap.height - deltaY;

			BX.adjust(
				this.canvasMapPointer, {
					props : {
						'bx-left' : deltaX,
						'bx-top' : deltaY,
						width : width,
						height : height
					},
					style : {
						width : width + 'px',
						height : height + 'px',
						transform : 'translate3d(' + deltaX + 'px, ' + deltaY + 'px, 0)'
					}
				}
			);
			clearTimeout(this.moveEventTimeout);
			this.moveEventParams = {
				'%left' : deltaX / this.canvasMap.width,
				'%top' : deltaY / this.canvasMap.height,
				left : deltaX / this.canvasMap.canvasProp,
				top : deltaY / this.canvasMap.canvasProp,
				right : right,
				bottom : bottom,
				width : width / this.canvasMap.canvasProp,
				height : height / this.canvasMap.canvasProp
			};
			if (!this['moveEventFunction'])
				this['moveEventFunction'] = BX.delegate(function(){
					BX.onCustomEvent(this, 'onMapPointerIsMoved', [this.moveEventParams]);
				}, this);
			this.moveEventTimeout = setTimeout(this.moveEventFunction, 100);
		}
	},
	moveEnd : function()
	{
		BX.unbind(document, "mousemove", this.move);
		BX.unbind(document, "mouseup", this.moveEnd);
	},
	collapsedNode : null,
	registerCollapsedNode : function(node)
	{
		if (BX(node))
		{
			this.collapsedNode = BX(node);
			BX.bind(this.collapsedNode, "click", BX.delegate(function(){
					if (this.options.collapsed === true)
					{
						this.collapse(false);
					}
					else
					{
						this.collapse(true);
					}
			}, this));

			BX.bind(this.canvasMapBlock, "dblclick", this.collapse.bind(this));
		}
		this.collapseEnd();
	},
	collapseEnd : function()
	{
		if (this.options.collapsed)
		{
			this.root.style.display = 'none';
			if (this.collapsedNode)
				BX.removeClass(this.collapsedNode, "disabled");
			BX.removeClass(this.root, "collapse");
			BX.addClass(this.root, "collapse2");
		}
		else
		{
			this.root.style.display = 'block';

			BX.ZIndexManager.bringToFront(this.root);

			if (this.collapsedNode)
				BX.addClass(this.collapsedNode, "disabled");
			BX.addClass(this.root, "collapse");
			BX.removeClass(this.root, "collapse2");
		}

		this.root.style.transform = 'translate(0, 0) scale(1, 1)';
		this.root.style.opacity = '1';

		if (this.root["~top"])
		{
			this.root.style.top = this.root["~top"];
			delete this.root["~top"];
		}


		this.options.collapsing = null;
	},
	collapse : function(collapse)
	{
		collapse = (collapse === true || collapse === false ? collapse : (!this.options.collapsed));
		if (this.options.collapsing !== null || this.options.collapsed == collapse)
			return;

		this.options.collapsing = true;
		this.options.collapsed = collapse;
		var posB, pos, shiftX, scale;
		if (collapse)
		{
			if (!this.collapsedNode || !this.root.pos)
			{
				this.collapseEnd();
			}
			else
			{
				posB = BX.pos(this.collapsedNode, false);
				pos = this.root.pos;
				shiftX = Math.ceil((posB.left + posB.width / 2 ) - (pos.left + pos.width / 2));
				scale = (posB.width * 0.7)/pos.width;

				this.root.style.transform = 'translate(' + shiftX + 'px, 0) scale(' + scale + ', ' + scale + ')';
				this.root.style.opacity = '0';
				this.root["~top"] = this.root.style.top;
				this.root.style.top = posB.top + 'px';

				setTimeout(BX.proxy(this.collapseEnd, this), 700);
			}
		}
		else if (this.root.pos)
		{
			this.collapseEnd();
			/*if (!this.collapsedNode)
			{
				this.collapseEnd();
			}
			else
			{
				posB = BX.pos(this.collapsedNode, false);
				pos = this.root.pos;
				shiftX = Math.ceil((posB.left + posB.width / 2 ) - (pos.left + pos.width / 2));
				scale = (posB.width * 0.7)/pos.width;
				BX.removeClass(this.root, "collapse2");

				this.root.style.opacity = '0';
				this.root.style.display = 'block';
				this.root.style.transform = 'translate(' + shiftX + 'px, 0) scale(' + scale + ', ' + scale + ')';
				this.root["~top"] = this.root.style.top;
				this.root.style.top = posB.top + 'px';

				BX.addClass(this.root, "collapse2");

				this.root.style.transform = 'translate(0, 0) scale(1, 1)';
				this.root.style.opacity = '1';
				this.root.style.top = this.root["~top"];

				setTimeout(BX.proxy(this.collapseEnd, this), 1000);
			}*/
		}
	},
	stretchStart : function()
	{

	},
	occupy : function()
	{
		this.options.busy = true;
	},
	release : function()
	{
		this.options.busy = false;
	},
	onResizeWindow : function()
	{
		if (this.options.visible)
		{
			var pos = BX.pos(this.block, false);
			this.root.pos.top = pos.top;
			this.root.pos.left = pos.left;
			this.root.style.top = pos.top + 'px';
			this.root.style.left = (pos.right - this.canvasMap.width) + 'px';
		}
	}
};
var frameMaster = new FrameMaster();

})();

Youez - 2016 - github.com/yon3zu
LinuXploit