//$Revision: 1.72 $

function UpServerErrors() {
	this.name = 'UpServerErrors';

	this.HTTP = 1;
	this.IO = 2;
	this.SECURITY = 3;
	this.OTHER = 4;
	this.START = 11;
	this.IFRAME = 12;
	this.DENIED = 13;
	this.LOGIC = 14;
	this.NOSPACE = 15;
	this.TOOMANY = 16;
	this.TOOBIG = 20;
	this.ISEMPTY = 21;

	this.THROWN = true;
	this.NATURAL = false;
}

var gErr = new UpServerErrors();

function inherit(subclass, superclass) {
	var c = function() {};
	c.prototype = superclass.prototype;
	subclass.prototype = new c();
}

function FilesUpServer () {
	this.name = "FilesUpServer";
	this.uid = 'UniqueServerGroupMark'; //optional

	this.UpHost = 'http://files.mail.ru';
	this.AjaxHost = 'http://files.mail.ru';
	this.UploadURL	= '/upload_ext/';
	this.AjaxURL	= '/cgi-bin/files/fajaxcall';
	this.DeleteURL = this.AjaxURL;
	this.ListURL = this.AjaxURL;
	this.ResetURI	= 'http://img.mail.ru/mail/ru/images/files/blank.html';

	this.SrvSpaceLeft = 0;
	this.SrvSpaceLeftCalc = 0;
	this.MaxFileSize = 100 * 1024*1024;
	this.SrvListLimit = 10;
	this.SrvListLimitExceeded = false;

	this.ServerMark = "[Flz]";
	this.ServerPriority = 1;
	this.LogGroupChangeToServer = false;
	this.UpMode = "contextflash";
	this.isInitialized = false;
	this.isInitOk = false;
	this.GroupCode = "";

	this.AuthData = null;
	this.UsePostAuth = true;
	this.AuthAutoUpdate = false;
	this.DontCheckSpace = false;
}

FilesUpServer.prototype.Init = function (initObj) {
	if (initObj.uid) this.uid = initObj.uid;
	if (initObj.UpHost) this.UpHost = initObj.UpHost;
	if (initObj.UploadURL) this.UploadURL = initObj.UploadURL;

	if (initObj.AjaxHost) this.AjaxHost = initObj.AjaxHost;
	if (initObj.AjaxURL) this.AjaxURL = initObj.AjaxURL;
	this.DeleteURL = this.AjaxURL;
	this.ListURL = this.AjaxURL;
	if (initObj.DeleteURL) this.DeleteURL = initObj.DeleteURL;
	if (initObj.ListURL) this.ListURL = initObj.ListURL;
	if (initObj.ResetURI) this.ResetURI = initObj.ResetURI;

	if (initObj.SrvSpaceLeft) this.SrvSpaceLeft = initObj.SrvSpaceLeft;
	if (initObj.SrvSpaceLeftCalc) this.SrvSpaceLeftCalc = initObj.SrvSpaceLeftCalc;
	if (initObj.SpaceLeft) this.SrvSpaceLeft = this.SrvSpaceLeftCalc = initObj.SpaceLeft;

	if (initObj.MaxFileSize) this.MaxFileSize = initObj.MaxFileSize;
	if (initObj.SrvListLimit) this.SrvListLimit = initObj.SrvListLimit;

	if (initObj.ServerMark) this.ServerMark = initObj.ServerMark;

	if (initObj.GroupCode) this.GroupCode = initObj.GroupCode;
	if (initObj.ServerPriority) this.ServerPriority = initObj.ServerPriority;
	if (typeof initObj.AutoRestore != "undefined") this.AutoRestore = initObj.AutoRestore;

	if (initObj.AuthData) this.AuthData = initObj.AuthData;
	if (typeof initObj.UsePostAuth != "undefined") this.UsePostAuth = initObj.UsePostAuth;
	if (typeof initObj.DontCheckSpace != "undefined") this.DontCheckSpace = initObj.DontCheckSpace;
	if (typeof initObj.AuthAutoUpdate != "undefined") this.AuthAutoUpdate = initObj.AuthAutoUpdate;
	if (initObj.UpMode) this.UpMode = initObj.UpMode;
};

FilesUpServer.prototype.CalculateServerSize = function (filesize) {
	return filesize;
};

FilesUpServer.prototype.GetCookieValue = function (cookieString, cookieName) {
	//todo
	var value = (cookieString + ";").match(/cookieName=(\w+);/i);
	if (cookie_mpop !== null) lCookieReq += cookieMrcu[1];
};

FilesUpServer.prototype.PrepareAuthData = function () {
	var res = "";
	if (this.AuthData) {
		jswarn_l(2, "PrepareAuthDataFlz-Manual");
		res = this.AuthData;
	} else {
		jswarn_l(2, "PrepareAuthDataFlz-Auto");
		var val;
		var CookieText = document.cookie + ";";
		val = CookieText.match(/(mrcu=[^;]+;)/i);
		if (val !== null) res += val[1] + " ";
		val = CookieText.match(/(Mpop=[^;]+;)/i);
		if (val !== null) res += val[1] + " ";
		if (this.conf.isLoggedIn) {
            val = CookieText.match(/(flsmlrur=\w+;)/i);
			if (val !== null) res += val[1];
		} else {
    		val = CookieText.match(/(flsmlrua=\w+;)/i);
    		if (val !== null) res += val[1];
		}
	}
	return {'UsePostAuth': this.UsePostAuth, 'Cookie': res};
};

FilesUpServer.prototype.GetServerParams = function (wantArray) {
	if (wantArray)
		return [{name: "sourcehost", value: this.conf.SourceHost}, {name: "groupcode", value: escape(this.GroupCode)}, {name: "upmode", value: this.UpMode}];
	return "sourcehost=" + this.conf.SourceHost + "&groupcode=" + escape(this.GroupCode) + "&upmode=" + this.UpMode;
};

FilesUpServer.prototype.GetUploadURI = function () {
	return this.UpHost + this.UploadURL;
};

FilesUpServer.prototype.GetAjaxURI = function () {
	return this.AjaxHost + this.AjaxURL;
};

FilesUpServer.prototype.GetDeleteURI = function () {
	return this.AjaxHost + this.DeleteURL;
};

FilesUpServer.prototype.GetListURI = function () {
	return this.AjaxHost + this.ListURL;
};

FilesUpServer.prototype.GetResetURI = function () {
	return this.ResetURI;
};


FilesUpServer.prototype.SpaceLeft = function (value) {
	if (value) {
		this.SrvSpaceLeft = value - 0;
		this.SrvSpaceLeftCalc = value - 0;
		jswarn_l(2,'Set ' + this.uid  + ' SpaceLeft=' + this.SrvSpaceLeft );
	}
	return {'SrvSpaceLeft': this.SrvSpaceLeft, 'SrvSpaceLeftCalc': this.SrvSpaceLeftCalc};
};


FilesUpServer.prototype.GetErrorDetails = function (errorCode) {
	var notificationText = t('DefUploadFailed') + t('CheckInetAndRetry');
	var notificationTitle = t('DefUploadFailed') + t('CheckInet');
	var notificationIsConfirm = true;

	if (errorCode >= 20) notificationIsConfirm = false;
	if (errorCode == gErr.NOSPACE) {
		notificationTitle = t('ErrNoFreeSpace' + (this.conf.isLoggedIn ? 'R' : 'A'));
	} else if (errorCode == gErr.TOOMANY) {
		notificationTitle = ts('ErrTooManySendFiles', this.SrvListLimit);
	} else if (errorCode == gErr.TOOBIG) {
		notificationTitle = ts('ErrTooBigFile', this.MaxFileSize);
	} else if (errorCode == gErr.ISEMPTY) {
		notificationTitle = t('ErrCantReadFile');
	} else {
		notificationTitle = t('ErrUploadFailed');
	}
	notificationText = notificationTitle + (notificationIsConfirm ? '\n' + t('ConfTryItAgain')  : '');
	return {'Text': notificationText, 'Title': notificationTitle, 'IsConfirm': notificationIsConfirm, 'errorCode': errorCode};
};

FilesUpServer.prototype.cbParseUploadResponse = function (extraData) {
	var dataObj = {};
	if (extraData) {
		var parStr = extraData + '&';
		var userid = parStr.match(/userid=(\w+)\&/i);
		var fileid = parStr.match(/vfileid=(\w+)\&/i);
		var filesize = parStr.match(/vfilesize=(\w+)\&/i);
		var errortext = parStr.match(/msgcode=(\w+)\&/i);
		var idscount = parStr.match(/idscount=(\w+)\&/i);
		var availsize = parStr.match(/availsize=(\w+)\&/i);
		var linkcode = parStr.match(/linkcode=(\w+)\&/i);
		var folderstatarray = parStr.match(/folderstatarray=([\[\]\,0-Z]+)\&/i);
		var delfreesspace = parStr.match(/delfreesspace=(\w+)\&/i);
		var expiredsession = parStr.match(/expiredsession=(\w+)\&/i);
		var auth = parStr.match(/auth=([=:\w]+)\&/i);
		var groupttl = parStr.match(/groupttl=(\w+)\&/i);

        if (expiredsession !== null) {
			if (this.conf.cbOnExpiredSession) this.conf.cbOnExpiredSession(expiredsession[1] - 0);
			return {};
		}
		if (userid !== null) dataObj.userid = userid[1] - 0;
		if (fileid !== null) dataObj.fileid = fileid[1] - 0;
		if (filesize !== null) dataObj.filesize = filesize[1] - 0;
		if (idscount !== null) dataObj.idscount = idscount[1] - 0;
		if (availsize !== null) dataObj.availsize = availsize[1] - 0;
		if (linkcode !== null) {
			dataObj.linkcode = linkcode[1];
		}
		if (this.cbOnFolderStatistics && folderstatarray !== null) {
			try {
				data = eval(folderstatarray[1]);
				this.cbOnFolderStatistics(data);
			} catch (e) {}
		}

		if (delfreesspace !== null) dataObj.delfreesspace = delfreesspace[1] - 0;
		
		if (auth !== null) {
			//dataObj.auth = auth[1] - 0;
			jswarn_l(this.AuthData != auth[1] ? 0 : 2, "AuthGet:" + auth[1]);
			if (this.AuthAutoUpdate) this.AuthData = auth[1];
		}
		if (groupttl !== null) {
			//dataObj.groupttl = groupttl[1] - 0;
			this.GroupTTL = groupttl[1] - 0;
		}

		if (errortext !== null) {
			dataObj.errorcode = gErr.LOGIC;
			dataObj.errortext = errortext[1];
			var errors = new Hash('NO_FREE_SPACE', gErr.NOSPACE,
				'UPLOAD_ERROR', gErr.HTTP,
				'TOO_MANY_SEND_FILES', gErr.TOOMANY,
				'TOO_BIG_FILE', gErr.TOOBIG,
				'CAN_NOT_READ_FILE', gErr.ISEMPTY,
				'FILE_IS_EMPTY', gErr.ISEMPTY
				);
			if (errors.hasItem(dataObj.errortext)) {
				dataObj.errorcode = errors.getItem(dataObj.errortext);
			}
		}
	}
	return dataObj;
};

