Your IP : 18.216.172.133


Current Path : /home/bitrix/ext_www/dev.ballu.in.ua/bitrix/js/main/core/core_uploader/
Upload File :
Current File : /home/bitrix/ext_www/dev.ballu.in.ua/bitrix/js/main/core/core_uploader/uploader.js

;(function(window){

	if (window.BX["Uploader"])
		return;
	var
		BX = window.BX,
		statuses = { "new" : 0, ready : 1, preparing : 2, inprogress : 3, done : 4, failed : 5, error : 5.2, stopped : 6, changed : 7, uploaded : 8},
		repo = {},
		settings = {
			phpPostMinSize : 5.5 * 1024 * 1024, // Bytes
			phpUploadMaxFilesize : 5 * 1024 * 1024, // Bytes 5MB because of Cloud
			phpPostMaxSize : 11 * 1024 * 1024, // Bytes
			estimatedTimeForUploadFile : 10 * 60, // in sec
			maxTimeForUploadFile : 15 * 60, // in sec
			maxSize : null
		};
	/**
	 * @params array
	 * @params[input] - BX(id).
	 *  DOM-node with id="uploader_somehash" should exist and will be replaced	 *
	 * @params[dropZone] - DOM node to drag&drop
	 * @params[placeHolder] - DOM node to append files
	 *
	 */
	BX.Uploader = function(params)
	{
		if (settings.maxSize === null && BX.message["bxDiskQuota"] && BX.message("bxDiskQuota"))
			settings.maxSize = parseInt(BX.message("bxDiskQuota"));
		var ii;
		if (!(typeof params == "object" && params && (BX(params["input"]) || params["input"] === null)))
		{
			BX.debug(BX.message("UPLOADER_INPUT_IS_NOT_DEFINED"));
		}
		else
		{
			if (parseInt(BX.message("phpMaxFileUploads")) <= 0)
				ii = {phpMaxFileUploads : '20'};
			if (parseInt(BX.message('phpPostMaxSize')) <= 0)
			{
				ii = (ii || {});
				ii["phpPostMaxSize"] = settings.phpPostMaxSize + '';
			}
			if (parseInt(BX.message('phpUploadMaxFilesize')) <= 0)
			{
				ii = (ii || {});
				ii["phpUploadMaxFilesize"] = settings.phpUploadMaxFilesize + '';
			}
			if (ii)
				BX.message(ii);

			this.fileInput = (params["input"] === null ? null : BX(params["input"]));
			this.controlID = this.controlId = (params["controlId"] || "bitrixUploader");

			this.dialogName = "BX.Uploader";
			this.id = (BX.type.isNotEmptyString(params["id"]) ? params["id"] : Math.random());
			this.CID = (params["CID"] && BX.type.isNotEmptyString(params["CID"]) ? params["CID"] : ("CID" + BX.UploaderUtils.getId()));
			this.streams = new BX.UploaderStreams(params['streams'], this);

			// Limits
			this.limits = {
				phpMaxFileUploads : parseInt(BX.message('phpMaxFileUploads')),
				phpPostMaxSize : Math.min(parseInt(BX.message('phpPostMaxSize')), settings.phpPostMaxSize),
				phpUploadMaxFilesize : Math.min(parseInt(BX.message('phpUploadMaxFilesize')), settings.phpUploadMaxFilesize),
				uploadMaxFilesize : (params["uploadMaxFilesize"] && params["uploadMaxFilesize"] > 0 ? params["uploadMaxFilesize"] : 0),
				uploadFileWidth : (params["uploadFileWidth"] && params["uploadFileWidth"] > 0 ? params["uploadFileWidth"] : 0),
				uploadFileHeight : (params["uploadFileHeight"] && params["uploadFileHeight"] > 0 ? params["uploadFileHeight"] : 0),
				allowUpload : ((params["allowUpload"] == "A" || params["allowUpload"] == "I" || params["allowUpload"] == "F") ? params["allowUpload"] : "A"),
				allowUploadExt : (typeof params["allowUploadExt"] === "string" ? params["allowUploadExt"] : "")};
			var keys = ["phpMaxFileUploads", "phpPostMaxSize", "phpUploadMaxFilesize"];
			for (ii = 0; ii < keys.length; ii++)
			{
				this.limits[keys[ii]] = (typeof params[keys[ii]] == "number" && params[keys[ii]] < this.limits[keys[ii]] ? params[keys[ii]] : this.limits[keys[ii]]);
			}
			this.limits["phpPostSize"] = Math.min(this.limits["phpPostMaxSize"], settings.phpPostMinSize);

	// ALLOW_UPLOAD = 'A'll files | 'I'mages | 'F'iles with selected extensions
	// ALLOW_UPLOAD_EXT = comma-separated list of allowed file extensions (ALLOW_UPLOAD='F')
			this.limits["uploadFile"] = (params["allowUpload"] == "I" ? "image/*" : "");
			this.limits["uploadFileExt"] = this.limits["allowUploadExt"];

			if (this.limits["uploadFileExt"].length > 0)
			{
				var ext = this.limits["uploadFileExt"].split(this.limits["uploadFileExt"].indexOf(",") >= 0 ? "," : " ");
				for (ii = 0; ii < ext.length; ii++)
					ext[ii] = (ext[ii].charAt(0) == "." ? ext[ii].substr(1) : ext[ii]);
				this.limits["uploadFileExt"] = ext.join(",");
			}
			this.params = params;

			this.params["filesInputName"] = (this.fileInput && this.fileInput["name"] ? this.fileInput["name"] : "FILES");
			this.params["filesInputMultiple"] = (this.fileInput && this.fileInput["multiple"] || this.params["filesInputMultiple"] ? "multiple" : false);
			this.params["uploadFormData"] = (this.params["uploadFormData"] == "N" ? "N" : "Y");
			this.params["uploadMethod"] = (this.params["uploadMethod"] == "immediate" ? "immediate" : "deferred"); // Should we start upload immediately or by event
			this.params["uploadFilesForPackage"] = parseInt(this.params["uploadFilesForPackage"] > 0 ? this.params["uploadFilesForPackage"] : 0);
			this.params["imageExt"] = "jpg,bmp,jpeg,jpe,gif,png";
			this.params["uploadInputName"] = (!!this.params["uploadInputName"] ? this.params["uploadInputName"] : "bxu_files");
			this.params["uploadInputInfoName"] = (!!this.params["uploadInputInfoName"] ? this.params["uploadInputInfoName"] : "bxu_info");
			this.params["deleteFileOnServer"] = !(this.params["deleteFileOnServer"] === false || this.params["deleteFileOnServer"] === "N");
			this.params["pasteFileHashInForm"] = !(this.params["pasteFileHashInForm"] === false || this.params["pasteFileHashInForm"] === "N");

			repo[this.id] = this;
			if (this.init(this.fileInput)) // init fileInput
			{
				if (!!params["dropZone"])
					this.initDropZone(BX(params["dropZone"]));

				if (!!params["events"])
				{
					for(ii in params["events"])
					{
						if (params["events"].hasOwnProperty(ii))
						{
							BX.UploaderUtils.bindEvents(this, ii, params["events"][ii]);
						}
					}
				}
				this.uploadFileUrl = (!!params["uploadFileUrl"] ? params["uploadFileUrl"] : (this.form ? this.form.getAttribute("action") : ""));
				if (!this.uploadFileUrl || this.uploadFileUrl.length <= 0)
				{
					BX.debug(BX.message("UPLOADER_ACTION_URL_NOT_DEFINED"));
				}
				this.status = statuses.ready;


				/* This params only for files. They are here for easy way to change them */
				this.fileFields = params["fields"];
				this.fileCopies = params["copies"];
				var queueFields = (!!params["queueFields"] ? params["queueFields"] : {});
				queueFields["placeHolder"] = BX(queueFields["placeHolder"] || params["placeHolder"]);
				queueFields["showImage"] = (queueFields["showImage"] || params["showImage"]);
				queueFields["sortItems"] = (queueFields["sortItems"] || params["sortItems"]);
				queueFields["thumb"] = (queueFields["thumb"] || params["thumb"]);
				this.queue = new BX.UploaderQueue(queueFields, this.limits, this);

				this.params["doWeHaveStorage"] = true;
				BX.addCustomEvent(this, 'onDone', BX.delegate(function(){
					this.init(this.fileInput);
				}, this));
				if (!!this.params["filesInputName"] && this.params["pasteFileHashInForm"])
				{
					BX.addCustomEvent(this, 'onFileIsUploaded', BX.delegate(function(id, item){
						var node = BX.create("INPUT", {props : { type : "hidden", name : this.params["filesInputName"] + '[]', value : item.hash }});
						if (BX(params["placeHolder"]) && BX(id + 'Item'))
							BX(id + 'Item').appendChild(node);
						else if (this.fileInput !== null)
							this.fileInput.parentNode.insertBefore(node, this.fileInput);
					}, this));
				}
				if (this.params["deleteFileOnServer"])
				{
					BX.addCustomEvent(this, 'onFileIsDeleted', BX.delegate(function(id, file){
						if (!!file && !!file.hash)
						{
							var data = this.preparePost({mode : "delete", hash : file.hash}, false);
							BX.ajax.get(
								this.uploadFileUrl,
								data.data
							);
						}
					}, this));
				}
				BX.onCustomEvent(window, "onUploaderIsInited", [this.id, this]);
				this.uploads = new BX.UploaderUtils.Hash();
				this.upload = null;
				if (this.params["bindBeforeUnload"] === false)
				{
					this.__beforeunload = BX.delegate(this.terminate, this);
				}
				else
				{
					this.__beforeunload = BX.delegate(function(e) {
						if (this.uploads && this.uploads.length > 0)
						{
							var confirmationMessage = BX.message("UPLOADER_UPLOADING_ONBEFOREUNLOAD");
							(e || window.event).returnValue = confirmationMessage;
							return confirmationMessage;
						}
					}, this);
				}
				BX.bind(window, 'beforeunload', this.__beforeunload);
			}
		}
	};

	BX.Uploader.prototype = {
		init : function(fileInput)
		{
			this.log('input is initialized');
			if (BX(fileInput))
			{

				if (fileInput == this.fileInput && !this.form)
					this.form = this.fileInput.form;

				if (fileInput == this.fileInput)
					fileInput = this.fileInput = this.mkFileInput(fileInput);
				else
					fileInput = this.mkFileInput(fileInput);

				BX.onCustomEvent(this, "onFileinputIsReinited", [fileInput, this]);

				if (fileInput)
				{
					BX.bind(fileInput, "change", BX.delegate(this.onChange, this));
					return true;
				}
			}
			else if (fileInput === null && this.fileInput === null)
			{
				this.log('Initialized && null');
				return true;
			}
			return false;
		},
		destruct : function () {
			this.releaseDropZone();
		},
		log : function(text)
		{
			BX.UploaderUtils.log('uploader', text);
		},
		initDropZone : function(node)
		{
			var dropZone = null;
			if (!!BX.DD && BX.type.isDomNode(node) && node.parentNode)
			{
				dropZone = new BX.DD.dropFiles(node);
				if (dropZone && dropZone.supported() && BX.ajax.FormData.isSupported()) {
					dropZone.f = {
						dropFiles : BX.delegate(function(files, e) {
							if (e && e["dataTransfer"] && e["dataTransfer"]["items"] && e["dataTransfer"]["items"].length > 0)
							{
								var dt = e["dataTransfer"], ii, entry, fileCopy = [], replace = false;
								for (ii = 0; ii < dt["items"].length; ii++)
								{
									if (dt["items"][ii]["webkitGetAsEntry"] && dt["items"][ii]["getAsFile"])
									{
										replace = true;
										entry = dt["items"][ii]["webkitGetAsEntry"]();
										if (entry && entry.isFile)
										{
											fileCopy.push(dt["items"][ii]["getAsFile"]());
										}
									}
								}
								if (replace)
									files = fileCopy;
							}
							this.onChange(files);
						}, this),
						dragEnter : function(e) {
							var isFileTransfer = false;
							if (e && e["dataTransfer"] && e["dataTransfer"]["types"])
							{
								for (var i = 0; i < e["dataTransfer"]["types"].length; i++)
								{
									if (e["dataTransfer"]["types"][i] === "Files")
									{
										isFileTransfer = true;
										break;
									}
								}
							}
							if (isFileTransfer)
								BX.addClass(dropZone.DIV, "bxu-file-input-over");
						},
						dragLeave : function() { BX.removeClass(dropZone.DIV, "bxu-file-input-over"); }
					};
					BX.addCustomEvent(dropZone, 'dropFiles', dropZone.f.dropFiles);
					BX.addCustomEvent(dropZone, 'dragEnter', dropZone.f.dragEnter);
					BX.addCustomEvent(dropZone, 'dragLeave' , dropZone.f.dragLeave);
				}
				if (this.params["dropZone"] == node)
				{
					this.dropZone = dropZone;
				}
			}
			return dropZone;
		},
		releaseDropZone : function() {
			if (this.dropZone)
			{
				BX.unbindAll(this.dropZone.DIV);
				this.dropZone.DIV.removeAttribute('dropzone');

				BX.removeCustomEvent(this.dropZone, 'dropFiles', this.dropZone.f.dropFiles);
				BX.removeCustomEvent(this.dropZone, 'dragEnter', this.dropZone.f.dragEnter);
				BX.removeCustomEvent(this.dropZone, 'dragLeave' , this.dropZone.f.dragLeave);
				delete this.dropZone.f.dropFiles;
				delete this.dropZone.f.dragEnter;
				delete this.dropZone.f.dragLeave;
				delete this.dropZone._cancelLeave;
				delete this.dropZone._prepareLeave;

				delete this.dropZone;
			}
		},
		onAttach : function(files, nodes, check)
		{
			check = (check !== false);
			if (typeof files !== "undefined" && files.length > 0)
			{
				if (!this.params["doWeHaveStorage"])
					this.queue.clear();

				if (!BX.type.isArray(files)) // FileList
				{
					var result = [];
					for (var j=0; j < files.length; j++)
					{
						result.push(files[j]);
					}
					files = result;
				}

				BX.onCustomEvent(this, "onAttachFiles", [files, nodes, this]);

				var added = false, ext, type;

				nodes = (typeof nodes == "object" && !!nodes && nodes.length > 0 ? nodes : []);

				for (var i=0, f; i < files.length; i++)
				{
					f = files[i];
					if (BX(f) && f.value)
					{
						ext = (f.value.name || '').split('.').pop();
					}
					else
					{
						ext = (f['name'] || f['tmp_url'] || '').split('.').pop();
						if (ext.indexOf('?') > 0)
							ext = ext.substr(0, ext.indexOf('?'));
					}

					ext = (BX.type.isNotEmptyString(ext) ? ext : '').toLowerCase();
					type = (BX.type.isNotEmptyString(f['type']) ? f['type'] : '').toLowerCase();

					if (
						check &&
						(
							(
								this.limits["uploadFile"] == "image/*" &&
								(
									(BX.type.isNotEmptyString(type) && type.indexOf("image/") !== 0) ||
									(!BX.type.isNotEmptyString(type) && this.params["imageExt"].indexOf(ext) < 0)
								)
							) ||
							(
								this.limits["uploadFileExt"].length > 0 && this.limits["uploadFileExt"].indexOf(ext) < 0
							)
						)
					)
					{
						continue;
					}
					BX.onCustomEvent(this, "onItemIsAdded", [f, (nodes[i] || null), this]);
					added = true;
				}
				if (added)
				{
					BX.onCustomEvent(this, "onItemsAreAdded", [this]);
					if (this.params["uploadMethod"] == "immediate")
						this.submit();
				}
			}
			return false;
		},
		onChange : function(fileInput)
		{
			BX.onCustomEvent(this, "onFileinputWillBeChanged", [fileInput, this]);
			BX.PreventDefault(fileInput);

			var files = fileInput;
			if (fileInput && fileInput.target)
				files = fileInput.target.files;
			else if (!fileInput && BX(this.fileInput))
				files = this.fileInput.files;

			if (BX(this.fileInput) && this.fileInput.disabled)
			{
				BX.DoNothing();
			}
			else
			{
				BX.onCustomEvent(this, "onFileinputIsChanged", [fileInput, this]);
				this.init((fileInput && fileInput["target"] ? fileInput.target : fileInput));
				this.onAttach(files);
			}

			return false;
		},
		mkFileInput : function(oldNode)
		{
			if (!BX(oldNode))
				return false;
			BX.unbindAll(oldNode);
			var node = oldNode.cloneNode(true);
			BX.adjust(node, {
				props : {
					value : ""
				},
				attrs: {
					name: (this.params["uploadInputName"] + '[]'),
					multiple : this.params["filesInputMultiple"],
					accept : this.limits["uploadFile"],
					value : ""
			}});
			oldNode.parentNode.insertBefore(node, oldNode);
			oldNode.parentNode.removeChild(oldNode);
			return node;
		},
		preparePost : function(data, prepareForm)
		{
			if (prepareForm === true && this.params["uploadFormData"] == "Y" && !this.post)
			{
				var post2 = {data : {"AJAX_POST" : "Y", SITE_ID : BX.message("SITE_ID"), USER_ID : BX.message("USER_ID")}, filesCount : 0, size : 10};
				post2 = (this.form ? BX.UploaderUtils.FormToArray(this.form, post2) : post2);
				if (!!post2.data[this.params["filesInputName"]])
				{
					post2.data[this.params["filesInputName"]] = null;
					delete post2.data[this.params["filesInputName"]];
				}
				if (!!post2.data[this.params["uploadInputInfoName"]])
				{
					post2.data[this.params["uploadInputInfoName"]] = null;
					delete post2.data[this.params["uploadInputInfoName"]];
				}
				if (!!post2.data[this.params["uploadInputName"]])
				{
					post2.filesCount -= post2.data[this.params["uploadInputName"]].length;
					post2.data[this.params["uploadInputName"]] = null;
					delete post2.data[this.params["uploadInputName"]];
				}
				if (this.limits["phpMaxFileUploads"] <= post2.filesCount)
				{
					BX.debug('You can not upload any file from your list.');
					return false;
				}
				post2.size = BX.UploaderUtils.sizeof(post2.data);
				this.post = post2;
			}
			var post = (prepareForm === true && this.params["uploadFormData"] == "Y" ? this.post : {data : {"AJAX_POST" : "Y", SITE_ID : BX.message("SITE_ID"), USER_ID : BX.message("USER_ID")}, filesCount : 0, size : 10}), size = 0;
			post.data["sessid"] = BX.bitrix_sessid();
			post.size += (6 + BX.bitrix_sessid().length);
			if (data)
			{
				post.data[this.params["uploadInputInfoName"]] = {
					controlId : this.controlId,
					CID : this.CID,
					inputName : this.params["uploadInputName"],
					version : BX.Uploader.getVersion()
				};
				for (var ii in data)
				{
					if (data.hasOwnProperty(ii))
					{
						post.data[this.params["uploadInputInfoName"]][ii] = data[ii];
					}
				}
				size = BX.UploaderUtils.sizeof(this.params["uploadInputInfoName"]) + BX.UploaderUtils.sizeof(post.data[this.params["uploadInputInfoName"]]);
			}
			post.length = post.size + size;
			return post;
		},
		submit : function()
		{
			this.start();
		},
		stop : function()
		{
			this.terminate();
		},
		adjustProcess : function(streamId, item, status, params, pIndex)
		{
			var text = '', percent = 0;
			if (this.queue.itFailed.hasItem(item.id))
			{
				text = 'response [we do not work with errors]';
			}
			else if (status == statuses.error)
			{
				delete item.progress;
				this.queue.itFailed.setItem(item.id, item);
				this.queue.itForUpload.removeItem(item.id);

				BX.onCustomEvent(this, "onFileIsUploadedWithError", [item.id, item, params, this, pIndex]);
				BX.onCustomEvent(item, "onUploadError", [item, params, this, pIndex]);
				text = 'response [error]';
			}
			else if (status == statuses.uploaded)
			{
				delete item.progress;
				this.queue.itUploaded.setItem(item.id, item);
				this.queue.itForUpload.removeItem(item.id);

				BX.onCustomEvent(this, "onFileIsUploaded", [item.id, item, params, this, pIndex]);
				BX.onCustomEvent(item, "onUploadDone", [item, params, this, pIndex]);
				text = 'response [uploaded]';
			}
			else if (status == statuses.inprogress)
			{
				if (typeof params == "number")
				{
					if (params == 0 && item.progress.status == statuses["new"])
					{
						BX.onCustomEvent(item, "onUploadStart", [item, 0, this, pIndex]);
						item.progress.status = statuses.inprogress;
					}

					percent = item.progress.uploaded + (item.progress.streams[streamId] * params) / 100;
				}
				else
				{
					item.progress.uploaded += item.progress.streams[streamId];
					item.progress.streams[streamId] = 0;
					percent = item.progress.uploaded;
				}
				text = 'response [uploading]. Uploaded: ' + percent;
				BX.onCustomEvent(item, "onUploadProgress", [item, percent, this, pIndex]);
			}
			else if (status == statuses.failed)
			{
				if (item.progress.streams[streamId] == item.progress.percentPerChunk)
				{
					item.progress = null;
					delete item.progress;
				}
				else
				{
					item.progress.streams[streamId] -= item.progress.percentPerChunk / params.packages;
					item.progress.streams[streamId] = (item.progress.streams[streamId] > 0 ? item.progress.streams[streamId] : 0);
				}
			}
			else
			{
				if (status == statuses["new"])
				{
					var chunks = (item.getThumbs("getCount") > 0 ? item.getThumbs("getCount") : 0)
						+ 2;// props + (default canvas || file)

					item.progress = {
						percentPerChunk : (100 / chunks),
						streams : {},
						uploaded : 0,
						status : statuses["new"]
					};
					item.progress.streams[streamId] = item.progress.percentPerChunk;
					text = 'request preparing [start]. Prepared: ' + item.progress.streams[streamId];
				}
				else if (status == statuses.preparing)
				{
					item.progress.streams[streamId] = (item.progress.streams[streamId] > 0 ? item.progress.streams[streamId] : 0);
					item.progress.streams[streamId] += item.progress.percentPerChunk / params.packages;
					text += 'request preparing [cont]. Prepared: ' + item.progress.streams[streamId];
				}
				else
				{
					text = 'request preparing [finish]. ';
				}
				BX.onCustomEvent(item, "onUploadPrepared", [item, params, this, pIndex]);
			}
			this.log(item.name + ': ' + text);
		},
		terminate : function(pIndex)
		{
			var packageFormer, packagesFormer;
			if (!pIndex || pIndex == 'beforeunload')
			{
				packagesFormer = this.uploads;

				this.uploads = new BX.UploaderUtils.Hash();
				this.upload = null;

				while ((packageFormer = packagesFormer.getFirst()) && packageFormer)
				{
					packagesFormer.removeItem(packageFormer.id);
					this.terminate(packageFormer);
				}
				return;
			}
			else if (BX.type.isNotEmptyString(pIndex))
			{
				packageFormer = this.uploads.removeItem(pIndex);
			}
			else if (typeof pIndex == "object")
			{
				packageFormer = pIndex;
			}
			if (packageFormer && packageFormer["stop"])
			{
				packageFormer.stop();
				this.log(packageFormer.id + ' Uploading is canceled');
				BX.onCustomEvent(this, 'onTerminated', [packageFormer.id, packageFormer]);
			}
		},
		start : function()
		{
			if (this.queue.itForUpload.length <= 0)
			{
				BX.onCustomEvent(this, 'onStart', [null, {filesCount : 0}, this]);
				BX.onCustomEvent(this, 'onDone', [null, null, {filesCount : 0}]);
				BX.onCustomEvent(this, 'onFinish', [null, null, {filesCount : 0}]);
				return;
			}

			var pIndex = 'pIndex' + BX.UploaderUtils.getId(), queue = this.queue.itForUpload;
			this.queue.itForUpload = new BX.UploaderUtils.Hash();
			this.post = false;
			this.log('create new package ' + pIndex);
			var packageFormer = new BX.UploaderPackage({
				id : pIndex,
				data : queue,
				post : this.preparePost({}, true),
				uploadFileUrl : this.uploadFileUrl,
				limits : this.limits,
				params : this.params
			}, this);
			BX.addCustomEvent(packageFormer, 'adjustProcess', BX.proxy(this.adjustProcess, this));
			BX.addCustomEvent(packageFormer, 'startStream', BX.proxy(function(stream, pack, files){ BX.onCustomEvent(this, 'startPackage', [stream, pack.id, files]); }, this));
			BX.addCustomEvent(packageFormer, 'progressStream', BX.proxy(function(stream, pack, proc){ BX.onCustomEvent(this, 'processPackage', [stream, pack.id, proc]); }, this));
			BX.addCustomEvent(packageFormer, 'doneStream', BX.proxy(function(stream, pack, data){ BX.onCustomEvent(this, 'donePackage', [stream, pack.id, data]); }, this));
			BX.addCustomEvent(packageFormer, 'stopPackage', BX.proxy(function(pack){
				this.log('restore files: '+ pack.data.length);
				this.queue.restoreFiles(pack.data);
			}, this));
			BX.addCustomEvent(packageFormer, 'donePackage', BX.proxy(function(stream, pack, data){
				BX.onCustomEvent(this, 'onDone', [stream, pack.id, pack, data]);
				var res = this.checkUploads(pack.id);
				if (!res)
					BX.onCustomEvent(this, 'onFinish', [stream, pack.id, pack, data]);
			}, this));
			BX.addCustomEvent(packageFormer, 'errorPackage', BX.proxy(function(stream, pack, data){
				BX.onCustomEvent(this, 'error', [stream, pIndex, data]);
				BX.onCustomEvent(this, 'onError', [stream, pIndex, data]);
				this.checkUploads(pack.id);
			}, this));
			BX.addCustomEvent(packageFormer, 'processPackage', BX.proxy(function(stream, pack, procent){
				BX.onCustomEvent(this, 'processPackage', [stream, pack, procent]);
			}, this));

			BX.onCustomEvent(this, 'onStart', [pIndex, packageFormer, this]);
			this.uploads.setItem(pIndex, packageFormer);
			this.checkUploads();
		},
		checkUploads : function(pIndex)
		{
			if (pIndex)
				this.uploads.removeItem(pIndex);
			this.upload = this.uploads.getFirst();
			if (this.upload)
				this.upload.start(this.streams);
			return this.upload;
		},
		// public functions
		getItem : function(id)
		{
			return this.queue.getItem(id);
		},
		getItems : function()
		{
			return this.queue.items;
		},
		restoreItems : function()
		{
			this.queue.restoreFiles.apply(this.queue, arguments);
		},
		clear : function()
		{
			var item;
			while((item = this.queue.items.getFirst()) && item)
			{
				item.deleteFile();
			}
		}
	};

	BX.UploaderSimple = function(params)
	{
		BX.UploaderSimple.superclass.constructor.apply(this, arguments);
		this.dialogName = "BX.UploaderSimple";
		this.previews = new BX.UploaderUtils.Hash();
		if (this.params["uploadMethod"] != "immediate")
		{
			BX.addCustomEvent(this, "onFileNeedsPreview", BX.delegate(function(id, item) {
				this.previews.setItem(item.id, item);
				this.log('onFileNeedsPreview: ' + item.id);
				setTimeout(BX.delegate(this.onFileNeedsPreview, this), 500);
			}, this));
			BX.addCustomEvent(this, "onStart", BX.delegate(function(pIndex, packageFormer) {
				if (packageFormer && packageFormer.filesCount > 0)
				{
					var queue = packageFormer.raw.getIds(), ii;
					for (ii = 0; ii < queue.length; ii++)
					{
						this.previews.removeItem(queue[ii]);
					}
				}
			}, this));
		}
		else
		{
			BX.addCustomEvent(this, "onFileIsUploaded", BX.delegate(function(id, item, data) {
				this.dealWithFile(item, data);
			}, this));
		}
		this.streams = new BX.UploaderStreams(1, this);
		return this;
	};
	BX.extend(BX.UploaderSimple, BX.Uploader);

	BX.UploaderSimple.prototype.preparePost = function()
	{
		var post = BX.UploaderSimple.superclass.preparePost.apply(this, arguments);
		if (post && post.data && post.data[this.params["uploadInputInfoName"]] && !post.data[this.params["uploadInputInfoName"]]["simpleUploader"])
		{
			post.data[this.params["uploadInputInfoName"]]["simpleUploader"] = "Y";
			post.size += 15;
		}
		return post;
	};
	BX.UploaderSimple.prototype.init = function(fileInput, del)
	{
		this.log('input is initialized: ' + (del !== false ? 'drop' : ' does not drop'));
		if (BX(fileInput))
		{
			if (fileInput == this.fileInput && !this.form)
				this.form = this.fileInput.form;

			if (fileInput == this.fileInput)
				fileInput = this.fileInput = this.mkFileInput(fileInput, del);
			else
				fileInput = this.mkFileInput(fileInput, del);

			BX.onCustomEvent(this, "onFileinputIsReinited", [fileInput, this]);

			if (fileInput)
			{
				BX.bind(fileInput, "change", BX.delegate(this.onChange, this));
				return true;
			}
		}
		else if (fileInput === null && this.fileInput === null)
		{
			this.log('Initialized && null');
			return true;
		}
		return false;
	};
	BX.UploaderSimple.prototype.log = function(text)
	{
		BX.UploaderUtils.log('simpleup', text);
	};
	BX.UploaderSimple.prototype.mkFileInput = function(oldNode, del)
	{
		if (!BX(oldNode))
			return false;
		BX.unbindAll(oldNode);
		var node = oldNode.cloneNode(true);
		BX.adjust(node, {
			attrs: {
				id : "",
				name: (this.params["uploadInputName"] + '[file' + BX.UploaderUtils.getId() + '][default]'),
				multiple : false,
				accept : this.limits["uploadFile"]
		}});
		oldNode.parentNode.insertBefore(node, oldNode);
		if (del !== false)
			oldNode.parentNode.removeChild(oldNode);

		return node;
	};
	BX.UploaderSimple.prototype.onChange = function(fileInput)
	{
		BX.PreventDefault(fileInput);

		fileInput = (fileInput.target || fileInput.srcElement || this.fileInput);

		if (BX(this.fileInput) && this.fileInput.disabled)
		{
			BX.DoNothing();
		}
		else
		{
			this.init(fileInput, false);
			this.onAttach([fileInput]);
		}
		return false;
	};
	BX.UploaderSimple.prototype.dealWithFile = function(item, data)
	{
		var file;
		if (data &&
			data["status"] == "uploaded" &&
			data["hash"] &&
			data["file"] &&
			data["file"]["files"] &&
			data["file"]["files"]["default"])
		{
			file = data["file"]["files"]["default"];
		}
		if (file)
		{
			item.file = {
				"name" : file["name"],
				"~name" : file["~name"],
				size : parseInt(file["size"]),
				type : file["type"],
				id : item.id,
				hash : data["hash"],
				copy : "default",
				url : file["url"],
				uploadStatus : statuses.done
			};
			item.nonProcessRun = true;
			BX.onCustomEvent(item, "onFileHasGotPreview", [item.id, item]);
		}
		else
		{
			BX.onCustomEvent(item, "onFileHasNotGotPreview", [item.id, item]);
		}
	};
	BX.UploaderSimple.prototype.onFileNeedsPreviewCallback = function(pack, data)
	{
		if (!(data && data["files"]))
		{
			this.log('onFileNeedsPreviewCallback is failed.');
			return;
		}
		this.log('onFileNeedsPreviewCallback');
		this.onFileNeedsPreview();

		var item;
		while((item = pack.result.getFirst()) && !!item)
		{
			pack.result.removeItem(item.id);
			this.dealWithFile(item, data["files"][item.id]);
		}
	};
	BX.UploaderSimple.prototype.onFileNeedsPreview = function()
	{
		this.log('onFileNeedsPreview');
		var queue = new BX.UploaderUtils.Hash(), item;
		while (queue.length < this.limits["phpMaxFileUploads"] &&
			(item = this.previews.getFirst()) && item && item !== null)
		{
			this.previews.removeItem(item.id);
			queue.setItem(item.id, item);
		}
		if (queue.length > 0)
		{
			this.post = false;
			var pIndex = 'pIndex' + BX.UploaderUtils.getId();
			this.log('create new package for preview ' + pIndex);
			var packageFormer = new BX.UploaderPackage({
				id : pIndex,
				data : queue,
				post : this.preparePost({type : "brief"}, true),
				uploadFileUrl : this.uploadFileUrl,
				limits : this.limits,
				params : this.params
			});
			packageFormer["SimpleUploaderUploadsPreview"] = "Y";
			BX.addCustomEvent(packageFormer, 'adjustProcess', BX.proxy(function(streamId, item, status, params, pIndex) {
				if (status == statuses.error)
				{
					this.adjustProcess(streamId, item, status, params, pIndex);
				}
			}, this));
			BX.addCustomEvent(packageFormer, 'startStream', BX.proxy(function(stream, pack, files){ BX.onCustomEvent(this, 'startPackagePreview', [stream, pack.id, files]); }, this));
			BX.addCustomEvent(packageFormer, 'progressStream', BX.proxy(function(stream, pack, proc){ BX.onCustomEvent(this, 'processPackagePreview', [stream, pack.id, proc]); }, this));
			BX.addCustomEvent(packageFormer, 'doneStream', BX.proxy(function(stream, pack, data){ BX.onCustomEvent(this, 'donePackagePreview', [stream, pack.id, data]); }, this));
			BX.addCustomEvent(packageFormer, 'stopPackage', BX.proxy(function(pack){
//				this.log('restore preview files: ', pack.repo.length);
//				this.queue.restoreFiles(pack.repo);
			}, this));
			BX.addCustomEvent(packageFormer, 'donePackage', BX.proxy(function(stream, pack, data){
				this.checkUploads(pack.id);
				this.onFileNeedsPreviewCallback(pack, data);
			}, this));
			BX.addCustomEvent(packageFormer, 'errorPackage', BX.proxy(function(stream, pack, data){
				BX.onCustomEvent(this, 'error', [stream, pIndex, data]);
				BX.onCustomEvent(this, 'onError', [stream, pIndex, data]);
				this.checkUploads(pack.id);
			}, this));
			BX.addCustomEvent(packageFormer, 'processPackage', BX.proxy(function(stream, pack, procent){
				BX.onCustomEvent(this, 'processPackagePreview', [stream, pack, procent]);
			}, this));

			BX.onCustomEvent(this, 'onStartPreview', [pIndex, packageFormer, this]);
			this.uploads.setItem(pIndex, packageFormer);
			this.checkUploads();
		}
	};
	BX.Uploader.isSupported = function()
	{
		return (window.Blob || window["MozBlobBuilder"] || window["WebKitBlobBuilder"] || window["BlobBuilder"]);
	};
	var objName = "BX.UploaderSimple";
	if (BX.Uploader.isSupported())
		objName = "BX.Uploader";
	BX.Uploader.getInstanceName = function()
	{
		return objName;
	};
	BX.Uploader.getInstance = function(params)
	{
		BX.onCustomEvent(window, "onUploaderIsAlmostInited", [objName, params]);
		return eval("new " + objName + "(params);");
	};
	BX.UploaderPackage = function(params, manager)
	{
		this.filesCount = 0;
		this.length = 0;
		params = (params || {});
		this["pIndex"] = this.id = params["id"];
		this.limits = params["limits"];
		this.params = params["params"];
		this.status = statuses.ready;

		if (params["data"] && params.data.length > 0)
		{
			/**
			 * this.length integer
			 * this.repo BX.UploaderUtils.Hash()
			 * this.raw BX.UploaderUtils.Hash()
			 * this.data BX.UploaderUtils.Hash()
			 */
			this.length = params.data.length;
			this.filesCount = params.data.length;
			this.uploadFileUrl = params["uploadFileUrl"];
			this.raw = params.data;

			this.repo = new BX.UploaderUtils.Hash();
			this.data = new BX.UploaderUtils.Hash();
			this.result = new BX.UploaderUtils.Hash();
			this.init();
			this.post = params["post"];
			if (!this.post)
			{
				var item;
				while ((item = this.raw.getFirst()) && item)
				{
					this.adjustProcess(null, item, statuses.error, {error : BX.message("UPLOADER_UPLOADING_ERROR2")});
					this.raw.removeItem(item.id);
				}
				BX.onCustomEvent(this, 'errorPackage', [null, this, null]);
			}
			else
			{
				var ii, data = { packageIndex : this.id, filesCount : this.filesCount, mode : "upload" };
				for (ii in data)
				{
					if (data.hasOwnProperty(ii))
					{
						this.post.data[this.params["uploadInputInfoName"]][ii] = data[ii];
						this.post.size += BX.UploaderUtils.sizeof(ii) + BX.UploaderUtils.sizeof(data[ii]);
					}
				}
				this.post.startSize = this.post.size;
				BX.onCustomEvent(this, "initializePackage", [this, this.data]);
				if (manager)
					BX.onCustomEvent(manager, "onPackageIsInitialized", [this, this.data]);
				this.log('initialize');
			}
		}
		this._exec = BX.delegate(this.exec, this);
	};

	BX.UploaderPackage.prototype = {
		checkFile : function(item)
		{
			var error = "";
			if (item.file)
			{
				if (this.limits["uploadMaxFilesize"] > 0 && item.file.size > this.limits["uploadMaxFilesize"])
				{
					error = BX.message('FILE_BAD_SIZE') + ' (' + BX.UploaderUtils.getFormattedSize(this.limits["uploadMaxFilesize"], 2) + ')';
				}
				else if (settings.maxSize !== null && item.file.size > settings.maxSize)
				{
					error = BX.message('UPLOADER_UPLOADING_ERROR6');
				}
			}
			return error;
		},
		packStream : function(stream)
		{
			if (stream.filesSize <= 0)
				return null;

			var fd = new BX.UploaderUtils.FormData(), item,
				data = this.post.data,
				files = stream.files,
				res;
			for (item in data)
			{
				if (data.hasOwnProperty(item))
				{
					BX.UploaderUtils.appendToForm(fd, item, data[item]);
				}
			}
			for (var id in files)
			{
				if (files.hasOwnProperty(id))
				{
					data = files[id];

					if (!!data.props)
					{
						res = BX.UploaderUtils.prepareData(data.props);
						for (item in res)
						{
							if (res.hasOwnProperty(item))
							{
								BX.UploaderUtils.appendToForm(fd,  this.params["uploadInputName"] + '[' + id + '][' + item + ']', res[item]);
							}
						}
					}
					if (!!data.files)
					{
						for (var ii = 0; ii < data.files.length; ii++)
						{
							item = data.files[ii];
							item.copy = item.postName = (item.thumb ? item.thumb : 'default');
							if (item.packages > 0)
							{
								item.postName += ('.ch' + item.package + '.' + (item.start > 0 ? item.start : "0") + '.chs' + item.packages);
							}
							fd.append((this.params["uploadInputName"] + '[' + id + '][' + BX.UploaderUtils.prepareData(item.postName) + ']'), item, BX.UploaderUtils.prepareData(item.postName));
						}
					}
				}
			}
			fd.action = this.uploadFileUrl;
			return fd;
		},
		packFiles : function(item, stream)
		{
			if (!item)
				return statuses.error;
			else if (item["uploadStatus"] == statuses.done || item["uploadStatus"] == statuses.error)
				return item["uploadStatus"];
			var count = (this.limits["phpMaxFileUploads"] - this.post.filesCount - (stream.filesCount || 0)),
				size = (this.limits["phpPostMaxSize"] - stream.filesSize - stream.postSize),
				filesSize = (this.limits["phpPostSize"] - stream.filesSize),
				blob, file, data = {files : []}, tmp, error, callback, cf;
			while (size > 0 && count > 0 && filesSize > 0)
			{
				file = null; blob = null; error = ''; callback = [];
				if (item.uploadStatus != statuses.preparing)
				{
					error = this.checkFile(item);
					if (error === '')
					{
						data.props = item.getProps();
						if (item["restored"])
						{
							data.props["restored"] = item["restored"];
							delete item["restored"];
						}
						callback.push(BX.proxy(function() {
							item.uploadStatus = statuses.preparing;
							this.adjustProcess(stream.id, item, statuses["new"], {});
						}, this));
					}
					else
					{
						data.props = {name : item.name, error : true};
						this.adjustProcess(stream.id, item, statuses.error, {error : error});
						item.uploadStatus = statuses.error;
					}
				}
				if (item.uploadStatus == statuses.error)
				{

				}
				else if (item.nonProcessRun === true)
				{
					item.uploadStatus = statuses.done;
				}
				else
				{
					if (!item["file"])
					{
						file = null;
					}
					else if (item.file.uploadStatus != statuses.done)
					{
						file = item.file;
					}
					else if (item["thumb"] && item.thumb !== null)
					{
						file = item.thumb;
					}
					else
					{
						item.thumb = file = item.getThumbs(null);
					}
					var fileConstructor = Object.prototype.toString.call(file);
					if (file === null)
					{
						item.uploadStatus = statuses.done;
						this.adjustProcess(stream.id, item, statuses.done, {});
						item.file.uploadStatus = statuses.done;
						item.thumb = null;
					}
					else if (BX.type.isDomNode(file))
					{
						data.props = (data.props || {name : item.name });
						data.files.push(file);
						callback.push(BX.proxy(function(file) {
							file.uploadStatus = statuses.done;
							if (item.file == file)
							{
								this.adjustProcess(stream.id, item, statuses.preparing, {canvas : "default", package : 1, packages : 1});
							}
							else
							{
								this.adjustProcess(stream.id, item, statuses.preparing, {canvas : item.thumb.thumb, package : 1, packages : 1});
								item.thumb = null;
							}
						}, this))
					}
					else if (!(fileConstructor == '[object File]' || fileConstructor == '[object Blob]'))
					{
						data.props = (data.props || {name : item.name });
						data.props["files"] = (data.props["files"] || {});
						data.props["files"][(file["thumb"] || "default")] = file;
						callback.push(BX.proxy(function(file) {
							file.uploadStatus = statuses.done;
							if (item.file == file)
							{
								this.adjustProcess(stream.id, item, statuses.preparing, {canvas : "default", package : 1, packages : 1});
							}
							else
							{
								this.adjustProcess(stream.id, item, statuses.preparing, {canvas : item.thumb.thumb, package : 1, packages : 1});
								item.thumb = null;
							}
						}, this))
					}
					else
					{
						blob = BX.UploaderUtils.getFilePart(file, (size - BX.UploaderUtils.sizeof({name : item.name})), this.limits["phpUploadMaxFilesize"]);
						if (!blob)
						{
							data.props = "error";
							this.adjustProcess(stream.id, item, statuses.error, {error : BX.message('FILE_BAD_SIZE')});
							item.uploadStatus = statuses.error;
						}
						else
						{
							data.files.push(blob);
							data.props = (data.props || {name : item.name});
							callback.push(BX.proxy(function(file, blob) {
								BX.UploaderUtils.applyFilePart(file, blob);
								if (item.file == file && blob == file)
								{
									this.adjustProcess(stream.id, item, statuses.preparing, {canvas : "default", package : 1, packages : 1});
								}
								else if (item.file == file)
								{
									this.adjustProcess(stream.id, item, statuses.preparing, {canvas : "default", package : (blob.package + 1), packages : blob.packages, blob : blob});
								}
								else if (blob == file)
								{
									this.adjustProcess(stream.id, item, statuses.preparing, {canvas : item.thumb.thumb, package : 1, packages : 1, blob : blob});
									item.thumb = null;
								}
								else
								{
									this.adjustProcess(stream.id, item, statuses.preparing,
										{canvas : item.thumb.thumb, package : (blob.package + 1), packages : blob.packages, blob : blob});
									if (item.thumb.uploadStatus == statuses.done)
										item.thumb = null;
								}
							}, this));
						}
					}
				}
				if (data.files.length > 0 || data["props"])
				{
					tmp = BX.UploaderUtils.sizeof(data.files) + (data["props"] ? BX.UploaderUtils.sizeof(data.props) : 0);
					size -= tmp;
					filesSize -= tmp;
					if (size >= 0 && filesSize > 0)
					{
						while ((cf=callback.shift()) && cf)
							cf(file, blob, error);

						stream.filesSize += tmp;
						stream.files[item.id] = BX.UploaderUtils.makeAnArray(stream.files[item.id], data);

						if (data.files.length) { count--; stream.filesCount++; }
					}
					else if (stream.filesCount <= 0)
					{
						this.adjustProcess(stream.id, item, statuses.error, {error : BX.message('UPLOADER_UPLOADING_ERROR4')});
						item.uploadStatus = statuses.error;
					}
					data = {files : []};
				}
				if (item.uploadStatus !== statuses.preparing)
				{
					break;
				}
			}
			return item.uploadStatus;
		},
		start : function(streams)
		{
			this.streams = streams;
			if (this.status != statuses.ready)
				return;
			this.status = statuses.inprogress;
			this.__onAllStreamsAreKilled = BX.delegate(function(streams, stream){
				this.stop();
				BX.onCustomEvent(this, 'donePackage', [stream, this, this['lastResponse']]);
			}, this);
			BX.addCustomEvent(this.streams, 'onrelease', this.__onAllStreamsAreKilled);
			BX.onCustomEvent(this, 'startPackage', [this, streams]);
			this.log('start');
			streams.init(this, this._exec);
		},
		stop : function()
		{
			this.status = statuses.stopped;
			this.streams.stop();
			BX.onCustomEvent(this, 'stopPackage', [this, this.repo]);
			BX.removeCustomEvent(this.streams, 'onrelease', this.__onAllStreamsAreKilled);
			this.log('stop');
		},
		log : function()
		{
			BX.UploaderUtils.log('package', this.id, arguments);
		},
		init : function()
		{
			var item, callback = BX.proxy(function(id, item) {
				if (this.raw.removeItem(id))
				{
					this.data.setItem(id, item);
					this.repo.setItem(id, item);
					BX.onCustomEvent(item, "onFileHasToBePrepared", [item.id, item]);

					this.init();
				}
			} , this);

			while ((item = this.raw.getFirst()) && item)
			{
				BX.addCustomEvent(item, "onFileIsDeleted", BX.delegate(function(item){
					this.length--;
					this.filesCount--;
					if (this.data.removeItem(item.id))
						this.post.data[this.params["uploadInputInfoName"]]['filesCount'] = this.filesCount;
					this.result.removeItem(item.id);
					this.repo.removeItem(item.id);
				}, this));
				if (item.status === statuses["new"])
				{
					BX.addCustomEvent(item, "onFileIsInited", callback);
					break;
				}
				else
				{
					callback(item.id, item);
				}
			}
		},
		exec : function(stream, reinit)
		{
			if (this.status !== statuses.inprogress)
				return;
			this.log('exec');
			var item, exec = false;
			if (stream.pack != this)
			{
				this.log('stream is bound: ' + stream.id);
				BX.addCustomEvent(stream, 'onsuccess', BX.delegate(this.doneStream, this));
				BX.addCustomEvent(stream, 'onfailure', BX.delegate(this.errorStream, this));
				BX.addCustomEvent(stream, 'onprogress', BX.delegate(this.progressStream, this));
			}
			if (reinit !== false)
			{
				this.log('stream is reinited: ' + stream.id);
				stream.init(this);
			}

			var status, files = stream.filesCount;
			if (this.filesCount > 0)
			{
				while ((item = this.data.getFirst()) && item)
				{
					if (item.uploadStatus == statuses.done)
					{
						// everything is good so we go to another file
					}
					else if (item.preparationStatus != statuses.done) // if file is not initialized
					{
						exec = true;
						break;
					}
					status = this.packFiles(item, stream);
					if (typeof status == "undefined")
					{
						break;
					}
					else if (status != statuses.error)
					{
						files++;
						if (status == statuses.preparing) // if file is not fitted into package
						{
							break;
						}
					}
					this.data.removeItem(item.id);
					if (this["SimpleUploaderUploadsPreview"] == "Y") // in case if it is a simple Uploader uploads preview
					{
						delete item.uploadStatus;
					}
				}
				if (exec === true || (!item && this.raw.length > 0)) // if image is not loaded
				{
					setTimeout(BX.proxy(function(){this.exec(stream, false)}, this), 100);
					return;
				}
			}
			var fd = (files > 0 ? this.packStream(stream) : null);
			if (fd !== null)
			{
				this.log('stream is packed: ' + stream.id);
				this.startStreaming(stream);
				stream.send(fd);
				this.sended = true;
			}
			else
			{
				this.log('stream is killed: ' + stream.id);
				stream.kill();
			}
		},
		adjustProcess : function(streamId, item, status, params)
		{
			if (item && this.repo.hasItem(item.id))
			{
				if (status == statuses.error || status == statuses.uploaded)
				{
					this.data.removeItem(item.id);
					this.result.setItem(item.id, item);
				}
				BX.onCustomEvent(this, 'adjustProcess', [streamId, item, status, params, this.id, this]);
			}
		},
		adjustPostSize : function(stream, increase)
		{
			var result = false, sugestedSize = null;

			var deltaTime = (stream.xhr.finishTime - stream.xhr.startTime);
			if (increase !== false)
			{
				sugestedSize = Math.ceil(deltaTime > 0 ? ((stream.postSize + stream.filesSize) * 1000/ deltaTime ) * settings.estimatedTimeForUploadFile : 0);

				if (sugestedSize > this.limits["phpPostSize"])
				{
					sugestedSize = Math.min(
						this.limits["phpPostSize"] * 2,
						sugestedSize,
						this.limits["phpPostMaxSize"]
					);
				}
			}
			else if (this.limits["phpPostSize"] > settings.phpPostMinSize)
			{
				sugestedSize = Math.ceil(this.limits["phpPostSize"] / 2);
			}
			if (sugestedSize > 0 && sugestedSize !== this.limits["phpPostSize"])
			{
				this.limits["phpPostSize"] = Math.max(sugestedSize, settings.phpPostMinSize);
				result = true;
			}
			return result;
		},
		startStreaming : function(stream)
		{
			this.log('start streaming');
			for (var id in stream.files)
			{
				if (stream.files.hasOwnProperty(id))
				{
					this.adjustProcess(stream.id, this.repo.getItem(id), statuses.inprogress, 0);
				}
			}
			BX.onCustomEvent(this, 'startStream', [stream, this.id, stream.files]);
		},
		doneStream : function(stream, data)
		{
			this.adjustPostSize(stream, true);
			var merge = function(ar1, ar2)
				{
					for (var jj in ar2)
					{
						if (ar2.hasOwnProperty(jj) && !ar1[jj])
						{
							ar1[jj] = ar2[jj]
						}
						else if ((typeof ar2[jj] == typeof ar1[jj] == "object") && ar2[jj] !== null && ar1[jj] !== null)
						{
							ar1[jj] = merge(ar1[jj], ar2[jj]);
						}
					}
					return ar1;
				};
			this.response = merge((this.response || {}), (data || {}));
			var item, id, file, nonProcessRun, files, ij, copies;
			for (id in stream.files)
			{
				if (stream.files.hasOwnProperty(id))
				{
					item = this.repo.getItem(id);
					if (item && (file = data.files[id]))
					{
						if (!file) // has never been loaded
						{
							this.queue.restoreFiles(new BX.UploaderUtils.Hash([item]));
						}
						else if (!file["status"]) // was downloaded partly before but not this time
						{
							if (BX.type.isArray(stream.files[id]["files"]))
							{
								copies = {};
								for (ij = 0; ij < stream.files[id]["files"].length; ij++)
								{
									file = stream.files[id]["files"][ij];
									if (copies[file["copy"]])
										continue;
									copies[file["copy"]] = "Y";
									if (file["copy"] == "default" && file["package"] <= 0)
									{
										this.queue.restoreFiles(new BX.UploaderUtils.Hash([item]));
										break;
									}

									if (file["copy"] == "default")
									{
										item.uploadStatus = statuses.preparing;
										item.file["uploadStatus"] = statuses.preparing;
										item.file["package"] = file["package"];
									}

									if (item.file["copies"])
									{
										item.file["copies"].reset();
										var copy;
										while((copy = item.file["copies"].getNext()) && copy)
										{
											delete copy["uploadStatus"];
											delete copy["firstChunk"];
											delete copy["package"];
											delete copy["packages"];
										}
										item.file["copies"].reset();
									}
								}
							}
						}
						else if (file.status == "error")
						{
							this.adjustProcess(stream.id, item, statuses.error, file);
						}
						else if ((item.hash = file.hash) && file.status == "uploaded")
						{
							if (settings.maxSize !== null)
								settings.maxSize -= item.file.size;
							this.adjustProcess(stream.id, item, statuses.uploaded, file);
						}
						else // chunks
						{
							this.adjustProcess(stream.id, item, statuses.inprogress, file);
							// in case we need to glue chunks only(!)

							nonProcessRun = false;
							files = (file["file"] && file["file"]["files"] ? file["file"]["files"] : false);
							if (typeof files == "object")
							{
								for (ij in files)
								{
									if (files.hasOwnProperty(ij))
									{
										if (files[ij]["chunksInfo"] &&
											files[ij]["chunksInfo"]["count"] == files[ij]["chunksInfo"]["uploaded"] &&
											files[ij]["chunksInfo"]["count"] > files[ij]["chunksInfo"]["written"])
										{
											nonProcessRun = true;
											break;
										}
									}
								}
								item.nonProcessRun = nonProcessRun;
								if (nonProcessRun == true)
								{
									if (!item["nonProcessRunLastTimeWritten"] ||
										item["nonProcessRunLastTimeWritten"] != files[ij]["chunksInfo"]["written"])
									{
										item["nonProcessRunLastTimeWritten"] = files[ij]["chunksInfo"]["written"];
										item["nonProcessRunLastTimeWrittenCount"] = 0;
									}
									else
									{
										item["nonProcessRunLastTimeWrittenCount"]++
									}

									if (item["nonProcessRunLastTimeWrittenCount"] <= 10)
									{
										delete item.uploadStatus;
										this.data.setItem(item.id, item);
									}
									else
									{
										this.adjustProcess(stream.id, item, statuses.error, {error : BX.message("UPLOADER_UPLOADING_ERROR3")});
									}
								}
							}
						}
					}
				}
			}
			this.log('stream is done: ' + stream.id, data["status"], this.response);
			this['lastResponse'] = data;
			if (data["status"] == "inprogress")
			{
				BX.onCustomEvent(this, 'continuePackage', [stream, this, data]);
			}
			else
			{
				if (data["status"] == "error")
					this.errorStream(stream, data);
				else
				{
					this.stop();
					BX.onCustomEvent(this, 'donePackage', [stream, this, data]);
				}
			}
		},
		errorStream : function(stream, data)
		{
			var item, err, id, copy;
			this.log('error stream: ' + stream.id, 'status: ', stream.xhr.status, data);
			if (stream && data == "timeout" && this.adjustPostSize(stream, false) && stream["files"])
			{
				for (id in stream["files"])
				{
					if (stream["files"].hasOwnProperty(id))
					{
						if (this.repo.hasItem(id) &&
							BX.type.isArray(stream["files"][id]["files"]) &&
							stream["files"][id]["files"].length > 0)
						{
							item = this.repo.getItem(id);
							if (stream["files"][id]["files"][0]["package"] <= 0 ||
								item["uploadStatus"] !== statuses.inprogress)
							{
								delete item["uploadStatus"];
								delete item.file["uploadStatus"];
								delete item.file["firstChunk"];
								delete item.file["package"];
								delete item.file["packages"];
							}
							else
							{
								item.file["package"] = Math.min(
									stream["files"][id]["files"][0]["package"],
									item.file["package"]);
							}
							if (item.file["copies"])
							{
								item.file["copies"].reset();
								while((copy = item.file["copies"].getNext()) && copy)
								{
									delete copy["uploadStatus"];
									delete copy["firstChunk"];
									delete copy["package"];
									delete copy["packages"];
								}
								item.file["copies"].reset();
							}

							if (!this.data.hasItem(id))
							{
								this.result.removeItem(id);
								this.data.unshiftItem(id, item);
							}
						}
					}
				}
				BX.onCustomEvent(this, 'resendPackage', [stream, this, data]);
			}
			else
			{
				this.stop();
				var defaultTextError = (data == "timeout" ? BX.message("UPLOADER_UPLOADING_ERROR5") : BX.message("UPLOADER_UPLOADING_ERROR1"));
				data = (data || {});
				data["files"] = (data["files"] ? data["files"] : {});
				if ((item = this.repo.getFirst()) && item)
				{
					do
					{
						if (!this.result.hasItem(item.id))
						{
							if (data.files && data.files[item.id])
								err = data.files[item.id];
							else if (BX.type.isNotEmptyString(data["error"]))
								err = data;
							else if (BX.type.isArray(data["errors"]))
							{
								err = {error : ""};
								for (var ii = 0; ii < data["errors"].length; ii++)
								{
									err.error += (BX.type.isPlainObject(data["errors"][ii]) && data["errors"][ii]["message"] ? data["errors"][ii]["message"] : data["errors"][ii]);
								}
							}
							else
								err = {error : defaultTextError};
							this.adjustProcess(stream.id, item, statuses.error, err);
						}
					} while ((item = this.repo.getNext()) && item);
				}
				BX.onCustomEvent(this, 'errorPackage', [stream, this, data]);
			}
		},
		progressStream : function(stream, procent)
		{
			var id;
			stream.files = (stream.files || {});
			for (id in stream.files)
			{
				if (stream.files.hasOwnProperty(id))
				{
					this.adjustProcess(stream.id, this.repo.getItem(id), statuses.inprogress, procent);
				}
			}
			BX.onCustomEvent(this, 'processPackage', [stream, this, procent]);
		}
	};

	BX.UploaderStream = function(_id, streamsManager)
	{
		this.id = 'stream' + _id;
		this._id = _id;
		this.manager = streamsManager;
		this._onsuccess = BX.delegate(this.onsuccess, this);
		this._onfailure = BX.delegate(this.onfailure, this);
		this._onerror = BX.delegate(this.onerror, this);
		this._onprogress = BX.delegate(this.onprogress, this);
	};
	BX.UploaderStream.prototype =
	{
		xhr : {},
		init : function(pack)
		{
			this["pIndex"] = pack.id;
			this.pack = pack;
			this.files = {};
			this.filesCount = 0;
			this.filesSize = 0;
			this.postSize = pack.post.size;
		},
		send : function(fd)
		{
			if (fd && fd.local === true)
			{
				BX.adjust(fd.form, { attrs : { action: fd.action} } );
				BX.ajax.submit(fd.form, BX.proxy(function(data) {
					data = BX.util.htmlspecialcharsback(data);
					while (/^<(.*?)>(.*?)<(.*?)>$/gi.test(data))
						data = data.replace(/^<(.*?)>(.*?)<(.*?)>$/gi, "$2");
					while (/^<([^<>]+)>(.*?)/gi.test(data))
						data = data.replace(/^<(.*?)>(.*?)/gi, "$2");
					while (/(.+?)<([^<>]+)>$/gi.test(data))
						data = data.replace(/(.+?)<([^<>]+)>$/gi, "$1");

					var res = BX.parseJSON(data, {});

					if (!!res)
					{
						this.onsuccess(res);
					}
					else
					{
						this.onfailure("processing", data);
					}
				}, this) );
				this.onprogress(90);
			}
			else if (fd)
			{
				this.xhr = BX.ajax({
					'method': 'POST',
					'dataType': 'json',
					'data' : fd,
					'url': fd.action,
					'onsuccess': this._onsuccess,
					'onfailure': this._onfailure,
					'onerror': this._onerror,
					'start': false,
					'preparePost':false,
					'processData':true,
					'skipAuthCheck': true,
					'timeout' : settings.maxTimeForUploadFile
				});
				this.xhr.upload.addEventListener('progress', this._onprogress, false);
				var d = new Date();
				this.xhr.startTime = d.getTime();
				this.xhr.send(fd);
			}
			else
			{
				this.onfailure("empty", null);
			}
		},
		onsuccess : function(data)
		{
			var d = new Date();
			this.xhr.finishTime = d.getTime();
			try
			{
				if (typeof data == "object" && data && data["files"] && data["status"] !== "error")
					BX.onCustomEvent(this, 'onsuccess', [this, data]);
				else
					BX.onCustomEvent(this, 'onfailure', [this, data]);
			}
			catch (e)
			{
				BX.debug(e);
			}
			BX.onCustomEvent(this, 'onrelease', [this]);
		},
		onfailure : function(status, e)
		{
			var d = new Date(), data = (e && e["data"] ? BX.parseJSON(e["data"], {}) : "");

			if (BX.message("bxUploaderLog") === "Y" && status === "processing")
			{
				BX.ajax.post(
					"/bitrix/tools/upload.php?action=error",
					{
						sessid : BX.bitrix_sessid(),
						path : window.location.pathname,
						data : e["data"]
					}
				);
			}
			this.xhr.finishTime = d.getTime();
			BX.onCustomEvent(this, 'onfailure', [this, data]);
			BX.onCustomEvent(this, 'onrelease', [this]);
		},
		onerror : function()
		{
			var d = new Date();
			this.xhr.finishTime = d.getTime();
			this.onfailure.apply(arguments);
		},
		onprogress : function(e)
		{
			var procent = 15;
			if(typeof e == "object" && e.lengthComputable) {
				procent = e.loaded * 100 / (e["total"] || e["totalSize"]);
			}
			else if (e > procent)
				procent = e;
			procent = (procent > 5 ? procent : 5);

			BX.onCustomEvent(this, 'onprogress', [this, procent]);
			return procent;
		},
		kill : function()
		{
			BX.DoNothing();
			BX.onCustomEvent(this, 'onkill', [this]);
		},
		restore : function()
		{
			this.manager.restore(this);
		}
	};

	BX.UploaderStreams = function(count, uploader)
	{
		this.streams = new BX.UploaderUtils.Hash();
		this.killedStreams = new BX.UploaderUtils.Hash();
		this.packages = new BX.UploaderUtils.Hash();
		this.uploaded = uploader;
		this.timeout = 3000; // time between streams
		this._exec = BX.delegate(this.exec, this);
		this._restore = BX.delegate(this.restore, this);
		this._kill = BX.delegate(this.kill, this);
		this.count = Math.min(5, (count > 1 ? count : 1));
		this.status = statuses.ready;

	};
	BX.UploaderStreams.prototype = {
		init : function(pack, handler)
		{
			if (this.package !== pack)
			{
				this.package = pack;
				this.package.log('streams are occupied', handler);
				this.packages.setItem(pack.id, pack.post); // For compatibility
				this.handler = handler;
				var count = this.count, stream;
				while ((stream = this.streams.getFirst()) && stream)
				{
					this.streams.removeItem(stream.id);
					stream = null;
				}
				this.streams = new BX.UploaderUtils.Hash();
				while (count-- > 0)
				{
					stream = new BX.UploaderStream(count, this);
					BX.addCustomEvent(stream, 'onrelease', this._restore);
					BX.addCustomEvent(stream, 'onkill', this._kill);
					this.streams.setItem(stream.id, stream);
				}
			}
			this.start();
		},
		exec : function()
		{
			if (this.status == statuses.ready)
			{
				this.package.log('streams are in executing');
				var stream = this.streams.getFirst();
				if (stream)
				{
					this.streams.removeItem(stream.id);
					if (this.streams.length > 0)
					{
						setTimeout(this._exec, this.timeout);
					}
					this.handler(stream); // return to package class
				}
			}
			else
			{
				this.package.log('streams are locked');
			}
		},
		restore : function(stream)
		{
			this.streams.setItem(stream.id, stream);
			BX.defer_proxy(this.exec, this)();
		},
		kill : function(stream)
		{
			this.killedStreams.setItem(stream.id, stream);
			if (this.killedStreams.length == this.count)
			{
				BX.onCustomEvent(this, 'onrelease', [this, stream]);
			}
		},
		start : function()
		{
			this.status = statuses.ready;
			this.exec();
		},
		stop : function()
		{
			this.status = statuses.stopped;
		}
	};
	BX.Uploader.getVersion = function() {
		return "1";
	}
}(window));