(function(){
var GO = {
	ge: function (id) {
		return GO.doc.getElementById(id);
	},
	chr: function () {
		if (arguments.length > 1) {
			return String.fromCharCode.apply(null, arguments);
		}
		return String.fromCharCode.call(null, arguments[0]);
	},
	setQuery: function (oElem) {
		oElem.href += document.MSearch.q.value;
		return true;
	},
	mapSearch: function (hElem, value) {
		hElem.href += GO.base64.encodeURI(value);
		return true;
	},
	checkForm: function (oForm) {
		if (oForm.elements.q.value !== "") {
			return true;
		}
		window.location.href = "http://go.mail.ru";
		return false;
	}
};
GO.win = this;
GO.doc = document;
GO.head = GO.doc.getElementsByTagName("head")[0] || GO.doc.documentElement;

/* BEGIN GO.utf8 part */
var
	utf8re1 = /[^\x00-\x7F]/,
	utf8re2 = /([^\x00-\x7F]+[\x00-\x7F]{0,10})+/,
	utf8encodeFn1 = function (a) {
		if (a < 128) {
			return GO.chr(a);
		}
		if (a < 2048) {
			return GO.chr(192 + (a >> 6)) + GO.chr(128 + (a & 63));
		}
		if (a < 65536) {
			return GO.chr(224 + (a >> 12)) + GO.chr(128 + ((a >> 6) & 63)) + GO.chr(128 + (a & 63));
		}
		//if (a < 2097152) {
		//	return GO.chr(240 + (a >> 18)) + GO.chr(128 + ((a >> 12) & 63)) + GO.chr(128 + ((a >> 6) & 63)) + GO.chr(128 + (a & 63));
		//}
		return GO.chr(240 + (a >> 18)) + GO.chr(128 + ((a >> 12) & 63)) + GO.chr(128 + ((a >> 6) & 63)) + GO.chr(128 + (a & 63));
	},
	utf8encodeFn2 = function (b) {
		var
			c = [],
			i = 0,
			l = b.length;
		while (i < l) {
			c[i] = utf8encodeFn1(b.charCodeAt(i++));
		}
		return c.join('');
	},
	utf8decodeFn = function (d) {
		var
			a = [],
			c1,
			c2,
			c3,
			c4,
			l = d.length,
			j = 0,
			i = 0;
		while (i < l) {
			c1 = d.charCodeAt(i++);
			if (c1 > 127) {
				c2 = d.charCodeAt(i++);
			}
			if (c1 > 223) {
				c3 = d.charCodeAt(i++);
			}
			if (c1 > 239) {
				c4 = d.charCodeAt(i++);
			}
			a[j++] = (function(){
				if (c1 < 128) {
					return GO.chr(c1);
				}
				if (c1 < 224) {
					return GO.chr(((c1 - 192) << 6) + (c2 - 128));
				}
				if (c1 < 240) {
					return GO.chr(((c1 - 224) << 12) + ((c2 - 128) << 6) + (c3 - 128));
				}
				return GO.chr(((c1 - 240) << 18) + ((c2 - 128) << 12) + ((c3 - 128) << 6) + (c4 - 128));
			})();
		}
		return a.join('');
	};
GO.utf8 = {
	encode: function (d) {
		d = d.replace(/^\s*/,"").replace(/\s*$/,"");
		var
			e = [],
			j = 0,
			g = '',
			f;
		while ((f = d.search(utf8re1)) != -1) {
			g = d.match(utf8re2)[0];
			e[j++] = d.substr(0,f);
			e[j++] = utf8encodeFn2(g);
			d = d.substr(f + g.length);
		}
		e[j++] = d;
		return e.join('');
	},
	decode: function (d) {
		var
			a = [],
			b = 0,
			c = '',
			j = 0;
		while ((b = d.search(utf8re1)) != -1) {
			c = d.match(utf8re2)[0];
			a[j++] = d.substr(0, b);
			a[j++] = utf8decodeFn(c);
			d = d.substr(b + c.length);
		}
		a[j++] = d;
		return a.join('');
	}
};
/* END GO.utf8 part */

/* BEGIN base64 part */
var base64coll = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
GO.base64 = {
	encode: function (a) {
		var l = a.length, c, o2, o3, h1, h2, h3, h4, bits, i = 0, enc = "";
		do {
			c = a.charCodeAt(i++);
			o2 = a.charCodeAt(i++);
			o3 = a.charCodeAt(i++);
			bits = c << 16 | o2 << 8 | o3;
			h1 = bits >> 18 & 0x3f;
			h2 = bits >> 12 & 0x3f;
			h3 = bits >> 6 & 0x3f;
			h4 = bits & 0x3f;
			enc += base64coll.charAt(h1) + base64coll.charAt(h2) + base64coll.charAt(h3) + base64coll.charAt(h4);
		} while (i < l);
		switch (a.length % 3) {
			case 1:
				enc = enc.slice(0,-2) + '==';
			break;
			case 2:
				enc = enc.slice(0,-1) + '=';
			break;
		}
		return enc;
	},
	encodeURI: function (a) {
		return this.encode(GO.utf8.encode(a)).replace(/=/g,'%3D').replace(/\//g,'%2F');
	},
	decode: function (a) {
		var l = a.length, o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc='';
		do {
			h1 = base64coll.indexOf(a.charAt(i++));
			h2 = base64coll.indexOf(a.charAt(i++));
			h3 = base64coll.indexOf(a.charAt(i++));
			h4 = base64coll.indexOf(a.charAt(i++));
			bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;	
			o1 = bits >> 16 & 0xff;
			o2 = bits >> 8 & 0xff;
			o3 = bits & 0xff;
			if (h3 == 64) {
				enc += GO.chr(o1);
			} else if (h4 == 64) {
				enc += GO.chr(o1, o2);
			} else {
				enc += GO.chr(o1, o2, o3);
			}
		} while (i < l);
		return enc;
	},
	decodeURI: function (a) {
		return GO.utf8.decode(this.decode(a.replace(/\%3D/gi, '=').replace(/\%2F/gi, '/')));
	}
};
/* END base64 part */

/* BEGIN onAir part */
GO.onAir = (function(){
	var w = window,
	d = document,
	bind = function (elem, eType, fn) {
		if (elem.addEventListener) {
			elem.addEventListener(eType, fn, false);
		} else if (elem.attachEvent) {
			elem.attachEvent("on" + eType, fn);
		}
		return elem;
	},
	OBJ2URI = function (obj) {
			var str = '',
				idx,
				i = true,
				item;
			for (idx in obj) {
				if (!obj.hasOwnProperty(idx)) {
					continue;
				}
				item = obj[idx];
				str += (i ? '' : '&') + idx + '=' + (item.call ? item.call(this) : item);
				(i)?i=!i:null;
			}
			return str;
	},
	run = function (str) {
		return (new Function("return (" + str + ")"))();
	},
	trim = function (str) {
		return str.replace(/^\s+/, '').replace(/\s+$/, '');
	},
	cutString = function (str, len, pst) {
		pst = pst || '';
		if (len != 0 && str.length > len) {
			str = trim(str).substring(0, len) + pst;
		}
		return str;
	},
	ID = 0,
	onAir = function (arg) {
		if (this.onAir) {
			return new onAir(arg);
		}
		if (!arg.parent || (!arg.requestURI && arg.requestURI != '' && !arg['static'])) {
			this.idx = null;
			return false;
		}
		onAir.instances[this.idx = ID++] = this;
		this.block = {
			id: 'onAirBlock_' + this.idx,
			node: null,
			width: arg.blockWidth || '',
			height: arg.blockHeight || '',
			className: arg.blockClass || false
		};
		this.parent = arg.parent.nodeName ? arg.parent : d.getElementById(arg.parent);
		this.staticList = arg['static'] || false;
		this.list = {
			id: 'onAirList_' + this.idx,
			node: null,
			className: arg.listClass || false,
			items: [],
			itemClass: arg.listItemClass || false,
			itemHeight: false,
			itemHeightSet: arg.listItemHeight || false,
			itemMaxLength: arg.listItemMaxLength || 0,
			visibleItems: 0,
			mouseFocus: false
		};
		this.scroll = {
			speed: arg.scrollSpeed || 1,
			amount: arg.scrollAmount || 2,
			currentItem: 0,
			currentScroll: 0,
			delay: arg.scrollDelay || 2000,
			delayTID: null,
			timeout: arg.scrollTimeout || 25,
			isAction: false,
			loop: 0
		};
		this.request = {
			type: arg.requestType || 'AJAX',
			uri: arg.requestURI,
			attr: arg.requestAttr || {},
			period: arg.requestPeriod || 0,
			isAction: false,
			loadBlock: null,
			responseCharset: arg.requestResponseCharset || 'windows-1251'
		};
		this.data = null;
	};
	onAir.prototype = {
		get: function (data) {
			if (data) {
				if (typeof (data) === 'string') {
					try {
						data = run(data);
					} catch (e) {data = null}
				}
				return this.data = data;
			} else {
				switch (this.request.type) {
					case 'AJAX':
						this.sendRequestAjax();
					break;
					case 'SCRIPT':
						this.sendRequestScript();
					break;
				}
			}
		},
		responseHandler: function (instance) {
			if (this.err) {
				(function(){
					this.request.isAction = false;
					this.hideLoadBlock();
					this.startScrollTimer();
				}).call(instance);
				return;
			}
			if (this.response && this.response != '') {
				var response = this.response;
				(function(){
					this.request.isAction = false;
					this.hideLoadBlock();
					this.data = this.get(response);
					this.insertListItems();
					this.setOperations();
				}).call(instance);
			}
		},
		sendRequestAjax: function () {
			var request = new AJAX(
				this.request.uri,
				OBJ2URI.call(this, this.request.attr),
				false,
				'GET',
				5000,
				false,
				this.responseHandler,
				'complete',
				[this]
			);
			if (request.send()) {
				this.request.isAction = true;
				this.showLoadBlock();
			}
		},
		sendRequestScript: function () {
			var t = this,
				id = 'r_oascr',
				oldScript,
				script = d.createElement('script');
			if (oldScript = d.getElementById(id)) {
				oldScript.parentNode.removeChild(oldScript);
			}
			script.type = 'text/javascript';
			script.src = this.request.uri + '?' + OBJ2URI.call(this, this.request.attr);
			script.id = id;
			script.setAttribute('charset',this.request.responseCharset);
			
			this.request.isAction = true;
			this.showLoadBlock();
			
			return d.getElementsByTagName('head')[0].appendChild(script);
		},
		createLoadBlock: function () {
			var block = this.request.loadBlock = d.createElement('div');
			block.id = 'onAirLoadBlock_' + this.idx;
			block.className = 'onAirLoadBlock';
			return this.block.node.appendChild(block);
		},
		showLoadBlock: function () {
			var block;
			if (!(block = this.request.loadBlock)) {
				block = this.createLoadBlock();
			}
			block.style.display = 'block';
			return block;
		},
		hideLoadBlock: function () {
			var block;
			if (!(block = this.request.loadBlock)) {
				block = this.createLoadBlock();
			}
			block.style.display = 'none';
			return block;
		},
		setOperations: function () {
			if ((this.list.itemHeight * this.list.items.length) <= this.block.node.offsetHeight) {
				this.startScrollTimer = function(){};
				return;
			}
			this.startScrollTimer();
		},
		sendRequest: function () {
			var t = this;
			(function(){
				if (t.scroll.isAction || t.list.mouseFocus) {
					setTimeout(arguments.callee, 10);
				} else {
					t.stopScrollTimer.call(t);
					t.get.call(t);
				}
			})();
		},
		startScrollTimer: function () {
			var t = this;
			return (this.scroll.delayTID !== null) ? null : this.scroll.delayTID = setTimeout(function(){
				t.scrolling.call(t);
			}, this.scroll.delay);
		},
		stopScrollTimer: function (tid) {
			tid = tid || this.scroll.delayTID;
			if (tid !== null) {
				clearTimeout(tid);
				this.scroll.delayTID = null;
			}
		},
		scrolling: function (item) {
			var t = this,
				amount = this.scroll.amount,
				nextItem = this.scroll.currentItem + amount,
				scrollLength,
				step,
				scroll = 0,
				list = this.list.node,
				height = this.list.itemHeight * amount,
				handler;

			this.scroll.isAction = true;
			
			if (!this.list.items[this.scroll.currentItem + this.list.visibleItems]) {
				this.scroll.loop++;
				if (!this.staticList && this.request.period && this.scroll.loop == this.request.period) {
					handler = function () {
						t.scroll.currentScroll = 0;
						t.scroll.currentItem = 0;
						t.scroll.loop = 0;
						t.scroll.isAction = false;
						t.sendRequest.call(t);
					};
				} else {
					step = (scrollLength = t.scroll.currentScroll) * (this.scroll.speed / 10);
					handler = function () {
						scroll = ((scroll += step) > scrollLength) ? scrollLength : parseInt(scroll);
						list.style.top = (scroll == scrollLength?'':'-') + (t.scroll.currentScroll - scroll) + 'px';
						if (scroll == scrollLength) {
							t.scroll.currentScroll = 0;
							t.scroll.currentItem = 0;
							t.scroll.isAction = false;
							t.startScrollTimer.call(t);
						} else {
							setTimeout(arguments.callee, t.scroll.timeout);
						}
					};	
				}
			} else {
				step = (scrollLength = height) * (this.scroll.speed / 10);
				handler = function () {
					var px;
					scroll = ((scroll += step) > scrollLength) ? scrollLength : parseInt(scroll);
					list.style.top = '-' + (px = (t.scroll.currentScroll + scroll))+'px';
					if (scroll == scrollLength) {
						t.scroll.currentScroll = px;
						t.scroll.isAction = false;
						t.startScrollTimer.call(t);
					} else {
						setTimeout(arguments.callee, t.scroll.timeout);
					}
				};
			}
			this.scroll.delayTID = null;
			this.scroll.currentItem = nextItem;
			handler();
		},
		setItemsAttr: function () {
			var items = this.list.items,
				l = items.length,
				item;
			while (l--) {
				item = items[l];
				if (this.list.itemHeightSet) {
					item.style.height = this.list.itemHeightSet + 'px';
				}
			}
			if (!this.list.itemHeight) {
				if (this.list.itemHeightSet) {
					this.list.itemHeight = this.list.itemHeightSet
				} else {
					this.list.itemHeight = item.offsetHeight;
				}
			}
			
			this.list.visibleItems = Math.floor(this.block.height / this.list.itemHeight);
			
			return items;
		},
		createBlock: function (id) {
			var block = this.block.node = d.createElement('div');
			block.id = id || this.block.id;
			block.style.width = this.block.width + (typeof this.block.width === 'string' ? '' : 'px');
			block.style.height = this.block.height + (typeof this.block.height === 'string' ? '' : 'px');
			block.style.overflow = 'hidden';
			block.style.position = 'relative';
			this.block.className ? block.className = this.block.className : null;
			return block; 
		},
		createList: function () {
			var list = this.list.node = d.createElement('ul');
			list.id = this.list.id;
			list.style.position = 'relative';
			this.list.className ? list.className = this.list.className : null;
			return list;
		},
		clearList: function () {
			var list = this.list.node,
				items = this.list.items,
				item;
			while (item = list.firstChild) {
				list.removeChild(item);
			}
			items.length = 0;
			list.style.top = '';
			this.scroll.currentScroll = 0;
			this.scroll.currentItem = 0;
		},
		createListItems: function (data, list) {
			list = list || this.list.node;
			var i = data.length,
				items = [];
			if (this.list.items.length != 0) {
				this.clearList();
			}
			if (data && i != 0) {
				while (i--) {
					items[i] = list.insertBefore(this.createListItem(data[i], i), list.firstChild);
				}
			}
			return this.list.items = items;
		},
		createListItem: function (data, idx, item) {
			item = item || d.createElement('li');
			var className = this.list.itemClass;
			
			item.className = (className ? className + ' ' : '') + ((idx % 2) == 0 ? 'odd' : 'even');
			
			item.innerHTML = '<a href="'+data[1]+'" title="' + trim(data[0]) + '" target="_blank">' + cutString(data[0], this.list.itemMaxLength, '&hellip;') + '</a>';
			return item;
		},
		insertListItems: function (list) {
			list = list || this.list.node;
			list.style.visibility = 'hidden';
			
			if (this.createListItems(this.data).length != 0) {
				this.setItemsAttr();
			}
			
			list.style.visibility = '';
			return list;
		},
		create: function () {
			if (this.idx === null) {
				return false;
			}
			var t = this,
			block = this.createBlock(this.block.id),
			scrollStart = function(){
				t.list.mouseFocus = false;
				if (!t.scroll.isAction) {
					t.startScrollTimer.call(t);
				}
			};
			block.appendChild(this.createList());
			this.parent.appendChild(block);
			
			bind(bind(this.list.node, 'mouseover', function(){
				t.list.mouseFocus = true;
				if (!t.scroll.isAction) {
					t.stopScrollTimer.call(t);
				} else {
					setTimeout(arguments.callee, 10);
				}
			}), 'mouseout', scrollStart);
			
			if (this.staticList) {
				this.data = this.get(this.staticList);
				this.insertListItems();
				this.setOperations();
			} else {
				this.get();
			}
		}
	};
	onAir.instances = [];
	onAir.responseScript = function (instanceID, response) {
		var instance = this.instances[instanceID];
		instance.responseHandler.call(response, instance);
	};
	return onAir;
})();
/* END onAir part */

/* BEGIN MRP part */
GO.MRP = (function(){

var
G = 0,
W = this,
D = document,
H = D.getElementsByTagName("head")[0] || D.documentElement,
expando = "MRP_" + now();

function now () {
	return +new Date;
}

/*
 * var suggest = new MRP({
 * 	field: {String|DOMNode} id or DOMNode input
 * 	form: [Optional] {String|DOMNode} id or DOMNode field form
 * 	onsubmit: [Optional] {Function} onsubmit form handler
 * 	wrapper: [Optional]	{String|DOMNode} id or DOMNode wrapper area suggest
 * 	wrapperId: [Optional] {String} id wrapper node, default "mrp-wrap-[GUID]"
 * 	wrapperCln: [Optional] {String} class wrapper node, default "mrp-wrap"
 * 	inner: [Optional] {String|DOMNode} id or DOMNode inner area suggest
 * 	innerId: [Optional] {String} id inner node, default "mrp-innr-[GUID]"
 * 	innerCln: [Optional] {String} class inner node, default "mrp-innr"
 * 	itemsId: [Optinal] {String} id each suggest item, default "mrp-item-[GUID]-[ITEM-ID]"
 * 	itemsCln: [Optinal] {String} class each suggest item, default "mrp-item"
 * 	itemsSelectCln: [Optinal] {String} class selected suggest item, default "mrp-item-select" 
 * 	itemsLimit: [Optional] {Number} max number suggest item list, default 10
 * 	checkFreq: [Optional] {Number} default 150
 * 	keyLockFreq: [Optional] {Number} default 100
 * 	cacheLimit: [Optional] {Number} default 100
 * 	requestProtocol: [Optinal]
 * 	requestHost: [Optinal]
 * 	requestPort: [Optinal]
 * 	requestPath: [Optinal]
 *  requestQuery: [Optinal], default {
		callback: "JSONP.cb",
		cp: function () {return this.valueCurr.length;},
		q: function () {return this.valueCurr;}
	}
 * 	requestCharset: [Optinal], default "windows-1251"
 * 	correctPosX: [Optinal], default 0
 * 	correctPosY: [Optinal], default 0
 * });
 */
/**
 * Конструктор саджестов
 * @param {Node|String} field Поле, к которому надо прицепить подсказаньки
 * @param {Object} sets Объект с полями настройки саджестовы
 * @return {Undefined} Void
 */
var MRP = function (sets) {
/* Если нету настроек вообще, то создаем пустой объект */
	sets = sets || {};
/* Если нету данных о поле, то возвращаем false */
	if (!sets.field) {
		return false;
	}
	
	this.guid = G++;

/* DOMNode поля, к которому все это дело колдуется. */
	this.field = MRP.gee(sets.field);
/* DOMNode формы, которой принадлежит поле. */
	this.form = MRP.gee(sets.form);
	
	this.onsubmit = sets.onsubmit || null;
	this.ondraw = sets.ondraw || null;
	
	this.wrapper = MRP.gee(sets.wrapper);
	this.wrapperId = this.wrapper && !sets.wrapperId && this.wrapper.id || sets.wrapperId || "mrp-wrap-" + this.guid;
	this.wrapperCln = this.wrapper && !sets.wrapperCln && this.wrapper.className || sets.wrapperCln || "mrp-wrap";
	
	this.inner = this.wrapper && MRP.gee(sets.inner);
	this.innerId = this.inner && !sets.innerId && this.inner.id || sets.innerId || "mrp-innr-" + this.guid;
	this.innerCln = this.inner && !sets.innerCln && this.inner.className || sets.innerCln || "mrp-innr";
	
	this.items = [];
	this.itemsFilled = [];
	this.itemsId = sets.itemsId || "mrp-item-" + this.guid + "-";
	this.itemsCln = sets.itemsCln || "mrp-item";
	this.itemsSelectCln = sets.itemsSelectCln || "mrp-item-select";
	this.itemsLimit = sets.itemsLimit || 10;
	this.itemsCurr = null;
	
/* периодичность проверки поля на изменение содержимого текста */
	this.checkFreq = sets.checkFreq || 150;
	
	this.keyLockFreq = sets.keyLockFreq || 100;
	
/* Счетчик для кэша */
	this._i = 0;
/* Кэш - массив. Здесь будет хранится все по индексам. */
	this._c = [];
/* Кэш - хэш-таблица. Здесь будут хранится ключи к основному кэшу. */
	this._h = {};
/* Ограничение размера кэша */
	this.cacheLimit = sets.cacheLimit || 100;

/* Для отслеживания запросов */
	this.valueCurr = "";
	this.valuePrev = "";
	this.valueExc = null;
	
/* Как форматируем значение поля */
	this.getValueCase = sets.getValueCase || "lower";

	var location = W.location;
/* Настройки для запроса подсказок */
	this.request = {
		protocol: sets.requestProtocol || location.protocol,
		host: sets.requestHost || location.host,
		port: sets.requestPort || location.port,
		path: sets.requestPath || location.pathname,
		query: sets.requestQuery || {
			cp: function () {
				return this.valueCurr.length;
			},
			q: function () {
				return this.valueCurr;
			}
		},
		charset: sets.requestCharset || "windows-1251",
		timeout: sets.requestTimeout || 3000
	};
	
/* Корректировка координат */
	this.correctPosX = sets.correctPosX || 0;
	this.correctPosY = sets.correctPosY || 0;
	
/* Всячискии флаги выполнения или наоборот */
/* Есть ли фокус и поля */
	this._focus = false;
/* Номер проверки. По сути id таймаута */
	this._checking = false;
/* Отметка паузы в проверке, она то по сути идет, но ничего не проверяет */
	this._paused = false;
	this._off = false;
	this._hidden = true;
	this._firstCorrection = false;
	this._data = {};
	
/* Записываем созданный this в кэш экземпляров */
	MRP.coll[this.guid] = this;
};
MRP.fn = MRP.prototype = {
	show: function () {
		MRP.show(this.wrapper);
		this._hidden = false;
	},
	hide: function () {
		MRP.hide(this.wrapper);
		this._hidden = true;
	},
	off: function (enabled) {
		this._off = !enabled;
	},
	
/**
 * Получает значение поля в данный момент, форматируя его в зависимости с настройками
 * @return {String} Значение поля
 */
	getValue: function () {
		var value = this.field.value;
		switch (this.getValueCase) {
			case "upper":
				value = value.toUpperCase();
			break;
			case "lower":
				value = value.toLowerCase();
			break;
		}
		return value;
	},
	
	correctPosition: function () {
		var coords = MRP.coords(this.field);
		this.wrapper.style.left = coords.x + this.correctPosX + "px";
		this.wrapper.style.top = coords.y + this.field.offsetHeight + this.correctPosY + "px";
	},
	
/**
 * Отчищает кэш объекты и сбрасывает счетчик
 */
	cacheClear: function () {
		this._i = 0;
		this._c = [];
		this._h = [];
	},

/**
 * Добавляет запись в кэш
 * @param {Object} cacheItem Элемент, который следует добавить в кэш. он может быть почти любого вида, но обязательно сожержать поле hid. Также добавиться еще одно - cid, автоматом
 * @return {Number} id добавленного элемента в кэше
 */
	cacheWrite: function (cacheItem) {
		if (this._i == this.cacheLimit) {
			this.cacheClear();
		}
		this._c[this._h[cacheItem.hid || ""] = cacheItem.cid = this._i] = cacheItem;
		return this._i++;
	},

/**
 * Получает содержимое кэша по заданному индексу
 * @param {String|Number} cid Индентификатор поиска в массиве (Number) или в хэше (String)
 * @return {Object} Содержимое кэша по заданному cid или null, если такого не было найдено
 */
	cacheRead: function (cid) {
		return (cid = this.cacheCheck(cid)) !== false && this._c[cid] || null;
	},
	
/**
 * Проверяет, есть ли позиция в кэше.
 * @param {String|Number} cid Индентификатор поиска в массиве (Number) или в хэше (String)
 * @return {Number|Boolean} id в массиве, если найдено, в ином случае false
 */
	cacheCheck: function (cid) {
		var answer = false;
		if (typeof cid === "number" && this._c[cid]) {
			answer = cid;
		} else if (typeof cid === "string") {
			if (Object.prototype.hasOwnProperty(cid) && Object.prototype.hasOwnProperty.call(this._h, cid)) {
				answer = this._h[cid];
			} else {
				answer = (answer = this._h[cid]) !== undefined && answer;
			}
		}
		return answer;
	},

/**
 * Выбирает саджест по порядковому номеру в массиве
 * @param {Number} id Идентификатор элемента в массиве с актуальными подсказками
 * @return {Object} MRP
 */
	itemSelect: function (id) {
		var item;
		
		if (this.itemsCurr !== null) {
			item = this.items[this.itemsFilled[this.itemsCurr]];
			if (item) {
				item.className = item.className.replace(" " + this.itemsSelectCln, "");
			}
		}
		
		if (id !== null) {
			item = this.items[this.itemsFilled[id]];
			if (item) {
				item.className += " " + this.itemsSelectCln;
			}
		}
		
		this.itemsCurr = id;
		return item || null;
	},

/**
 * Выбирает саджест с заданым шагом от исходного
 * @param {Number} step Шаг, через который следует выбрать элемент с подсказкой.
 * 		Может быть как положительным, так и отрицательным, но никак не 0.
 * @return {Object} MRP
 */
	itemSelectStep: function (step) {
		var
		length = this.itemsFilled.length,
		itemsCurr = this.itemsCurr,
		itemsNext;
		
		if (itemsCurr === null) {
			if (step > 0) {
				itemsNext = 0;
			} else {
				itemsNext = length - 1;
			}
		} else {
			itemsNext = itemsCurr + step;
			if (step > 0) {
				if (itemsNext > length) {
					itemsNext = itemsNext - length - 1;
				}
			} else {
				if (itemsNext < 0) {
					itemsNext = length + 1 + itemsNext;
				}
			}
		}
		
		return this.itemSelect(itemsNext);
	},
	
	itemsReset: function () {
		this.itemSelect(null);
		this.itemsFilled = [];
	},
	
/**
 * Инициализирует подсказки. Прикрепляет все обработчики, создает поля и все самое начальное
 * @return {Object|Boolean} Вернет экземпляр объекта или же false, если что-то пошло не так
 */
	init: function () {
/* Прячем this в замыкании для event-хендлеров */
		var _this = this;
/* Проверяем, нашлося ли поле при выхове конструктора */
		if (!this.field) {
			return false;
		}
/* Если форма не была получена по ид или ее даже в настройках не было, то ищем ее самостоятельно */
		if (!this.form) {
			var form = this.field;
			do {
				form = form.parentNode;
			} while (form && form.nodeName !== "FORM");
			this.form = form;
		}
/* Если вдруг так получилось формы все равно не получено, то возвращаем функу */
		if (!this.form) {
			return false;
		}
		
/* Выключаем автозаполнение у поля */
		MRP.attr(this.field, "autocomplete", "off");
		
/* Получаем настоящее значение поля в момент инициализации */
		this.valueCurr = this.valuePrev = this.getValue();
		
/* ### BIND ### */
/* Добавляем обработчик для фокуса на поле */
		MRP.bind(this.field, "focus", function(){
/* Делаем отметку, что поле получило фокус */
			_this._focus = true;
/* Определяем функу цикличной проверки поля на изменение сожержимого */
			function LoopChecking () {
				if (!_this._off) {
					if (!_this._paused) {
						_this.valueCurr = _this.getValue();
						if (_this.valuePrev !== _this.valueCurr) {
							if (_this.valueExc !== _this.valueCurr) {
/* Проверяем строку на пустоту, если она пуста, то скрываем блок подсказок */
								if (!MRP.isEmptyStr(_this.valueCurr)) {
									_this.getData(_this.valueCurr);
								} else {
									_this.hide();
								}
								_this.valuePrev = _this.valueCurr;
								_this.valueExc = null;
							}
						}
					}
				}
				_this._checking = setTimeout(LoopChecking, _this.checkFreq);
			}
/* Запускаем ее, а дальше она сама все сделает */
			LoopChecking();
		});
		
		var stayVisible = false;
		MRP.bind(this.field, "blur", function(){
/* Снимаем отметку о полученом полем фокусе */
			_this._focus = false;
/* Стопим таймаут проверки */
			clearTimeout(_this._checking);
			setTimeout(function(){
				if (!stayVisible) {
					_this.valuePrev = _this.valueCurr;
					_this.valueExc = "";
					_this.hide();
				}
			}, 200);
		});
		
		var keyLock = false;
		MRP.bind(this.field, MRP.ua.opera ? "keypress" : "keydown", function(event){
			if (keyLock) {
				return;
			}
			keyLock = true;
			setTimeout(function(){
				keyLock = false;
			}, _this.keyLockFreq);
			
			var
			keyCode = event.keyCode || event.which,
			keyHandler = _this.keys[keyCode];
			
			if (keyHandler) {
				keyHandler.call(_this);
			}
		});
		
		var
		submitHandlerPr = this.onsubmit,
		submitHandler = this.form.onsubmit,
		formSubmit = submitHandlerPr;
		if (submitHandler) {
			formSubmit = submitHandlerPr || submitHandler;
			this.form.onsubmit = null;
		}
		this.onsubmit = function (event) {
			var
			eventType = event ? "keydown" : "click",
			enter = true,
			item = _this.itemsFilled[_this.itemsCurr];
			
			if (item !== undefined) {
				if ((item = _this._data.items[item]) !== undefined && !_this._hidden) {
					enter = MRP.getItemPrefs(item.type).enter.call(_this, item, eventType);
				}
			}
			if (enter === false || formSubmit && formSubmit.call(this, eventType) === false) {
				return false;
			} else {
				if (!event) {
					_this.form.submit();
				}
				return true;
			}
		};
		MRP.bind(this.form, "submit", this.onsubmit);

/* Строим, дополняем, украшаем лю... главные врапперы для результатов */
		var
		wrapper = this.wrapper,
		inner = this.inner;
		
		if (!wrapper) {
			wrapper = MRP.ce("div");
			this.wrapper = wrapper;
			this.hide();
			D.body.appendChild(wrapper);
		}
		if (!inner) {
			inner = MRP.ce("div");
			this.inner = wrapper.appendChild(inner);
		}
		MRP.attr(wrapper, {
			id: this.wrapperId,
			"class": this.wrapperCln
		});
		MRP.attr(inner, {
			id: this.innerId,
			"class": this.innerCln
		});
		
		MRP.bind(W, "resize", function () {
			_this.correctPosition();
		});
		
/* А тут делаем уже непосредственно сами блоки результов и создаем коллекцию из них */
		var
		counter = 0,
		item;
		
		while (counter < this.itemsLimit) {
			item = this.items[counter] = this.inner.appendChild(MRP.ce("div", {
				id: this.itemsId + counter,
				"class": this.itemsCln
			}));

			MRP.bind(item, "mousemove", function () {
				_this.itemSelect(this._mrp);
			});
			MRP.bind(item, "click", function (event, target) {
				var
				submit = true,
				item = _this._data.items[this._mrp];
				
				while (target && target.id !== _this.wrapperId) {
					if (target.rel === "mrp-nosubmit") {
						submit = false;
						break;
					}
					target = target.parentNode;
				}
				
				if (submit) {
					_this.field.value = item.text;
					_this.onsubmit.call(_this.form);
				} else {
					stayVisible = true;
				}
			});
			counter++;
		}
		
/* Если все хорошо и функа дошла до сюда, то возвращаем экземпляр */
		return this;
	}
};


/* Вспомогательный функционал */
/**
 * Функция обхода объекта(массива в частном случае)
 * @param {Array|Object} obj Массив или объект, который следует обойти
 * @param {Function} iterFn Callback-функция вызываемая при каждой итерации. Контекстом вызова является значение ячейки в таблице или свойства/метода в объекте. 2 передаваемых параметра: индекс и значение
 * @return {Number|String} последний индекс в итерации
 */
MRP.each = function (obj, iterFn /* TODO: , iterFnArgs */) {
	var
	idx = 0,
	length,
	item;
	
	if ((length = obj.length) !== undefined) {
		while (idx < length) {
			item = obj[idx];
			if (iterFn.call(item, idx++, item) === false) {
				break;
			}
		}
	} else {
		for (idx in obj) {
			item = obj[idx];
			if (iterFn.call(item, idx, item) === false) {
				break;
			}
		}
	}
	return idx;
};

/**
 * Дополняет исходный объект thisObj, дополнительными полями из объекта extObj, если thisObj не указано, то дополняется контекст выхова метода this
 * @param {Object} extObj Дополняющий объект
 * @param {Object} thisObj Исходный объект, который следует дополнить [Опционально]
 * @return {Object} Дополненный объект
 */
MRP.extend = MRP.fn.extend = function (extObj, thisObj) {
	thisObj = thisObj || this;
	MRP.each(extObj, function(prop, value){
		thisObj[prop] = value;
	});
	return thisObj;
};

/**
 * Синоним document.getElementById
 * @param {String} id идентификатор элемента в документе
 * @return {Node|Null} DOMNode или Null, если не найдено
 */
MRP.ge = function (id) {
	return D.getElementById(id);
};

MRP.gee = function (idOrNode) {
	if (idOrNode) {
		if (typeof idOrNode === "string") {
			return MRP.ge(idOrNode);
		} else if (idOrNode.nodeName && idOrNode.nodeType) {
			return idOrNode;
		}
	}
	return null;
};

/**
 * Создает Node с именем name и атрибутами attrs
 * @param {String} name nodeName (тег)
 * @param {Object} attributes Атрибуты создаваемого элемента, например {id: 'foo', title: 'bar'}
 * @return {Node} Созданный элемент с атрибутами attrs, если таковые были
 */
MRP.ce = function (name, attributes) {
	var node = D.createElement(name);
	if (attributes) {
		MRP.attr(node, attributes);
	}
	return node;
};

/**
 * Добавляем обработчик (handler) по событию (event) к элементу (elem)
 * @param {Node} elem Элемент
 * @param {String} event Собитие, без приставки "on", т.е. "click", "blur", "load", ...
 * @param {Function} handler Обработчик по событию. Если функция вернет false, то у элемента будет отменено действие по умолчанию
 * @return {Node} Элемент, к которому добавлялся обработчик
 */
MRP.bind = function (elem, event, handler) {
	function fn (evt) {
		evt = evt || W.event;
		if (handler.call(elem, evt, evt.target || evt.srcElement) === false) {
			if (typeof evt.preventDefault !== "undefined") {
				evt.preventDefault();
			} else {
				evt.returnValue = false;
			}
		}
	}
	if (typeof elem.addEventListener !== "undefined") {
		elem.addEventListener(event, fn, false);
	} else if (typeof elem.attachEvent !== "undefined") {
		elem.attachEvent("on" + event, fn);
	}
	return elem;
};

var
quot1 = /"/g,
quot2 = /'/g,
emptyStr = /^\s*$/;

MRP.escape = {
/**
 * Преобразовывает строку в URI-последовательность (utf8)
 * @param {String} str Исходная строка
 * @return {String} Преобразованная строка
 */
	uri: function (str) {
		if (W.encodeURIComponent) {
			str = W.encodeURIComponent(str);
		} else {
			str = W.escape(str);
		}
		return str;
	},

/**
 * Эскейпит html спец. символы в строку
 * @param {String} str Исходная строка
 * @return {String} Преобразованная строка
 */
	html: function (str) {
		return MRP
			.ce("p")
			.appendChild(D.createTextNode(str))
			.parentNode
			.innerHTML
			.replace(quot1, "&quot;")
			.replace(quot2, "&#39;");
	}
};

/**
 * Проверяет строку на пустоту
 * @return {Boolean} Возвращает true, если строка пустая или состояит из одних пробелов, иначе false
 */
MRP.isEmptyStr = function (str) {
	return emptyStr.test(str || "");
}

/**
 * Определяет координаты (px) элемента относительно левого правого угла документа
 * @param {Node} elem Элемент, координаты которого нужно узнать
 * @return {Object} объект с координатами {x: _left_, y: _top_}
 */
MRP.coords = function (elem) {
	var
	left = 0,
	top = 0;
	
	if (typeof elem.getBoundingClientRect !== "undefined") {
		var
		box = elem.getBoundingClientRect(),
		body = D.body,
		docElem = D.documentElement;
		
		left = box.left + (W.pageXOffset || docElem.scrollLeft || body.scrollLeft) - (docElem.clientLeft || body.clientLeft || 0);
		top  = box.top  + (W.pageYOffset || docElem.scrollTop  || body.scrollTop)  - (docElem.clientTop  || body.clientTop  || 0);
	} else {
		while (elem) {
			left += elem.offsetLeft;
			top += elem.offsetTop;
			if (elem.offsetParent === null) {
				if (elem.nodeName === 'BODY') {
					elem = null;
				} else {
					elem = elem.parentNode;
				}
			}
			if (elem !== null) {
				elem = elem.offsetParent;
			}
		}
	}
	return {
		x: left,
		y: top
	};
};

/**
 * User-Agent
 * @type {Object}
 */
var UserAgent = new String(navigator.userAgent.toLowerCase());
/* Дополняем строковой объект User-Agent */
MRP.ua = MRP.extend({
	ie: UserAgent.indexOf("msie") != -1,
	ie6: UserAgent.indexOf("msie 6") != -1,
	ie7: UserAgent.indexOf("msie 7") != -1,
	ie8: UserAgent.indexOf("msie 8") != -1,
	ff: UserAgent.indexOf("firefox") != -1,
	opera: UserAgent.indexOf("opera") != -1,
	chrome: UserAgent.indexOf("applewebkit") != -1 && UserAgent.indexOf("chrome") != -1
}, UserAgent);

/* Дополнительные переменные для проверок (Спижено из jQuery) */
var
attrStyleFloat = UserAgent.ie ? "styleFloat" : "cssFloat",
attrProps = {
	"for": "htmlFor",
	"class": "className",
	"float": attrStyleFloat,
	cssFloat: attrStyleFloat,
	styleFloat: attrStyleFloat,
	readonly: "readOnly",
	maxlength: "maxLength",
	cellspacing: "cellSpacing",
	rowspan: "rowSpan",
	tabindex: "tabIndex"
};

/**
 * Получает значение аттрибута у элемента, если value не передано, если же передано, то меняет у аттрибута значение
 * @param {Node} elem Элемент
 * @param {String|Object} attribute Имя аттрибута
 * @param {String} value Значение аттрибута [Опционально]
 * @return {String|Void} Возвращает значение аттрибута, если функция вызввалась без указания значения, в ином случае ничего не возвращает
 */
MRP.attr = function (elem, attribute, value) {
	if (typeof attribute === "object") {
		MRP.each(attribute, function (attribute, value) {
			MRP.attr(elem, attribute, value);
		});
	} else {
		if (value) {
			MRP.attr.st.call(elem, attribute, value);
		} else {
			return MRP.attr.gt.call(elem, attribute);
		}
	}
};
MRP.attr.gt = function (attribute) {
	var value;
	if (attribute === "style") {
		if (MRP.ua.ie) {
			value = this.style.getAttribute("cssText");
		} else {
			value = this.getAttribute(attribute);
		}
	} else {
		if (value = attrProps[attribute]) {
			value = this[value];
		} else {
			value = this.getAttribute(attribute);
		}
	}
	return value;
};
MRP.attr.st = function (attribute, value) {
	if (attribute === "style") {
		if (MRP.ua.ie) {
			this.style.setAttribute("cssText", value);
		} else {
			this.setAttribute(attribute, value);
		}
	} else {
		if (attrProps[attribute]) {
			this[attrProps[attribute]] = value;
		} else {
			this.setAttribute(attribute, value);
		}
	}
};
MRP.shows = function (elem) {
	return elem.style.display !== "none";
};
MRP.hidden = function (elem) {
	return elem.style.display === "none";
};
MRP.show = function (elem) {
	if (!MRP.shows(elem)) {
		elem.style.display = "";
	}
};
MRP.hide = function (elem) {
	if (!MRP.hidden(elem)) {
		elem.style.display = "none";
	}
};

/**
 * Создает URI строку из объекта. н. {foo: 'bar', test: '1'} будет преобразовано в foo=bar&test=1
 * @param {Object} obj Объект с парами key: value, где он преобразуется так key=value
 * @param {Object} context [Optional] Контекст вызова функции в value
 * @return {String} созданная URI строка
 */
MRP.createQuery = function (obj, context) {
	var
	idx = 0,
	query = [];
	
	context = context || this;
	
	MRP.each(obj, function(qArgName, qArgValue){
		if (!obj.hasOwnProperty(qArgName)) {
			return;
		}
		if (idx != 0) {
			query[idx++] = "&";
		}
		query[idx++] = qArgName + "=";
		if (qArgValue.call) {
			query[idx++] = MRP.escape.uri(qArgValue.call(context));
		} else {
			query[idx++] = MRP.escape.uri(qArgValue);
		}
	});
	return query.join("");	
};

/**
 * Строит url-строку пригодную для AJAX запросов или для SCRIPT транспорта
 */
MRP.createUrl = function (lego) {
	var
	i = 0,
	url = [];
	
	if (lego.host) {
		url[i++] = lego.protocol + "//";
		url[i++] = lego.host;
		if (lego.port && lego.port != 80) {
			url[i++] = ":" + lego.port;
		}
		if (!lego.path) {
			url[i++] = "/";
		}
	}
	if (lego.path) {
		url[i++] = "/" + lego.path;
	}
	if (lego.query) {
		url[i++] = "?" + MRP.createQuery(lego.query, this);
	}
	return url.join("");
};

/**
 * Instances cache
 * @type {Array}
 */
MRP.coll = [];

function BasicCallback (data) {
	if (MRP.isEmptyStr(this.valueCurr)) {
		this.hide();
		return;
	}
	
	var _this = this, filledCounter = 0;
	this.itemsReset();
	MRP.each(this.items, function (idx) {
		var
		sItem,
		sType,
		sInner;
		
		if ((sItem = data.items[idx])
			&& (sType = MRP.getItemPrefs(sItem.type))
			&& (sInner = sType.draw.call(_this, sItem)) !== false)
		{
			this.innerHTML = sInner;
			_this.itemsFilled[this._mrp = filledCounter++] = idx;
			MRP.show(this);
		} else {
			MRP.hide(this);
		}
	});
	
	if (filledCounter) {
		if (this.ondraw) {
			this.ondraw();
		}
		if (!this._firstCorrection) {
			this.correctPosition();
			this._firstCorrection = true;
		}
		this.show();
	} else {
		this.hide();
	}
}
MRP.fn.extend({
/**
 * Получаем данные по данному запросу
 * @param {String} query Строка запроса (значение поля) [Опционально]
 */
	getData: function (query, callback) {
		var
		instance = this,
		data;
		
		query = query || this.valueCurr;
		callback = callback || BasicCallback;
		
/* Проверяем, есть ли в кэше этот запрос */
		if (data = this.cacheRead(query)) {
			callback.call(this, this._data = data.response, true);
		} else {
			MRP.JSONP(MRP.createUrl.call(this, this.request), {
				callback: function () {
					instance.cacheWrite({
						hid: query,
						response: this
					});
					if (this.terms.query === instance.valueCurr) {
						callback.call(instance, instance._data = this, false);
					}
				},
				charset: this.request.charset,
				timeout: this.request.timeout
			});
		}
	},
	keys: {
		13: function () {  // ENTER
			var _this = this;
			setTimeout(function(){
				_this.hide();
			}, 200);
		},
		27: function () { // ESC
			var _this = this;
			this.hide();
			if (MRP.ua.opera) {
				setTimeout(function(){
					_this.field.value = _this.valuePrev;
				}, 10);
			} else {
				this.field.value = this.valuePrev;
			}
		},
		37: function () { // LEFT ARROW
			
		},
		38: function () { // UP ARROW
			if (!this._hidden) {
				if (this.itemSelectStep(-1)) {
					var 
					item,
					id = this.itemsFilled[this.itemsCurr];
					
					if (id !== undefined) {
						item = this._data.items[id];
						MRP.getItemPrefs(item.type).select.call(this, item);
					}
				} else {
					this.field.value = this.valuePrev;
				}
			}
		},
		39: function () { // RIGHT ARROW
			if (!this._hidden) {
				this.valueExc = null;
			}
		},
		40: function () { // DOWN ARROW
			if (!this._hidden) {
				if (this.itemSelectStep(1)) {
					var 
					item,
					id = this.itemsFilled[this.itemsCurr];
					
					if (id !== undefined) {
						item = this._data.items[id];
						MRP.getItemPrefs(item.type).select.call(this, item);
					}
				} else {
					this.field.value = this.valuePrev;
				}
			} else {
				this.getData(this.valuePrev);
			}
		}
	}
});

/* ### JSONP ### */
var JSONP_ID = 1;
MRP.JSONP = function (url, sets) {
	sets = sets || {};
	var
	timeout = sets.timeout,
	timeoutId = null,
	callback = sets.callback || null,
	callbackName = sets.callbackName || (expando + JSONP_ID++),
	callbackArgs = sets.args || [];
	if (callback) {
		W[callbackName] = function (response) {
			if (callbackArgs.length > 2) {
				callback.apply(response, callbackArgs);
			} else {
				callback.call(response, callbackArgs[0], callbackArgs[1]);
			}
			if (timeout) {
				clearTimeout(timeoutId);
			}
			if (MRP.ua.ie) {
				W[callbackName] = undefined;
			} else {
				delete W[callbackName];
			}
		};
	}
	url += [
		/(\?).*$/.test(url) ? "&" : "?",
		"callback=",
		callbackName
	].join("");
	var script = MRP.ce("script", {
		type: "text/javascript",
		charset: sets.charset,
		src: url
	});
	script.onreadystatechange = function() {
		if (this.readyState === "complete") {
			try {
				H.removeChild(script);
			} catch (e) {}
		}
	};
	script.onload = function (event) {
		if (MRP.ua.opera) {
			try {
				(new Function("return (" + (event.srcElement || event.target).innerHTML + ")"))();
			} catch (e) {}
		}
		H.removeChild(script);
	};
	setTimeout(function () {
		H.insertBefore(script, null)
	}, 0);
	if (timeout) {
		timeoutId = setTimeout(function () {
			if (script && script.parentNode) {
				H.removeChild(script);
			}
			if (callback && W[callbackName]) {
				if (MRP.ua.ie) {
					W[callbackName] = undefined;
				} else {
					delete W[callbackName];
				}
			}
		}, timeout);
	}
};

/* Добавляем функционал шаблонов */
var itemsPrefs = {};
MRP.getItemPrefs = function (name) {
	return itemsPrefs[name] || null;
};
MRP.setItemPrefs = function (name, prefs) {
	itemsPrefs[name] = MRP.extend(prefs, {
		draw: function (item) {
			return item.text;
		},
		enter: function () {
			
		},
		select: function (item) {
			this.valueExc = this.field.value = item.text;
		}
	});
	return this;
};

/* Удаленный конструктор настроек */
MRP.remote = function (url, initCallback) {
	this.build = function (sets) {
		MRP.call(this, sets);
		if (initCallback) {
			initCallback.call(this);
		} else {
			this.init();
		}
	};
	MRP.JSONP(url);
};
MRP.remote.prototype = MRP.fn;

return MRP;
})();
/* END MRP part */

GO.win.GO = GO;
GO.win.onAirWow = GO.onAir;
GO.win.MRP = GO.MRP;
})();