FilesUpServer.prototype.DeleteFile = function (dataObj, cbConfirmDeletion) {
	jswarn_l(4, 'DeleteFile');
	var _this = this;
	dataObj.cbConfirmDeletion = cbConfirmDeletion; // not cbFunction!
	dataObj.UpServer = this;
	var st = AjaxFlash.SendRequest(
		{uri: this.GetAjaxURI()
		,funcName: this.conf.isLoggedIn ? 'cbHardDeleteFileMail' : 'cbHardDeleteFile'
		,funcArgs: [dataObj.fileid, dataObj.id]
		,callbackFunc: _this.cbDeleteFile
		,callbackObject: dataObj
		,dirtyCookies: this.PrepareAuthData().Cookie
	});
	return st;
};

FilesUpServer.prototype.cbDeleteFile = function (httpst, st, fileId, id, folderstatarray) {
	jswarn_l(2, 'cbDeleteFile:' + st + ', ' + fileId + ', ' +  id);
	var isItDone = false;
	if (httpst==200 && st==1) {
		isItDone = true;
		this.cbConfirmDeletion(id);
	}
	this.cbFunction(id, isItDone, this);
	if (this.UpServer && this.UpServer.cbOnFolderStatistics && folderstatarray !== null) {
		try {
			data = eval(folderstatarray);
			this.UpServer.cbOnFolderStatistics(data);
		} catch (e) {}
	}
};

FilesUpServer.prototype.RequestInitData = function (linkcode, cbProcessInitData) {
	jswarn_l(4, 'RequestInitData@Flz');
	var _this = this;
	var dataObj = {};
	dataObj.cbFunction = cbProcessInitData;
	dataObj.serverObj = this;
	var st = AjaxFlash.SendRequest(
		{uri: this.GetAjaxURI()
		,funcName: this.conf.isLoggedIn ? 'cbRestoreGroupListMail' : 'cbRestoreGroupList'
		,funcArgs: [linkcode]
		,callbackFunc: _this.cbParseInitResponse
		,callbackObject: dataObj
		,dirtyCookies: this.PrepareAuthData().Cookie
	});
	return st;
};


FilesUpServer.prototype.cbParseInitResponse = function (httpst, extraData) {
	jswarn_l(2, 'cbParseInitResponse:' + httpst + '/' + extraData);
	var data = Array.prototype.slice.call(arguments,1);
	var statObj = {};
	statObj.name = 'stat';
	var itemArr = [];
	if (httpst==200) {
		for(var i=0 ; i < data.length ; i++) {
			var curItem = data[i];
			if (curItem.IsCommonData) {
				if (curItem.SpaceLeft) statObj.availsize = curItem.SpaceLeft;
			} else {
				var itemObj = {};
				if (curItem.Id) itemObj.fileid = curItem.Id;
				if (curItem.FileName) itemObj.filename = curItem.FileName;
				if (curItem.FileSize) itemObj.filesize = curItem.FileSize - 0;
				if (curItem.UploadedOn) itemObj.uploadedon = curItem.UploadedOn - 0;
				itemArr.push(itemObj);
			}
		}
		this.serverObj.isInitOk = true;
	}
	//cbFunction => RestoreServerData
	this.cbFunction(this.serverObj, {'stat': statObj, 'list': itemArr});
};


FilesUpServer.prototype.onGroupListChange = function (ids) {
	jswarn_l(2, 'onGroupL.C/ids=' + ids);
	if (ids.length > 2) {
		var st = AjaxFlash.SendRequest(
			{uri: this.GetAjaxURI()
			,funcName: 'cbAddToMessageMail'
			,funcArgs: [ids, this.GroupCode]
			,callbackFunc: this.cbUpdateGroup
			,callbackObject: this
			,dirtyCookies: this.PrepareAuthData().Cookie
		});
	}
};

FilesUpServer.prototype.cbUpdateGroup = function (httpst, linkcode) {
	jswarn_l(2, 'cbUpdateGroup /' + httpst + ', ' + linkcode);
	if (httpst==200 || httpst === 1 ) {
		if (linkcode && linkcode.length == 6) {
			this.GroupCode = linkcode;
		} else {
			this.GroupCode = "";
		}
		if (this.cbOnGroupCodeChange) this.cbOnGroupCodeChange(this.GroupCode);
	}
};



function MailUpServer () {
	FilesUpServer.call(this);
	this.name = 'MailUpServer';
	this.UpHost = 'http://win.mail.ru';
	this.AjaxHost = 'http://win.mail.ru';
	this.UploadURL	= '/cgi-bin/attach_upload';
	this.AjaxURL	= '/cgi-bin/attach_upload';

	this.MaxFileSize = Math.floor (30 * 1024 * 1024/ 1.36);
	this.SrvListLimit = 10;

	this.ServerMark = "[@]";
	this.LogGroupChangeToServer = false;
	this.UpMode = "na";
	this.UsePostAuth = false;
}
inherit(MailUpServer, FilesUpServer);

MailUpServer.prototype.CalculateServerSize = function (filesize) {
	var fsize = Math.floor(1.36*(filesize + 1023)/1024)*1024;
	return fsize ? fsize : 0;
};

MailUpServer.prototype.cbParseUploadResponse = function (extraData) {
	var dataObj = {};
	if ((extraData) && (extraData.indexOf("AjaxResponse") != -1)  ) {
		var data =[];
		try {
			data = eval(extraData);
		} catch (e) {}
		var magick = data.shift();
		if(magick != "AjaxResponse") return {};
		var result = data.shift();
		if(result != "OK" && result != "Redirect" && result != "Error") return {};
		if(result == "OK") {
			var action_type = data.shift();
			if(action_type == "UPLOADED") {
				// 1 <-> data.length
				for(var i=0 ; i < 1 ; i++) {
					var curAttach = data[i];
					if (curAttach.Id) dataObj.fileid = curAttach.Id;
					if (curAttach.FileName) dataObj.filename = curAttach.FileName;
					if (curAttach.Size) dataObj.filesize = curAttach.Size;
					if (curAttach.SpaceLeft) dataObj.availsize = curAttach.SpaceLeft;

					if (curAttach.Error) {
						dataObj.errortext = curAttach.Error;
						dataObj.errorcode = gErr.LOGIC;
						var errors = new Hash(
							995, gErr.NOSPACE,
							996, gErr.HTTP,
							998, gErr.TOOMANY,
							999, gErr.TOOBIG,
							997, gErr.ISEMPTY
							);
						if (errors.hasItem(dataObj.errortext)) {
							dataObj.errorcode = errors.getItem(dataObj.errortext);
						}
					}
				}
			}
		} else if(result == "Error") {
			jswarn_l("ParseERR");
			return {}
		} else if(result == "Redirect") {
			jswarn_l("ParseRedir");
			if (this.conf.cbOnExiredSession) this.conf.cbOnExiredSession(data.shift(), data.shift());
			//Misprint fix
			if (this.conf.cbOnExpiredSession) this.conf.cbOnExpiredSession(data.shift(), data.shift());
			return {}
		}
	}
	jswarn_l("ParseFin");
	return dataObj;
};

MailUpServer.prototype.GetServerParams = function (wantArray) {
	if (wantArray)
		return [{name: "message", value: this.GroupCode}];
	return "message=" + this.GroupCode;
};

MailUpServer.prototype.PrepareAuthData = function () {
	jswarn_l(4, "PrepareAuthData@");
	if (this.AuthData) {
		jswarn_l(2, "PrepareAuthData@-Manual");
		res = this.AuthData;
	} else {
		res = document.cookie;
	}
	return {'UsePostAuth': this.UsePostAuth, 'Cookie': res};
};

MailUpServer.prototype.DeleteFile = function (dataObj, cbConfirmDeletion) {
	jswarn_l(1, 'DeleteFile');
	var _this = this;
	dataObj.cbConfirmDeletion = cbConfirmDeletion;
	var st = AjaxFlash.SendRequest(
		{uri: this.GetAjaxURI()
		,funcName: 'cbHardDeleteFile'
		,funcArgs: [dataObj.fileid, this.GetServerParams()]
		,callbackFunc: _this.cbDeleteFile
		,callbackObject: dataObj
		,dirtyCookies: this.PrepareAuthData().Cookie
	});
	return st;
};

MailUpServer.prototype.cbDeleteFile = function (httpst, marker, st, fileid, spaceleft) {
	var isItDone = false;
	var id = this.id;
	jswarn_l(2, 'cbDeleteFile:' + marker + ' ' + id + ' ' + st);
	if (httpst==200 && (st==1 || st==0)) {
		isItDone = true;
		this.cbConfirmDeletion(id);
	}
	this.cbFunction(id ? id : null, isItDone, this);
};

MailUpServer.prototype.RequestInitData = function (linkcode, cbProcessInitData) {
	jswarn_l(4, 'RequestInitData@Mail');
	var _this = this;
	var dataObj = {};
	dataObj.cbFunction = cbProcessInitData;
	dataObj.serverObj = this;
	var st = AjaxFlash.SendRequest(
		{uri: this.GetAjaxURI()
		,funcName: 'cbRestoreGroupList'
		,funcArgs: [linkcode]
		,callbackFunc: _this.cbParseInitResponse
		,callbackObject: dataObj
		,dirtyCookies: this.PrepareAuthData().Cookie
	});
	return st;
};




function FilesUploadsQueue () {
	this.name = "FilesUploadsQueue";
	this.uploads = new Hash();
	this.ListLimit = 20;
	this.ListLimitExceeded = false;

	this.STATUS_NEW = 0;
	this.STATUS_READY = 1;
	this.STATUS_INPROGRESS = 2; this.STATUS_UPLOAD = 2;
	this.STATUS_DONE = 3;
	this.STATUS_FAILED = 4;
	this.STATUS_STOPPED = 5;
	this.STATUS_BROKEN = 8;
	this.STATUS_TERMINATED = 9;

	this.timerValue = 1000;
	this.timerObj;

	this.blockMode = false;
	this.queueIsFinshed = false;
	this.queueIsActive = false;
	this.numberOfOpenTasks = 0;
	this.numberOfActiveUpload = 0;
	this.maxNumberOfUploads = 1;

	this.CompletedUploads = 0;

	//this.cbCheckUpload = function () {};
	//this.cbStartUpload = function () {};
}
	FilesUploadsQueue.prototype.init = function () {
		jswarn_l(2,'QInit');
		this.SetTimer();
	};

	FilesUploadsQueue.prototype.AddItem = function (id, name, size, status) {
		var infoObj = new Object();
		infoObj.size = 0;
		infoObj.name = name;
		infoObj.status = status ? status : this.STATUS_NEW;
		this.uploads.setItem(id, infoObj);
	};
	
	FilesUploadsQueue.prototype.RemoveItem = function (id) {
		if (this.uploads.hasItem(id)) {
			this.uploads.removeItem(id);
			return true;
		} else {
			return false;
		}
	};

	FilesUploadsQueue.prototype.AddItems = function (arr) {
		//arr is an array of hash-objects
		jswarn_l(2, 'QPushedItems: ' + arr.length)
		for (i = 0; i<arr.length; i++) {
			if (!arr[i].status) {arr[i].status = this.STATUS_DONE};
			this.uploads.setItem(GetRandomID(), arr[i]);
		}
	};

	FilesUploadsQueue.prototype.GetItem = function (id) {
		if (this.uploads.hasItem(id)) {
			return this.uploads.getItem(id);
		} else {
			return null;
		}
	};

	FilesUploadsQueue.prototype.HasItem = function (id) {
		return  this.uploads.hasItem(id);
	};
	
	FilesUploadsQueue.prototype.SetItem = function (id, hashObj) {
		if (this.uploads.hasItem(id)) {
			return this.uploads.getItem(id) = hashObj;
		} else {
			return null;
		}
	};

	FilesUploadsQueue.prototype.ChangeItem = function (id, hashObj) {
		if (this.uploads.hasItem(id)) {
			var curObj = this.uploads.getItem(id);
			for (var key in hashObj) {
				curObj[key] = hashObj[key];
			}
			return curObj;
		} else {
			return null;
		}
	};

	FilesUploadsQueue.prototype.SetItemStatus = function (id, status) {
		jswarn_l(2, 'SetItemStatus /id=' + id + ', st=' + status);
		if (this.uploads.hasItem(id)) {
			var UpItem = this.ChangeItem(id, {'status': status} );
			if (status > this.STATUS_INPROGRESS) {
				if (this.CalcLength() <= this.ListLimit) {
					this.ListLimitExceeded = false;
				}
			}
			return UpItem;
		} else {
			return null;
		}
	};

	FilesUploadsQueue.prototype.CheckQueueLength = function (UpServer) {
		if (UpServer) {
			var queue_size = this.CalcLength(function (o) { return (o.serverid == UpServer.id)});
			UpServer.SrvQueueLength = queue_size;
			jswarn_l(2, 'CheckQueueLen: sId' +  UpServer.id + '/' +UpServer.SrvQueueLength + ' of ' + UpServer.SrvListLimit);
			if ( queue_size < UpServer.SrvListLimit) {
				UpServer.SrvListLimitExceeded = false;
			} else {
				UpServer.SrvListLimitExceeded = true;
			}
		}

		var curQueueLength = this.CalcLength();
		if ((this.onQueueChanged) && (this.queueLength != curQueueLength)) this.onQueueChanged(curQueueLength, this.GetUIidArray());
		this.queueLength = curQueueLength;
		if (curQueueLength >= this.ListLimit) {
			this.ListLimitExceeded = true;
			return false;
		} else {
			this.ListLimitExceeded = false;
			return true;
		}
	};

	FilesUploadsQueue.prototype.SetTimer = function (id, status) {
		jswarn_l(1,'MonitorQueue');
		var thisSaved = this;
		window.setInterval(function() {thisSaved.MonitorQueue()},  1000);
	};

	FilesUploadsQueue.prototype.MonitorQueue = function () {
		var locQueueIsActive = false;
		var numberOfOpenTasks = 0;
		var numberOfActiveUpload = 0;

		for (var i in this.uploads.items) {
			//Hunting injected prototypes like toJSONString
			if (typeof this.uploads.items[i] == "object") {
				if (this.cbCheckUpload) {
					var cur_upload = this.uploads.getItem(i);
					if (cur_upload.status == this.STATUS_INPROGRESS) {
						numberOfActiveUpload++;
						this.cbCheckUpload(i);
					}
				}
				if (cur_upload.status < this.STATUS_DONE) {
					jswarn_l(cur_upload.status == 0 ? 7 : 5, i + ' ' + cur_upload.status)
					numberOfOpenTasks++;
					locQueueIsActive = true;
				}
			}
		}

		for (var i in this.uploads.items) {
			//Hunting injected prototypes
			if (typeof this.uploads.items[i] == "object") {
				var cur_upload = this.uploads.getItem(i);
				if (cur_upload.status == this.STATUS_READY) {
					if ( (numberOfActiveUpload < this.maxNumberOfUploads) & (this.blockMode == false)) {
						jswarn_l(5, '$');
						this.cbStartUpload(i);
					}
					break;
				}
			}
		}

		this.numberOfOpenTasks = numberOfOpenTasks;
		if (!this.queueIsActive && locQueueIsActive) {
			jswarn_l(1,'onQueueStart');
			if (this.onQueueStart) this.onQueueStart();
			this.queueIsActive = true;
			this.queueIsFinshed = false;
		} else if (this.queueIsActive) {
			if (!this.queueIsFinshed && (numberOfOpenTasks == 0)) {
				this.queueIsFinshed = true;
				this.queueIsActive = false;
				jswarn_l(1,'onQueueEnd');
				if (this.onQueueBeforeFinsh)  this.onQueueBeforeFinsh();
				if (this.onQueueFinsh)  this.onQueueFinsh();

				if (this.onQueueBeforeFinish)  this.onQueueBeforeFinish();
				if (this.onQueueFinish)  this.onQueueFinish();
			}
		} else {
			this.queueIsActive = locQueueIsActive;
		}
	};

	FilesUploadsQueue.prototype.GetItemsArray = function (callback) {
		var resArray = [];
		for (var i in this.uploads.items) {
			//Hunting injected prototypes
			if (typeof this.uploads.items[i] == "object") {
				var cur_upload = this.uploads.getItem(i);
				if (callback) {
					if (callback(cur_upload)) {
						resArray.push(cur_upload);
					}
				} else {
					resArray.push(cur_upload);
				}
			}
		}
		return resArray;
	};

	FilesUploadsQueue.prototype.GetPackedList = function (callback) {
		jswarn_l(5, "GetPackedList");
		var enc_str = ".";
		var items = this.GetItemsArray(callback);
		for (var i = 0; i < items.length; i++) {
			var cur_upload = items[i];
			if (cur_upload.fileid && cur_upload.status == this.STATUS_DONE) {
				enc_str += LCvt(cur_upload.fileid) + ".";
			}
		}
		return enc_str;
	};

	FilesUploadsQueue.prototype.GetUIidArray = function (callback) {
		jswarn_l(10, "GetUIidArray");
		var resArray = [];
		var items = this.GetItemsArray(callback);
		for (var i = 0; i < items.length; i++) {
			resArray.push(items[i].id);
		}
		return resArray;
	};

	FilesUploadsQueue.prototype.CalcLength = function (callback) {
		//jswarn_l(5, "CalcLength");
		var cnt = 0;
		var items = this.GetItemsArray(callback);
		return items.length;
	};

	FilesUploadsQueue.prototype.CalcSize = function (callback) {
		//jswarn_l(5, "CalcSize");
		var sum_size = 0;
		var items = this.GetItemsArray(callback);
		for (var i = 0; i < items.length; i++) {
			var cur_upload = items[i];
			if (cur_upload.filesize) {
				sum_size += cur_upload.filesize - 0;
			}
		}
		return sum_size;
	};
	
	FilesUploadsQueue.prototype.GetProgressData = function (callback) {
		jswarn_l(5, "GetProgressData");
		var data = {'total': 0, 'uploaded': 0, 'count': 0, 'notdone_count': 0};
		var items = this.GetItemsArray(callback);
		for (var i = 0; i < items.length; i++) {
			var cur_upload = items[i];
			data.total += cur_upload.orgsize - 0;
			data.uploaded += cur_upload.bytes_done - 0;
			data.count++;
			if (!cur_upload.UploadComplete) data.notdone_count++;
		}
		return data;
	};	


/**
 * generic uploader class.
 */

function FilesUploader() {
	this.name = 'FilesUploader';
	
    this.readyTimeout = 3000;

	var thisForQ = this;
	var queue = new FilesUploadsQueue();
	queue.cbCheckUpload = function (id) {thisForQ.CheckUpload(id);};
	queue.cbStartUpload = function (id) {thisForQ.StartUpload(id); };
	queue.init()
	this.queue = queue;

	this.blockedBrowseEvents = new Hash();
	
	this.servers = [];

	this.SrvSpaceLeft = 0;
	this.SrvSpaceLeftCalc = 0;
}

	FilesUploader.prototype.init = function (data) {
		return true;
	};

	FilesUploader.prototype.initConf = function (confObj) {
		this.conf = confObj;
		if (!this.conf.startBehavior) this.conf.startBehavior = 0;
		if (!this.conf.browseText) this.conf.browseText = "Browse...";
		if (!this.conf.objHost) this.conf.objHost = "img.mail.ru";
		if (!this.conf.objURL) this.conf.objURL = "/mail/ru/images/files/uploader9.swf?v=2";
		if (!this.conf.objWidth) this.conf.objWidth = "77";
		if (!this.conf.objHeight) this.conf.objHeight = "22";
		if (!this.conf.objectId) this.conf.objectId = "flashFuploader";
		if (!this.conf.TestIdSuffix) this.conf.TestIdSuffix = "FormTester";
		if (!this.conf.uploadObjContainer) this.conf.uploadObjContainer = "uploadObjContainer";
		if (!this.conf.fakeJsButton) this.conf.fakeJsButton = false;
		if (!this.conf.Lang) this.conf.Lang = "RU";
		if (!this.conf.fakeJsButtonClassPrefix) this.conf.fakeJsButtonClassPrefix = "FakeJsButton";
		//this.conf.useButtonGlyph variables: + this.conf.buttonGlyphURL
		if (!this.conf.buttonGlyphHost) this.conf.buttonGlyphHost = "http://img.imgsmail.ru";

		if (typeof this.conf.wmode == "undefined") this.conf.wmode = true;
		if (!this.conf.SourceHost) {
			this.conf.SourceHost = escape(location.hostname);
		} else {
			this.conf.SourceHost = escape(this.conf.SourceHost);
		}

		this.upObjContainer = document.getElementById(this.conf.uploadObjContainer);

		if (this.conf.startBehavior == 2) {
			//this.upObjContainer.style.border = "solid #0000FF";
			this.upObjContainer.style.overflow = "hidden";
			this.upObjContainer.style.width = "1px";
			this.upObjContainer.style.height = "1px";
		}
	};

	FilesUploader.prototype.onUploadPrepared = function (id) {
		jswarn_l(1, 'onUploadPrepared');
		this.SetItemStatus(id, this.queue.STATUS_READY);
		if (this.onUploadReady) this.onUploadReady(id);
	};

	FilesUploader.prototype.ChooseServer = function (id, name, size, browsePackStamp, browsePackCount) {
		var serverid = -1;
		var server_name = "noApprServer";
		var issueList = [];
		issueList.push({'id': -1, 'uid': 'global', 'errors': []});
		if (size == 0) issueList[0].errors.push(gErr.ISEMPTY);
		if (this.queue.ListLimitExceeded) {
			issueList[0].errors.push(gErr.TOOMANY);
			if (this.blockedBrowseEvents.hasItem(browsePackStamp)) {
				firstBrowseFile = false;
				return -2;
			}
			this.blockedBrowseEvents.setItem(browsePackStamp, {'browsePackCount': browsePackCount, 'lastErrCode': gErr.TOOMANY});
		}
		if (issueList[0].errors.length == 0) {
			for(var i=0 ; i < this.servers_sorted.length ; i++) {
				var cur_server;
				for(var j=0 ; i < this.servers.length ; j++) {
					if (this.servers[j].uid == this.servers_sorted[i].uid) {
						cur_server = this.servers[j];
						break;
					}
				}
				var sizeForServer = cur_server.CalculateServerSize(size)
				issueList.push({'id': cur_server.id, 'uid': cur_server.uid, 'errors': []})
				var curSrvIdx = issueList.length-1;
				if ( !(
					(cur_server.SrvListLimitExceeded)
					|| (cur_server.SrvQueueLength >= cur_server.SrvListLimit)
					|| (cur_server.SrvSpaceLeftCalc < sizeForServer && !cur_server.DontCheckSpace)
					|| (cur_server.MaxFileSize < sizeForServer)
					)) {
					jswarn_l(3, "Why " + i + "?: lim:" + (cur_server.SrvListLimitExceeded ? 1: 0)+ '; limQ:' + (cur_server.SrvQueueLength +1) + '>=' + cur_server.SrvListLimit + '=' + (cur_server.SrvQueueLength + 1 >= cur_server.SrvListLimit ? 1: 0) + '; spc:' + (cur_server.SrvSpaceLeftCalc < sizeForServer ? 1 : 0) + "; MaxS:" + (cur_server.MaxFileSize < sizeForServer ? 1 : 0));
					var canuseit = false;
					if (cur_server.UserAgreed === true) {
						canuseit = true;
					} else if (cur_server.UserAgreed === false) {
						canuseit = false;
					} else {
						if (cur_server.cbOnFirstUse) {
							canuseit = cur_server.UserAgreed = cur_server.cbOnFirstUse();
						} else {
							canuseit =  cur_server.UserAgreed = true;
						}
					}
					if (canuseit) {
						serverid = cur_server.id;
						server_name = cur_server.name;
						break;
					} else {
						issueList[curSrvIdx].errors.push(gErr.DENIED);
					}
				} else {
					jswarn_l(4, "limit: " + cur_server.SrvListLimitExceeded + "; ql:" + cur_server.SrvQueueLength + "; Sl:" + cur_server.SrvListLimit);
					if ((cur_server.SrvListLimitExceeded) || (cur_server.SrvQueueLength >= cur_server.SrvListLimit)) {
						issueList[curSrvIdx].errors.push(gErr.TOOMANY);
					}
					if (cur_server.SrvSpaceLeftCalc < sizeForServer && !cur_server.DontCheckSpace) issueList[curSrvIdx].errors.push(gErr.NOSPACE);
					if (cur_server.MaxFileSize < sizeForServer) issueList[curSrvIdx].errors.push(gErr.TOOBIG);
					jswarn_l(3, "Why not " + cur_server.uid + "? " + issueList[curSrvIdx].errors.join());
				}
			}
		} else {
			jswarn_l(3, "Why global failure? " + issueList[0].errors.join());
		}
		jswarn_l(1, 'ChoosedServer: ' + server_name + '/' + serverid);
		if (serverid == -1) this.ProcessIssuesList(issueList, id, name, size);

		if (this.prevBrowseEventStamp != browsePackStamp) {
			jswarn_l(5, "new Stamp logged " + this.prevBrowseEventStamp +  " / " + browsePackStamp);
			//this.blockedBrowseEvents.removeItem(prevBrowseEventStamp);
			this.prevBrowseEventStamp = browsePackStamp;
		}
		return serverid;
	};

	FilesUploader.prototype.PrepareUpload = function (id) {
		jswarn_l(1, 'bPrepareUpload');
		var _this = this;
		window.setTimeout(function() {_this.onUploadPrepared(id);},  this.readyTimeout);
	};

	FilesUploader.prototype.onBrowseDone = function (item, name, size, browsePackStamp, browsePackCount) {
		jswarn_l(1,'GonBrowseDone');
		var id = 0;
		if (typeof item == "object") {
			id = GetRandomID();
			name = this.GetFileNameOnly(item.value) + "";
			size = -1;
			browsePackStamp = new Date().valueOf();
			browsePackCount = 1;
		} else {
			id = item;
		}
		if (name.length == 0) return;

		var serverid = this.ChooseServer(id, name, size, browsePackStamp, browsePackCount);
		jswarn_l(3,'Got Server ' + serverid);
		if (serverid < 0) return false;

		if (this.onFileChoose) {
			var fixedsize = this.servers[serverid].CalculateServerSize(size);
			if (this.onFileChoose(id, name, fixedsize, this.servers[serverid], this.queue.GetUIidArray())) {
				this.queue.AddItem(id);
				this.queue.ChangeItem(id, {'id': id, 'filename': name, 'filesize': fixedsize, 'serverid': serverid, 'orgsize': size, 'bytes_done': 0});
				this.CheckQueue();
				this.ProcessGlobalData({'serverid': serverid});
				this.PrepareUpload(id);

				if (typeof item == "object") this.CreateBackend(id, item);

				this.queue.MonitorQueue(); // shake the queue to force callbacks to fire right now
			}
		}
	};

	FilesUploader.prototype.StartUpload = function (id) {
		var uploadObj = this.queue.GetItem(id);
		var UpServer = this.GetFileServer(id);
		jswarn_l(1,'bStartUpload');
		if (!uploadObj || uploadObj.status != this.queue.STATUS_READY) return false;

		var anyIssues = false;
		if (!this.UpdateUploaderData(id)) { anyIssues = true };
		if (this.onBeforeUpload && !anyIssues) {
			var onBeforeUploadResult = this.onBeforeUpload(id);
			if (!onBeforeUploadResult) {
				this.SetItemStatus(id, this.queue.STATUS_FAILED);
				uploadObj.StartFailReason = "PreCheck";
				this.LogEvents(4, UpServer, uploadObj);
				if (this.onUploadError) this.onUploadError(id, gErr.DENIED, gErr.THROWN);
				return false;
			}
		}

		if (!anyIssues){
			anyIssues = this.LaunchUpload(id);
		}

		if (anyIssues) {
			this.SetItemStatus(id, this.queue.STATUS_BROKEN);
			uploadObj.StartFailReason = "NoObj";
			this.LogEvents(4, UpServer, uploadObj);
			if (this.onUploadError) this.onUploadError(id, gErr.START, gErr.THROWN);
			return false;
		} else {
			this.SetItemStatus(id, this.queue.STATUS_INPROGRESS);
			this.queue.ChangeItem(id, {'StartedOn': new Date().valueOf(), 'availspacebefore': UpServer.SrvSpaceLeft});
			this.LogEvents(1, UpServer, uploadObj);
			if (this.onUploadStart) this.onUploadStart(id);
			return true;
		}
	};

	FilesUploader.prototype.StopUpload = function (id) {
		return true;
	};

	FilesUploader.prototype.RetryUpload = function (id) {
		jswarn_l(1,'bRetryUpload');
		this.SetItemStatus(id, this.queue.STATUS_NEW);
		this.PrepareUpload(id);
		//this.queue.SetItem(id, {'status': this.queue.STATUS_NEW});
		if (this.onUploadRetry) this.onUploadRetry(id);
	};

	FilesUploader.prototype.DiscardUpload = function (id) {
		return true;
	};

	FilesUploader.prototype.TerminateUpload = function (id) {
		jswarn_l(1,'bTerminateUpload /' + id);
		this.DiscardUpload(id);
		if (this.onBeforeUploadTerminate) {
			var dataObj = this.queue.GetItem(id);
			var backupObj = this.onBeforeUploadTerminate(id, dataObj);
			var _this = this;
			var serverObj = this.GetFileServer(id);
			if (backupObj) {
				serverObj.DeleteFile(backupObj, function (id) { _this.BuryUpload(id)} );
			} else {
				if (dataObj.status == this.queue.STATUS_INPROGRESS) this.LogEvents(5, serverObj, dataObj);
				this.BuryUpload(id);
			}
		} else {
			this.BuryUpload(id);
		}
	};

	FilesUploader.prototype.CheckUpload = function (id) {
		return true;
	};

	FilesUploader.prototype.cbUploadCompleted = function (id, extraData) {
		jswarn_l(1,'bcbUploadCompleted /id=' + id);
		var UpServer = this.GetFileServer(id);
		var dataObj = UpServer.cbParseUploadResponse(extraData);
		dataObj.CompletedOn = new Date().valueOf();
		if (!dataObj.filesize) {
			//merged old data with new, overwrite old
			dataObj = this.queue.ChangeItem(id, dataObj);
			this.SetItemStatus(id, this.queue.STATUS_FAILED);
			this.ProcessGlobalData(dataObj);

			dataObj.extraData = extraData;
			this.LogEvents(3, UpServer, dataObj);
			if (this.onUploadError) this.onUploadError(id, dataObj.errorcode, gErr.THROWN, dataObj);
			jswarn_l(2,'bLogic error: code='  + dataObj.errorcode + "; vsize=" + dataObj.filesize);
		} else {
			dataObj = this.queue.ChangeItem(id, dataObj);
			this.SetItemStatus(id, this.queue.STATUS_DONE);
			this.ProcessGlobalData(dataObj);
			if (dataObj.linkcode) UpServer.cbUpdateGroup(1, dataObj.linkcode);
			this.LogEvents(2, UpServer, dataObj);
			if (this.onUploadCompleted) this.onUploadCompleted(id, dataObj, extraData);
			this.DiscardUpload(id);
			this.queue.CompletedUploads++;
		}
	};

	FilesUploader.prototype.cbUploadError = function (id, errorCode, isCustom) {
		if (!isCustom) isCustom = gErr.NATURAL;
		jswarn_l(1,'bcbUploadFailed /code=' + errorCode + ', ' + isCustom);
		this.SetItemStatus(id, this.queue.STATUS_FAILED);
		var dataObj = this.queue.GetItem(id);
		var UpServer = this.GetFileServer(id);
		dataObj.errorcode = errorCode;
		this.LogEvents(3, UpServer, dataObj);
		if (this.onUploadError) this.onUploadError(id, errorCode, isCustom);
	};

	FilesUploader.prototype.cbUploadProgressed = function (id, bytesDone, bytesTotal) {
		jswarn_l(5,'b% ' + bytesDone + ' ' + bytesTotal);
		this.queue.ChangeItem(id, {'LastUpdatedOn': new Date().valueOf(), 'bytes_done': bytesDone});
		if (bytesDone == bytesTotal) this.queue.ChangeItem(id, {'UploadComplete': true});
		if (this.onUploadProgress) this.onUploadProgress(id, bytesDone, bytesTotal);
		if (this.cbSpeedMeterTick) this.cbSpeedMeterTick(this.queue.GetProgressData());
	};

	FilesUploader.prototype.ProcessIssuesList = function (issueObj, id, name, size) {
		if (this.onUploadIssueNotification) {
			var data = {};
			data.fileUid = id;
			data.fileName = name;
			data.fileSize = size;

			this.onUploadIssueNotification(data, issueObj);
		}
	};


	FilesUploader.prototype.ProcessGlobalData = function (dataObj) {
		var UpServer = this.servers[dataObj.serverid];
		if (UpServer) {
			var sizeOfActiveQueue = this.queue.CalcSize(function (o) { return ((o.serverid == UpServer.id) && (o.status <= 2))});
			sizeOfActiveQueue = sizeOfActiveQueue < 0 ? 0 : sizeOfActiveQueue;
			var sizeOfGoodQueue = this.queue.CalcSize(function (o) { return ((o.serverid == UpServer.id) && (o.status <= 3))});
			if (UpServer.DontTakeNewStat) {
                //UpServer.SrvSpaceLeft = 100*1024*1024;
				UpServer.SrvSpaceLeftCalc = UpServer.SrvSpaceLeft - sizeOfGoodQueue;
				jswarn_l(6, "GoodQ_S=" + sizeOfGoodQueue + "; SpaceLC=" + UpServer.SrvSpaceLeftCalc);
			} else {
                if (dataObj.availsize) {
					UpServer.SrvSpaceLeft = dataObj.availsize;
				}
				UpServer.SrvSpaceLeftCalc = UpServer.SrvSpaceLeft - sizeOfActiveQueue;
			}
			var QueueLength = this.queue.CalcLength(function (o) { return ((o.serverid == UpServer.id) && (o.status <= 2))});
			jswarn_l(5, 'gData:sId=' + UpServer.id + '/ql=' + QueueLength + '/' + formatFsize(sizeOfActiveQueue, 1) + ',SpPd=' + formatFsize(UpServer.SrvSpaceLeftCalc, 2) + ',SpSr=' + formatFsize(UpServer.SrvSpaceLeft, 2))
			if (this.onStatisticsChange) this.onStatisticsChange(UpServer);
			return true;
		}
	};


	FilesUploader.prototype.BuryUpload = function (id) {
		if (this.queue.HasItem(id)) {
			var UpServer = this.GetFileServer(id);
			var item = this.queue.GetItem(id);
			jswarn_l(1, "Bury.id="  + id + ';size=' + item.filesize);
			jswarn_l(5, "Bry.SpcBfr= " + item.availspacebefore + "/SpcAvl=" + item.availsize);
			if (!UpServer.DontTakeNewStat) {
    			jswarn_l(6, "SpcSrv=" + UpServer.SrvSpaceLeft + "; Calc=" + UpServer.SrvSpaceLeftCalc);jswarn_l(3, "delspace=" + item.delfreesspace + "; upm=" + UpServer.UpMode);
    			//if (item.filesize && (item.delfreesspace || UpServer.UpMode == "legacy") && item.availspacebefore != item.availsize) {
    			if (item.filesize && (item.delfreesspace || UpServer.UpMode == "legacy")) {
    				if (item.fileid) { UpServer.SrvSpaceLeft += item.filesize;};
    				// if upload was failed but availsize was sent, correct filesize was
    				if ( !(item.errorText && item.availsize)) { UpServer.SrvSpaceLeftCalc += item.filesize;};
    			}
			}
			this.queue.RemoveItem(id);
			if (UpServer.DontTakeNewStat) {
				this.ProcessGlobalData({'serverid': UpServer.id});
			}
			if (this.onStatisticsChange) this.onStatisticsChange(UpServer);
			jswarn_l(6, 'Buried:sr=' + UpServer.SrvSpaceLeft + ';cl=' + UpServer.SrvSpaceLeftCalc);
			this.CheckQueue(UpServer);
		} else {
			jswarn_l(5, "BuryUpload NOID/"  + id)
		}
		return true;
	};

	FilesUploader.prototype.GetFileServer = function (id) {
		if (this.queue.HasItem(id)) {
			var serverid = this.queue.GetItem(id).serverid;
			var resObj = this.servers[serverid];
			return resObj;
		}
		return;
	};

	FilesUploader.prototype.CastToFileServer = function (value) {
		if (typeof value == "number") {
			jswarn_l(6, 'CastToFileServer (num)/id=' + value);
			var obj = this.servers[value];
			return obj;
		} else {
			jswarn_l(6, 'CastToFileServer (obj)/id=' + value.id);
			return value;
		}
	};

	FilesUploader.prototype.RestoreServerData = function (UpServerPassed, serverData) {
		jswarn_l(1, "RestoreServerData")
		var UpServer = this.CastToFileServer(UpServerPassed);
		UpServer.isInitialized = true;
		if (UpServer.isInitOk || serverData.code || UpServer.UpMode == "legacy"){
			if (serverData.code || UpServer.UpMode == "legacy") UpServer.isInitOk = true;
			var list = serverData.list;
			var stat = serverData.stat;
			jswarn_l(3, "RestoreServerData:" + UpServer.name + '/' + list.length + '/' + stat.availsize)
			if (serverData.code) UpServer.GroupCode = serverData.code;
			for(var i=0 ; i < list.length ; i++) {
				var dataObj = list[i];
				if (this.onFileChoose) {
					dataObj.id = GetRandomID();
					//dataObj.availsize = 0;
					//dataObj.availspacebefore = -2;
					this.onFileChoose(dataObj.id, dataObj.filename, dataObj.filesize, UpServer, this.queue.GetUIidArray());
					this.onUploadCompleted(dataObj.id, {'filesize': dataObj.filesize, 'fileid': dataObj.fileid});
					this.queue.AddItem(dataObj.id);
					dataObj.CompletedOn = new Date().valueOf();
					this.queue.ChangeItem(dataObj.id, dataObj);
					dataObj = this.queue.ChangeItem(dataObj.id, {'serverid': UpServer.id, 'status': this.queue.STATUS_DONE});
				}
			}
			if (stat.availsize) UpServer.SpaceLeft(stat.availsize);
			this.CheckQueue(UpServer);
			this.ProcessGlobalData({'serverid': UpServer.id, 'availsize' : stat.availsize});
		}
		this.CheckInitStatus();
	};

	FilesUploader.prototype.CheckInitStatus = function () {
		var initializedNow = 0;
		var initializedWellNow = 0;
		for(var i=0 ; i < this.servers.length ; i++) {
			if (this.servers[i].isInitialized == true) initializedNow++;
			if (this.servers[i].isInitOk == true) initializedWellNow++;
		}
		if ((this.cbOnInitComplete) && (this.servers.length == initializedNow)) {
			jswarn_l(3, "CheckInitStatus: initWell=" + initializedWellNow + ", Total=" + initializedNow);
			this.EnableUploader();
			this.cbOnInitComplete(initializedWellNow, initializedNow);
			this.Initialized = true;
		} else {
			jswarn_l(5, '>CheckInitStatus, tmp: w=' + initializedWellNow + "/t=" + initializedNow);
		}
	};

	FilesUploader.prototype.AddUploadServer = function (serverObj) {
		serverObj.conf = this.conf;
		var obj = this.servers.push(serverObj);
		var serverid = this.servers.length-1;
		serverObj= this.servers[serverid]
		serverObj.id = serverid;
		jswarn_l(3, 'Added Server ' + serverObj.name + '/id=' + serverid + ', uid=' + serverObj.uid);
		this.ProcessGlobalData({'serverid': serverid});

		this.servers_sorted = [];
		for(var i=0 ; i < this.servers.length ; i++) {
			this.servers_sorted[i] = {};
			this.servers_sorted[i].uid = this.servers[i].uid;
			this.servers_sorted[i].name = this.servers[i].name;
			this.servers_sorted[i].ServerPriority = this.servers[i].ServerPriority;
		}
		this.servers_sorted.sort(function (a,b)	{ return a.ServerPriority - b.ServerPriority;} );

		if (serverObj.GroupCode && serverObj.AutoRestore) {
			jswarn_l(4, 'AutoRestore for ' + serverObj.name);
			this.InitializeUploadServer(serverObj)
		}
		return serverid;
	};

	FilesUploader.prototype.InitializeUploadServer = function (UpServerPassed) {
		var serverObj = this.CastToFileServer(UpServerPassed);
		jswarn_l(2, 'SyncedRestore for ' + serverObj.name);
		var _this = this;
		if (serverObj.GroupCode) {
			return serverObj.RequestInitData(serverObj.GroupCode, function (UpServer, dataObj) {_this.RestoreServerData(UpServer, dataObj)});
		} else {
			serverObj.isInitialized = true;
			serverObj.isInitOk = true;
			this.CheckInitStatus();
			return true;
		}
	};

	FilesUploader.prototype.LogEvents = function (eventTypeId, UpServer, dataObj) {
		//1 - upload start
		//2 - upload completed
		//3 - upload failed
		//4 - upload start issue
		//5 - upload canceled
		//9 - bufferIssue //planned event

		// Logging should not fail, but in any case, let it be wrapped to be sure... we can do without it anyway
		try {
			var uid = "unknown";
			if (UpServer) uid = UpServer.uid;
			var statName = "unknown";
			var names = ['Start', 'Done', 'Fail', 'Issue', 'Cancel'];
			if ((eventTypeId - 1) <= names.length) statName = names[eventTypeId-1];

			if (window.FilesRadar && eventTypeId > 0 && eventTypeId < 6) {
    			if (eventTypeId > 1 && eventTypeId < 4) {
                    if (dataObj.CompletedOn && dataObj.LastUpdatedOn) {
    					FilesRadar.timeEvent('Up'+statName+"_A", dataObj.CompletedOn - dataObj.LastUpdatedOn)
    				} else if (!dataObj.CompletedOn && eventTypeId == 3) {
                        var precents = 0;
						var fz = dataObj.filesize - 0;
						var bd = dataObj.bytes_done - 0;
                        if (fz && bd) { precents = Math.floor( bd / fz * 100);}
						FilesRadar.timeEvent('UpError_A', precents);
        			}
   				} else if (eventTypeId == 1) {
                    FilesRadar.timeEvent('Up'+statName+"_A", this.queue.CompletedUploads);
    			} else if (eventTypeId == 4) {
    					FilesRadar.timeEvent('Up'+statName+"_A", this.queue.CompletedUploads);
    			} else if (eventTypeId == 5) {
                    if (dataObj.StartedOn) {
    					FilesRadar.timeEvent('Up'+statName+"_A", (new Date()).getTime() - dataObj.StartedOn);
    				}
    			}
    			FilesRadar.sendData();
    		}


			if (!dataObj) dataObj = {};
			var str = "logV=1;srv="+uid;
			if (dataObj.fileid) str += ";fileid=" + dataObj.fileid;
			var userMore = "";
			if (dataObj.userid) {
				str += ";userid=" + dataObj.userid;
			} else if (window.gUserId) {
				userMore = "g";
				str += ";userid=" + (window.gUserId - 0);
			}
			if (window.gLoggedIn) {
				str += ";uinfo=";
				if (window.gLoggedIn === false) {
					str += "A" + userMore;
				} else {
					str += "R" + userMore;
				}
			}
			if (dataObj.filename) str += ";filename=" + dataObj.filename;
			if (dataObj.filesize) str += ";filesize=" + dataObj.filesize;
			if (dataObj.orgsize && dataObj.filesize && (dataObj.filesize != dataObj.orgsize)) str += ";realsize=" + dataObj.orgsize;
			if (dataObj.StartFailReason) str += ";issuereason=" + dataObj.StartFailReason;
			if (dataObj.errorcode) str += ";errorcode=" + dataObj.errorcode;
			if (eventTypeId == 2) {
				if (dataObj.filesize) str += ";upspeed=" + Math.floor(dataObj.filesize / (dataObj.CompletedOn - dataObj.StartedOn));
				if (dataObj.StartedOn && dataObj.CompletedOn) str += ";timetaken=" + (dataObj.CompletedOn - dataObj.StartedOn);
			}
			if (dataObj.StartedOn && dataObj.LastUpdatedOn) {
				str += ";lastprogresson=" + (dataObj.LastUpdatedOn - dataObj.StartedOn);
			}
			if (dataObj.filesize && dataObj.bytes_done && dataObj.filesize > 0) {
				str += ";donepercent=" + Math.floor(dataObj.bytes_done / dataObj.filesize * 100);
			}

			if (dataObj.extraData) str += ";extradata=" + dataObj.extraData;
			if (window.BrowserDetect) str += ";sw=" + BrowserDetect.browser + '/' + BrowserDetect.version + '/' + BrowserDetect.OS;
			if (this.shortname) str += ";uptype=" + this.shortname;
			if (UpServer.UpMode) str += ";upmode=" + UpServer.UpMode;
			if (dataObj.id) str += ";uid=" + dataObj.id;

			var statURI = this.servers[0].GetAjaxURI();
			if (this.statURI) statURI=this.statURI;
			jswarn_l(5, "[!]: " + statName + ' ' + str);
			var st = AjaxFlash.SendRequest(
				{uri: statURI  + '?f=' + statName
				,funcName: 'statUp' + statName
				,funcArgs: [str]
			});
		} catch (e) {}
	};

	FilesUploader.prototype.SetItemStatus = function (id, status) {
		jswarn_l(2, 'Up SetItemStatus /' + status + ', ' + id);
		this.queue.SetItemStatus(id, status);
		var serverObj = this.GetFileServer(id);
		if (serverObj) {
			this.CheckQueue(serverObj);
			if (status > this.queue.STATUS_INPROGRESS) {
				if (serverObj.LogGroupChangeToServer && serverObj.onGroupListChange) {
					serverObj.onGroupListChange(this.queue.GetPackedList(function (o) { return (o.serverid == serverObj.id)} ));
				}
			}
		}
	};
	
	FilesUploader.prototype.EnableUploaderContainer = function () {
		if (this.conf.startBehavior == 2) {
				this.upObjContainer.style.overflow = "visible";
				//this.upObjContainer.style.width = this.conf.objWidth + "px";
				//this.upObjContainer.style.height= this.conf.objHeight + "px";
				this.upObjContainer.style.width = "";
				this.upObjContainer.style.height= "";
		}
	};

	FilesUploader.prototype.EnableUploader = function () {
		jswarn_l(1, "Gen:enableUploader");
		this.EnableUploaderContainer();
	};
	
	FilesUploader.prototype.StopActiveUploads = function () {		
		jswarn_l(5, "StopActiveUploads");		
		var items = this.queue.GetItemsArray();
		for (var i = 0; i < items.length; i++) {
			var cur_upload = items[i];
			if (cur_upload.fileid && cur_upload.status == this.STATUS_INPROGRESS) {
				this.StopUpload(cur_upload.id);
			}
		}
	};
	
	FilesUploader.prototype.GetFileNameOnly = function (FileName) {
		var lastPathDelimiter = FileName.lastIndexOf("\\");
		if (lastPathDelimiter == -1) lastPathDelimiter = FileName.lastIndexOf("/");
		return FileName.substring(lastPathDelimiter+1);
	};


function FilesFlashUploader (uploadContainerId) {
	FilesUploader.call(this, uploadContainerId);
	this.name = 'FilesFlashUploader';
	this.shortname = 'flash';
}

inherit(FilesFlashUploader, FilesUploader);

	FilesFlashUploader.prototype.init = function (confObj) {
		jswarn_l(1,'FL-init');
		this.initConf(confObj);
		var TesterDiv = document.createElement("div");
		TesterDiv.id = this.conf.objectId + this.conf.TestIdSuffix;
		TesterDiv.style.display ="none";
		this.upObjContainer.parentNode.appendChild(TesterDiv);

		this.CreateBackend();
	};


	FilesFlashUploader.prototype.CreateBackend = function (confObj) {
		if(typeof swfobject == "undefined") return this.CreateBackend_Classic();

		var _this = this;
		//if (swfobject.hasFlashPlayerVersion("9.0.28")) {
		var UploaderDiv = document.createElement("div");
		UploaderDiv.id = this.conf.objectId;
		this.upObjContainer.appendChild(UploaderDiv);

		var fn = function() {
			var cf = _this.conf;
			jswarn_l(2, "DOM:SWFObject: /" + cf.objectId);
			var att = { data: "http://" +cf.objHost + cf.objURL, width: cf.objWidth, height: cf.objHeight };
			var par = {
						flashvars:"BrowseText=" + cf.browseText  + (cf.useButtonGlyph && cf.buttonGlyphURL ? "&buttonURL=" + cf.buttonGlyphHost + cf.buttonGlyphURL : ""), // + "&startBehavior=" + "1",
						menu: "false",
						loop: "false",
						allowScriptAccess: "always"};
			if (cf.wmode) par.wmode = "opaque";
			var id = cf.objectId;
			swfobject.createSWF(att, par, id);
			_this.FlashFormFix();
			if (cf.cbOnBackendCreate) cf.cbOnBackendCreate("flash");
			try {document.getElementById(cf.objectId).onclick = function(){document.getElementById(cf.objectId).blur()}} catch(e){};

		};
		swfobject.addDomLoadEvent(fn);
	}

	FilesFlashUploader.prototype.CreateBackend_Classic = function (confObj) {
		/* required parameters:
		this.conf.objHost
		this.conf.objURL
		this.conf.objWidth
		this.conf.objHeight
		this.conf.browseText
		this.conf.startBehavior
		this.conf.objectId
		*/
		var intext;
		intext='<object type="application/x-shockwave-flash" data="http://' + this.conf.objHost + this.conf.objURL + '" width="' + this.conf.objWidth + '" height="' + this.conf.objHeight + '" id="' + this.conf.objectId + '" name="' + this.conf.objectId + '"> ';
		intext+='<param name="movie" value="http://' + this.conf.objHost + this.conf.objURL + '" />';

		intext+='<param name="allowScriptAccess" value="always" />';
		intext+='<param name="loop" value="false" />';
		intext+='<param name="menu" value="false" />';
		intext+='<param name="quality" value="low" />';
		if (this.conf.wmode) intext+='<param name="wmode" value="opaque">';


		intext+='<param name="type" value="application/x-shockwave-flash" />';
		intext+='<param name="codebase" value="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" />';
		intext+='<param name="pluginspage" value="http://www.adobe.com/go/getflashplayer" />';
		intext+='<param name="FlashVars" value="BrowseText=' + this.conf.browseText + '" />'; //'&startBehavior=' + this.conf.startBehavior +
		intext+='</object>';
		
		var UploaderDiv = document.createElement("div");
		UploaderDiv.id = this.conf.objectId;
		UploaderDiv.innerHTML = intext;
		this.upObjContainer.appendChild(UploaderDiv);

		this.FlashFormFix();
	};
	
	FilesFlashUploader.prototype.FlashFormFix = function (confObj) {
		//!SWF ExternalInterface() Form Fix - http://http://www.teratechnologies.net/stevekamerman/
		var obj = document.getElementById(this.conf.objectId + this.conf.TestIdSuffix);
		if (typeof obj == "object") {
			var path = document;
			var error = false;
			var testnode = obj;
			while (obj && obj.parentNode) {
				obj = obj.parentNode;
				if(obj.nodeName.toLowerCase() == "form"){
					if(obj.name != undefined && obj.name != null && obj.name.length > 0){
						path = path.forms[obj.name];
						jswarn_l(5, "Ext.Interface_FormFix:" + obj.name);
					}else{
						jswarn_l(5, "Ext.Interface_FormFix: FORM WO NAME!!!");
						error = true;
					}
				//break;
				}
			}
			if (testnode) testnode.parentNode.removeChild(testnode);
			if(error) return false;
			window[this.conf.objectId]=path[this.conf.objectId];
		}
		return true;
	}

	FilesFlashUploader.prototype.GetBackendObj = function (movieName) {
		if (!movieName) movieName = this.conf.objectId;
		var obj;
		var anyIssues = false;
		if (typeof swfobject != "undefined" && !swfobject.XFilesUploaderSkipFlag) {
			jswarn_l(5, 'UpObj: Trying SWF way');
			var isIE = navigator.appName.indexOf("Microsoft") != -1;
			obj =  (isIE) ? window[movieName] : document[movieName];

			if (typeof obj == "undefined") anyIssues = true;
			if (!anyIssues) {
				jswarn_l(1, 'UpObj via SWF - OK: '  + typeof obj);
				return obj;
			} else {
				jswarn_l(0, 'UpObj via SWF - failed: '  + typeof obj);
				swfobject.XFilesUploaderSkipFlag = true;
			}
		}

		jswarn_l(5, 'UpObj: Trying GEBI way /as=' + anyIssues + ', /swf=' + typeof swfobject);
		anyIssues = false;
		obj = document.getElementById(movieName);

		if (typeof obj != "object") anyIssues = true;
		if (!anyIssues) {
			jswarn_l(1, 'UpObj via GEBI - OK: ' + typeof obj);
		} else {
			jswarn_l(0, 'UpObj via GEBI - failed: ' + typeof obj);
		}
		return obj;
	};

	FilesFlashUploader.prototype.UpdateUploaderData = function (id) {
		var mc = this.GetBackendObj();
		var postData = '';
		var UpServer = this.GetFileServer(id);
		var uploadURL = UpServer.GetUploadURI();
		postData += 'swf=1&upload=1';
		var serverParams = UpServer.GetServerParams();
		if (serverParams) postData += "&" + serverParams;
		postData += '&MRIS_IDS=' + this.queue.GetPackedList(function (o) { return (o.serverid == UpServer.id)});
		if (id) { postData += '&fuid=' + id };
		if (this.conf.isLoggedIn) postData+='&loggedin=1';
		var authData = UpServer.PrepareAuthData();
		if (authData.UsePostAuth == true) {
			postData += '&cookies=' + escape(authData.Cookie);
		} else {
			uploadURL += '?swf=1&cookies=' + escape(authData.Cookie);
		}

		if ((navigator.appName.indexOf('Microsoft') != -1) && (navigator.appVersion.indexOf('MSIE 7.') > 0)) {
			postData += '&noencode=1';
		}
		jswarn_l(2,'FUp:' + uploadURL);
		try {
			mc.setUploadURL(uploadURL);
			mc.setPostData(postData);
		} catch(e) {return false;}
		return true;
	};


	FilesFlashUploader.prototype.LaunchUpload = function (id) {
		var anyIssues = false;
		jswarn_l(5,'Launching id=' + id);
		try {
			var mc = this.GetBackendObj();
			mc.startUpload(id);
		} catch (e) {anyIssues = true}
		return anyIssues;
	};
	
	FilesFlashUploader.prototype.DiscardUpload = function (id) {
		jswarn_l(1,'IDiscardUpload');
		var mc = this.GetBackendObj();
		mc.cancelUpload(id);
	};

	FilesFlashUploader.prototype.StopUpload = function (id) {
		jswarn_l(1,'StopUpload /' + id);
		//logical stop is not supported, in fact it's the same as terminate
		this.DiscardUpload(id);
		this.SetItemStatus(id, this.queue.STATUS_STOPPED);
		if (this.onUploadStop) this.onUploadStop(id);
	};

	FilesFlashUploader.prototype.EnableUploader = function () {
		jswarn_l(1, "Flash:enableUploader");
		this.EnableUploaderContainer();
		try {
			var mc = this.GetBackendObj();
			mc.enableUploader();
		} catch (e) {}
	};

	FilesFlashUploader.prototype.CheckQueue = function (UpServer) {
		var turnItOn;
		turnItOn = this.queue.CheckQueueLength(UpServer);
		try {
			var mc = this.GetBackendObj();
			mc.setEnabled(turnItOn);
		} catch (e) {}
	};
	

function FilesIFrameUploader (uploadContainerId) {
	FilesUploader.call(this, uploadContainerId);
	this.name = 'FilesIFrameUploader';
	this.shortname = 'iframe';
}
inherit(FilesIFrameUploader, FilesUploader);

	FilesIFrameUploader.prototype.initHelpObject = function () {
		jswarn_l(0, "initHelpObject");
		if (document.getElementById('internal_upload_form') == null) {
			var html = '<form name="internal_upload_form" id="internal_upload_form" target="uFileIF1" action="/cgi-bin/files/fupload" method="post" enctype="multipart/form-data">';
			html += '<input type="hidden" name="upload" value="1" />';
			html += '<input type="hidden" name="iframe" value="1" />';
			//IE issue
			html += '<input type="hidden" name="fuid" value="0" />';
			html += '<input type="hidden" name="MRIS_IDS" value="." />';
			html += '<input type="hidden" name="upmode" value="" />';
			html += '<input type="hidden" name="groupcode" value="" />';
			html += '<input type="hidden" name="sourcehost" value="" />';

			html += '</form>';
			var UploaderDiv = document.createElement("div");
			UploaderDiv.id = "internal_form_container";
			UploaderDiv.innerHTML = html;
			document.body.appendChild(UploaderDiv);
			//document.write();
		}
		this.upform = document.forms['internal_upload_form'];
	};

	FilesIFrameUploader.prototype.init = function (confObj) {
		this.initConf(confObj);
		this.upObjContainer.innerHTML = ""; //delete flash object (if any) in any case to avoid double uploader
		if (this.conf.fakeJsButton) {
			this.upObjContainer.innerHTML = '<div id="homeOfInputs" class="' + this.conf.fakeJsButtonClassPrefix + '_' + this.conf.Lang + '"></div>';
			this.inputsPlace = document.getElementById('homeOfInputs');
		} else {
			this.inputsPlace = this.upObjContainer;
		}
		this.CreateBackend();
	};

	FilesIFrameUploader.prototype.CreateBackend = function (id, obj) {
		jswarn_l(4,'ICreateBackend');
		//Handle used one
		if (id && obj) {
			if (this.conf.fakeJsButton) {
				obj.className = "cachedInput"
			}
			obj.id = "cachedFileInput" + id;
			obj.name = "cachedFileInput" + id;
			obj.style.position = "absolute";
			obj.style.left = "-5000px";
			obj.onchange = function () {};
			this.CreateIframe(id)
		}
		//and create new instead of processed
		var _this = this;
		var newFileInput = document.createElement("input");
		newFileInput.type = "file";
		if (this.conf.fakeJsButton) {
			newFileInput.size = 1;
			newFileInput.className = "maskedInput";
		} else {
			newFileInput.size = 33;
		}
		newFileInput.id = "defFileNameToUpload";
		newFileInput.name = "defFileNameToUpload";
		newFileInput.onchange = function (el) {_this.onBrowseDone(this)};
		this.inputsPlace.appendChild(newFileInput);
	};

	FilesIFrameUploader.prototype.TerminateBackend = function (id) {
		jswarn_l(4,'ITerminateBackend');
		try {
			var elem = document.getElementById("cachedFileInput" + id);
			elem.parentNode.removeChild(elem);
		} catch (e) {jswarn_l(3,'NoInput.IsItDone?' + id);}
		this.TerminateIframe(id);
	};

	FilesIFrameUploader.prototype.CreateIframe = function (id) {
		jswarn_l(5,'ICreateIframe');
		var newDiv = document.createElement('div');
		var serverObj = this.GetFileServer(id);
		var ResetUrl = serverObj.GetResetURI();
		var newHtml ='<iframe name="uFileIF'+ id + '" id="uFileIF' +id + '" class="jsUploaderIframe" src="' + ResetUrl + '"></iframe>';
		newHtml +='</div>';
		newDiv.innerHTML = newHtml;
		newDiv.style.position = 'absolute';
		newDiv.style.left = '-1000px';
		this.upObjContainer.appendChild(newDiv);
		window.frames["uFileIF" + id].location = ResetUrl;
	};

    FilesIFrameUploader.prototype.TerminateIframe = function (id) {
		jswarn_l(5,'ITerminateIframe');
		try {
			var elem = document.getElementById("uFileIF" + id);
			elem.parentNode.removeChild(elem);
		} catch (e) {}
	};

	FilesIFrameUploader.prototype.DiscardUpload = function (id) {
		jswarn_l(1,'IDiscardUpload');
		this.TerminateBackend(id);
	};

	FilesIFrameUploader.prototype.UpdateUploaderData = function (id) {
		var postData = '';
		var UpServer = this.GetFileServer(id);
		var uploadURL = UpServer.GetUploadURI();

        if (!this.upform) this.initHelpObject();
		this.upform.action = uploadURL;
		this.upform.target = "uFileIF" + id;

		var arr = UpServer.GetServerParams("wantArray");
		arr.push({name: 'fuid', value: id});
		arr.push({name: 'MRIS_IDS', value: this.queue.GetPackedList(function (o) { return (o.serverid == UpServer.id)})})
		if (this.conf.isLoggedIn) arr.push({name: 'loggedin', value: 1});
  		for (i = 0; i < arr.length; i++) {
			if (arr[i].name && arr[i].value) {
				var el = this.upform[arr[i].name];
				if (el) {
					el.value = arr[i].value;
					jswarn_l(6,"Set " + arr[i].name + " = " +arr[i].value);
				} else {
					var inpt = document.createElement("input");
					inpt.type = "hidden";
					inpt.name = arr[i].name;
					inpt.value = arr[i].value;
					this.upform.appendChild(inpt);
					jswarn_l(6,"Created " + arr[i].name + " = " +arr[i].value);
				}
			}
		}
		jswarn_l(4,'ISetUp:' + uploadURL);
		return true;
	};
	
	FilesIFrameUploader.prototype.LaunchUpload = function (id) {
		var anyIssues = false;
		jswarn_l(5,'Launching id=' + id);
		var obj = document.getElementById("cachedFileInput" + id);
		// "steal it" for a while ;-)
		if (!this.upform) this.initHelpObject();
		this.upform.appendChild(obj);
		try {
			this.upform.submit();
		} catch(e) {
			jswarn_l(0,'#submitIssue');
			anyIssues = true;
		}
		// and return it back as new ;-)
		this.upObjContainer.appendChild(obj);
		return anyIssues;
	};

	FilesIFrameUploader.prototype.StopUpload = function (id) {
		jswarn_l(1,'StopUpload /' + id);
		window.frames["uFileIF" + id].location = this.GetFileServer(id).GetResetURI();;
		this.SetItemStatus(id, this.queue.STATUS_STOPPED);
		if (this.onUploadStop) this.onUploadStop(id);
	};

	FilesIFrameUploader.prototype.CheckUpload = function (id) {
		jswarn_l(3,'CheckUpload');
		var iframeWin = window.frames['uFileIF' + id];
		var elementPassMark = '';
		var elementPassMarkId = '';
		var markElement;
		try {
			markElement = iframeWin.document.getElementById('MRUploadQueueTaskMark');
		} catch(e) {jswarn_l(2,'onWatchdog_issue');}
		if (markElement) {
			return true;
		} else {
			this.cbUploadError(id, gErr.IFRAME, gErr.THROWN);
			return false;
		}
	};
	
	FilesIFrameUploader.prototype.CheckQueue = function (UpServer) {
		this.queue.CheckQueueLength(UpServer);
	};



//Map predefined callback functions to Obj external event handlers

function UploadDataCallback(id, extraData) {
	MRUUploader.cbUploadCompleted(id, extraData)
}
function UploadDoneCallback(id, extraData) {
	MRUUploader.cbUploadCompleted(id, extraData);
}

function UploadFailedCallback(id, errorCode, more) {
	MRUUploader.cbUploadError(id, errorCode);
}

function UploadProgressCallback(id, bytesDone, bytesTotal) {
	MRUUploader.cbUploadProgressed(id, bytesDone, bytesTotal);
}

//Flash only cb. For IF uploader it's internal.
function newUploadCallback (id, name, size, browsePackStamp, browsePackCount) {
	MRUUploader.onBrowseDone(id, name, size, browsePackStamp, browsePackCount);
}

function SpeedMeter (conf) {
	this.baseObjId = conf.baseObjId ? conf.baseObjId : 'pnlSpeedMeters';

	this.samples = new Array();
	this.speeds = new Array();
	this.minBuferLength = 7;
	this.maxSampleBuferLength = 30;
	this.maxSpeedBuferLength = 20;
	this.queueIsVisible = false;

	this.skipFirstSamples = 5;
};

SpeedMeter.prototype.EnablePanel = function (flag) {
	if (typeof document.getElementById(this.baseObjId) != "undefined") {
		if (flag) {
			document.getElementById(this.baseObjId).style.display = 'block';
		} else {
			document.getElementById(this.baseObjId).style.display = 'none';
		}
		this.queueIsVisible = flag ? true : false;
	}
}	

SpeedMeter.prototype.ResetMeter = function (id) {
	this.samples.length = 0;
	//this.speeds.length = 0;	
	this.queueIsVisible = false;
	this.EnablePanel(false);	
};

SpeedMeter.prototype.CheckUploadPiping = function (bytesRemain, uploaded) {	
}	

SpeedMeter.prototype.ProcessSpeedSmaple = function (curSample) {		
	jswarn_l(6, "ProcessSpeedSmaple " + this.skipFirstSamples + "/" + curSample.uploaded + '/' + curSample.total + '/' +curSample.count);

	if (this.skipFirstSamples > 0) {
		this.skipFirstSamples --;
		return;
	}

	curSample.time = new Date().valueOf();
	var prevSample;
	var delta = {};
	if (this.samples.length > 0) {prevSample =  this.samples[this.samples.length-1]};

	if (prevSample) {
		if (curSample.time - prevSample.time < 500) return;
		delta.time = curSample.time - prevSample.time;
		delta.size = Math.abs(curSample.uploaded - prevSample.uploaded);				
	}
	
	//First buffer	
	this.samples.push(curSample);
	if (this.samples.length > this.maxSampleBuferLength) {this.samples.splice(0, this.samples.length - this.maxSampleBuferLength)};

	if (curSample.count != prevSample.count) {
		//skip it
		return true;
	}	
	//Second buffer
	//Absolute current speed   
	var curSpeed = ((delta.size / delta.time) * 1000) - 0;
	if (curSpeed > 0) this.speeds.push(curSpeed);
	if (this.speeds.length > this.maxSpeedBuferLength) this.speeds.splice(0, this.speeds.length - this.maxSpeedBuferLength)
	
	//Get sum/avarege value of the buffer
	var avgBufferSpeed = 0;
	for (var j=0; j < this.speeds.length; j++) {
		avgBufferSpeed = avgBufferSpeed + (this.speeds[j]);
	}
	avgBufferSpeed = Math.round(avgBufferSpeed / this.speeds.length);

	var avgSpeed = 0;
	var k=1;
	var k_count = 0;
	for (var j=0; j < this.speeds.length; j++) {
		if (Math.abs(avgBufferSpeed-this.speeds[j])< k * avgBufferSpeed) {
			avgSpeed = avgSpeed + (this.speeds[j]);
			k_count++;
		}
	}
	avgSpeed = Math.round(avgSpeed / k_count);	
   	
	var bytesRemain = curSample.total - curSample.uploaded;
	var secs_local = Math.round(bytesRemain / avgSpeed) + (curSample.notdone_count * 1);
	if (this.samples.length > this.minBuferLength) {
		if (secs_local > 60 && !this.queueIsVisible) {
			this.EnablePanel(true);
		}
		try {
			document.getElementById(this.baseObjId + 'Time').innerHTML = this.formatTime(secs_local);
			document.getElementById(this.baseObjId + 'Speed').innerHTML = Math.floor(Math.round((avgSpeed) / 10 ) / 100)  + ' ' + t('UnitsKBpS');
		} catch (e) {};
	}
}

SpeedMeter.prototype.formatTime = function (seconds, textStyleId) {
	var result = '';
	var lblHrsF = t('UnitsHourF'), lblMinF = t('UnitsMinF'), lblSecF = t('UnitsSecF');
	var lblHrsS = t('UnitsHourS'), lblMinS = t('UnitsMinS'), lblSecS = t('UnitsSecF');

	var min = Math.floor(seconds / 60);
 	var hrs = Math.floor(min / 60);
	var sec = (seconds % 60);
	min = min % 60;

	var screen_width = 800;
   try {
		screen_width = screenSize().w;
	} catch (e) {}
	
	if (!textStyleId) { //1 - full; 2 - compact
		textStyleId = (screen_width > 1024) ? 1 : 2;
	};	

	if (textStyleId == 1) {
		if (hrs != 0) result += hrs + lblHrsF;
		if ( (result != '') && ((min != '') || (sec != '')) ) result += " ";
		if (min != 0) result += min + lblMinF;
		if ((result != '') && (sec != '')) result +=  " ";
		if (sec != 0) result += sec + lblSecF;
	} else if (textStyleId == 2) {
		if (hrs != 0) result += hrs + ' ' + lblHrsS;
		if ( (result != '') && (min != '') ) result += " ";

		if (min != 0) {
			if (hrs != 0) {
				result += min + ' ' + lblMinF;
			} else {
				result += te('##0;min##', min);
			}
		}
		if ((result != '') && ((sec != '') && (hrs == 0))) result +=  " ";

		if ((sec != '') && (hrs == 0) && (min == 0) ) {
			result += te('##0;sec##', sec);
		}
	}
	if (result.length == 0) result = t('UnitsTimeIsOut');

	return result;
}

try {
	gJsReady({'name': 'uploader'});
} catch (e) {};