var Booster = (function(){
	var _reFileNameParts = new RegExp('[^\/]+\\.[^\/\?#]+', 'g');
	var _rePathFromURL = new RegExp('(?:[^\/]+\/\/[^\/]+)?(\/.*)');
	if(!Prototype.Browser.IE) {
	    if(!window.cpage) window.cpage = document.location.href;
	    if(!window.cint) window.cint = setInterval(function(){
		if(window.cpage != document.location.href) {
		    window.cpage = document.location.href;
		    if(document.location.hash) {
			var href;
			if(document.location.match) href = document.location;
			else href = document.location.href;
			var hash = href.match(/#page=(.+)/);
			if(hash && hash[1]) Booster.go({'url':hash[1],'ni':1,'nh':Booster.isOldOpera});
		    } else {
			Booster.go({'url':document.location.pathname, 'ni':1,'nh':1});
		    }
		}
	    }, 500);
	}			

	var _inRequest = 0;
	var _loadedScripts = {};
	var _waitingScripts = {};
	var _pv = {};		// page vars
	var _banners = {};
	var _timeouts = [];
	var _intervals = [];
	var _timings = {}; _timings.i = [];
	var _supported_counters = {
		'li': 1,
		'tns': 1,
		'top': 1
	};
	
	
	// classes
	var _clBoosterLink = 'bstr-link';
	var _clBoosterNoHashLink = 'bstr-nh-link';
	var _clNoexecScript = 'bstr-noexec';
	var _attrBoosterConfirm = 'bstr-confirm';
	var _attrBoosterScroll = 'bstr-scroll';
	
	var ERR_PARSE_DOC = 1;
	var ERR_PARSE_SCRIPT = 2;
	var ERR_TIMEOUT_INCLUDE_SCRIPT = 3;
	var ERR_PARSE_FILE_NAME = 4;
	
	var AJAX_REQ_MAX_RETRIES = 3;
 	var AJAX_ERROR_TEXT = '<div class="mf_errorPageH1">Внимание!</div>В результате многолетних исследований, команда Mail.Ru выяснила, как сделать Мир лучше.<br>В данный конкретный момент мы завершаем работу над очередным этапом.<br>Пожалуйста, обновите страницу через 30 секунд.';
	var AJAX_ERROR_IMG = 'http://img.imgsmail.ru/0.gif';
	
	var TIMEOUT_INCLUDE_SCRIPT = 5000;
	
	//cln
	var _cln_get = 'http://img.imgsmail.ru/0.gif';
	
	window.bannerContent = {};
	window.nativeSetTimeout = window.setTimeout;
	window.setTimeout = function(a, t) {
		var t = window.nativeSetTimeout(a, t);
		_timeouts[_timeouts.length] = t;
		return t;
	}
	
	window.nativeSetInterval = window.setInterval;
	window.setInterval = function (a, t) {
		var t = window.nativeSetInterval(a, t);
		_intervals[_intervals.length] = t;
		return t;
	}
	
	var _parseFileName = function( url ){
		
		var n = url.match(_reFileNameParts);
		if(!n) {
			Booster.error(ERR_PARSE_FILE_NAME, url);
			return url;
		} else {
			return n[n.length-1];
		}
	}
	var _onResponse = function(t){
		_inRequest = 0;
		_timeInterval('server');
		_requestCln();
		Booster.fetchPage( t.responseText );
		Booster.wait('unset');
		if(Prototype.Browser.Opera) 
		    for(var i = 0;i < document.forms.length;i++) {
				var f = $(document.forms[i]);
				if(f.getInputs('radio').length) f.update(f.innerHTML);
		    }
	}
	
	var _requestCln = function() {
		if(!Booster.cln_url) return;
		if(!Booster.cln_element) {
			var el = new Element('img', {'id': 'bstr-cln', 'width': '0px', 'height': '0px'});
			document.body.appendChild(el);
		}
		var rnd = 0;
		while(rnd == Booster.cln_rnd) rnd = Math.floor(Math.random() * 100000);
		Booster.cln_rnd = rnd;
		el.src = Booster.cln_url + '?' + rnd;		
	}
	
	var _onFail = function(t){
		Booster.retries++;
		if(t.status > 500 && Booster.retries < AJAX_REQ_MAX_RETRIES) {
			new Ajax.Request(Booster.ajax_url, Booster.ajax_params);
		}
		else {
			_showError();
			_inRequest = 0; 
			Booster.wait('unset');					
		}
	}
	
	var _showError = function(){
		if(!Booster.error_container) {
			Booster.error_container = new Element('div');
			Booster.error_container.id = 'bstr-error-container';
			Booster.error_container.className = 'mf_errorPageContainer';
			Booster.error_container.style.display = 'none';
			Booster.error_container.innerHTML = '<div class="mf_errorPage">' +
				'<a id="bstr-error-close" class="mf_spIco mf_iDel2 mf_errorPageClose cp">закрыть</a>' +
				'<div class="mf_errorPageDiv">' +
				AJAX_ERROR_TEXT + 
				'</div></div>';			
			document.body.appendChild(Booster.error_container);
			document.body.appendChild(new Element('img', {
			    'src':'http://rs.mail.ru/d215980.gif?' + Math.floor(Math.random() * 100000),'width':'1','height':'1'}));
			Booster.error_container.closeBtn = $('bstr-error-close');
			Booster.error_container.closeBtn.observe('click', function() {_hideError()});
		}
		Booster.error_container.style.display = 'block';
	}
	
	var _hideError = function() {
		if(Booster.error_container) Booster.error_container.style.display = 'none';
	}
	
	var _writeScript = function( url ){
        var scr = new Element('script',  {'src': url, 'type': 'text/javascript'} );
        Booster.storage.appendChild(scr);		
	}
	var _enumIncludedScripts = function(){
		var scripts = document.getElementsByTagName('script');
		var scripts_len = scripts.length;
		for(var i = 0; i < scripts_len; i++){
			if(scripts[i].src != '') {
				_loadedScripts[_parseFileName(scripts[i].src)] = 1;
			}
		}
	}
	var _regoupPage = function(){
		var webagent = $('waHolder');
		if(/*@cc_on!@*/false) {
			if(webagent) {
				document.body.style.overflow = 'hidden';
				document.body.style.height = '100%';
				var b = document.getElementById('boosterCanvas');
				b.style.height = '100%';
				b.style.overflow = 'auto';
				b.style.position = 'relative';
				var wa = $('waWebAgent');
				if(/*@cc_on!@*/false) {
					wa.setAttribute('waNativeFixed', '1');
					wa.style.removeExpression('filter');
				}
				wa.style.top = '-22px';
				wa.style.position = 'absolute';
				webagent.style.position = 'absolute';
				webagent.style.top = '100%';
				webagent.style.width = '100%';
				webagent.style.left = '0';
				wa = null;
				webagent = null;
			}
		}
	}
	var _timeInterval = function( t ) {
		if (!_timings[t]) _timings[t] = new Date();
	    else {
            _timings.i[_timings.i.length] = t + '=' + ( (new Date()).getTime() - _timings[t].getTime() );
            delete _timings[t];
	    }
	}
	
	var _renderTimings = function() {
		if(!Booster.show_timings) return;
		
		var _fmt = function(a) {
			return (Math.floor(a*1000)/1000)/1000;
		};
		
		var srv_time = 0;
		var render_time = 0;
		var ti = '';
		var iGet = '';
		for(var i = 0; i < _timings.i.length; i++) {
			if(_timings.i[i].split('=')[0] == 'server') {
				srv_time = parseInt(_timings.i[i].split('=')[1]);
			}
			else {
				ti += _timings.i[i].split('=')[0] + ' = <b>' + _fmt(_timings.i[i].split('=')[1]) + '</b> сек<br>';
				iGet += ((iGet.length > 0) ? ',' : '') + _timings.i[i].split('=')[0] + ':' + (_fmt(_timings.i[i].split('=')[1])*1000);
				render_time += parseInt(_timings.i[i].split('=')[1]);
			}
		}
		var d = document.createElement('div');
		d.id = 'renderBlock';
		d.innerHTML = (_fmt(srv_time)) + ' server + ';
		d.innerHTML += (_fmt(render_time)) + ' rendering(browser) = <b>' + _fmt( srv_time + render_time ) + '</b> total<br />';
		d.innerHTML += ti;
		d.innerHTML += '<div style="position: relative;"><div id="renderBlockTOGgLE" style="position: absolute; bottom: 0; left: -60px;">TOGgLE</div></div>';
		//Booster.canvas.appendChild(d);
		
		d = document.createElement('div');
		d.id = 'radar_graphBlock';
		d.innerHTML += '<img id="radar_graph" src="http://img.imgsmail.ru/0.gif" onclick="this.src=\'http://img.imgsmail.ru/0.gif\'" />';
		//Booster.canvas.appendChild(d);
		
		d = document.createElement('img');
		d.width = '1px'; d.height = '1px';
		d.src='http://my.radar.imgsmail.ru/update?p=my&t=&v=' + (_fmt(render_time)*1000) + '&i=' + iGet + '&rnd=' + Math.floor(Math.random()*1000*1000) + (this.prevURL ? '&r=' + escape(this.prevURL) : '') + '';
		Booster.canvas.appendChild(d);
		
		_timings = {}; _timings.i = [];
	}
	
    var _prepareURL = function(url, nohash) {
    	url = url.replace(/%23page%3d[^&]+(&)?/i, '$1');
    	if(nohash) {
    		url = url.replace(/(&|\?)?(back=)[^&]+(&|$)?/i, '$1'+'$2'+escape(Booster.currentURL)+'$3');
    	}
    	var cln = url.match(/^(http:\/\/r.mail.ru\/cln\d+\/)(.*)/);
    	if(cln && cln.length == 3) {
    		url = 'http://' + cln[2];
    		Booster.cln_url = cln[1] + _cln_get;
    	}
    	else {
    		Booster.cln_url = '';
    	}
        return url;
    }	
	
	var _cleanUp = function() {
		_pv = {};
		_banners = {};
		window.bannerContent = {};
		for(var i = 0; i < _timeouts.length; i++) {
			clearTimeout(_timeouts[i]);
		}
		for(var i = 0; i < _intervals.length; i++) {
			clearInterval(_intervals[i]);
		}
        var chlds = Booster.storage.childNodes;
        var r_chlds = [];
        for(var i = 0; i < chlds.length; i++) {
			if(chlds[i].tagName.toLowerCase() == 'script' && chlds[i].readAttribute('src') == null) r_chlds[r_chlds.length] = chlds[i];
        }        
        chlds = $(document.body).cleanWhitespace().childNodes;
        for(var i = 0; i < chlds.length; i++) {
        	if(chlds[i].tagName == undefined)
        		continue;
        	
			if(chlds[i].tagName.toLowerCase() == 'script') r_chlds[r_chlds.length] = chlds[i];
        }
        while(r_chlds.length > 0) {
			var el = r_chlds.pop();
            var p = el.parentNode;
            p.removeChild(el);
        }	
	}
	
	var _pageRefresh = function() {
		document.location = Booster.currentURL;
	}
	
	
	
	return {
		init: function(show_timings){
			if(Prototype.Browser.Opera) 
			    if(window.navigator.appVersion.match(/([^\s]+)\s/)[1] < 9.7) this.isOldOpera = 1;
//			    else window.history.navigationMode = 'compatible';
			
			this.show_timings = show_timings;
			_regoupPage();
			
			this.canvas = $('boosterCanvas');
			this.storage = $('boosterStorage');
			this.storage.hide();
			
			
			_enumIncludedScripts();
			
			this.fetchAnchors();
			this.inited = true;
			this.cln_url = '';
			if(Prototype.Browser.IE) window.attachEvent('onbeforeunload', function(){ _pageRefresh() });
			else window.addEventListener('beforeunload',function(){ _pageRefresh() }, false);
			if(Prototype.Browser.IE) {
			    window.hframe = new Element('iframe', {'id':'history_frame','style':'display:none!important'});
			    window.hframe.src = location.protocol + '//' + location.host + '/boost.html?q=' + 
				(new Date()).getTime() + '#page=' + location.pathname;
			    document.body.appendChild(window.hframe);
			}
			this.currentURL = document.location.toString();
			
		},
		go: function(v){
			if(Object.isString(v)) {
			    var i = 1, a = arguments,v = {'url':v};
			    ['m','d','conf','sc','upl','ni','nh','be'].each(function(t){v[t] = a[i++]});
			}
			_hideError();
			if(_inRequest) return false;
			if(v.conf) if(!confirm(v.conf)) return false;
			this.wait();
			if(!v.m) v.m = 'get';
			if(v.d && v.m.toLowerCase() == 'get') v.url += '?' + v.d + (v.d = '');
			v.url = _prepareURL(v.url, v.nh);
			if(v.d) v.d = _prepareURL(v.d, v.nh);
			if(Prototype.Browser.IE) {
			    if(!window.hframe) {
				window.hframe = new Element('iframe', {'id':'history_frame','style':'display:none!important'});
				window.hframe.src = 'javascript:;';
				document.body.appendChild(window.hframe);
			    }
			    if(!v.ni) window.hframe.src = location.protocol + '//' + location.host + '/boost.html?q=' + 
				(new Date()).getTime() + '#page=' + (v.url.replace(_rePathFromURL, '$1') || v.url);
			}
			_timeInterval('server');
			var ajax_url = v.url;
			if(v.m == 'get') { 
			    if(ajax_url.indexOf('?') < 0) ajax_url += '?booster=1';
			    else {
				if(ajax_url.split('?').length == 1) ajax_url += 'booster=1';
				else ajax_url += '&booster=1';
			    }
			    if(v.be) ajax_url += '&boost_enc=1';
			} else {
			    if(v.d) v.d += '&booster=1';
			    else v.d =  'booster=1';
			}
			this.scroll = v.sc;
			
			_inRequest = 1;
			if(!v.nh) document.location.hash = 'page=' + v.url.replace(_rePathFromURL, '$1');
			this.retries = 0;
			this.ajax_url = ajax_url;
			this.ajax_params = {
				method: v.m,
				parameters: v.d,
				contentType: v.upl ? 'multipart/form-data' : 'application/x-www-form-urlencoded',
				onFailure: _onFail,
				onSuccess: _onResponse
			};
			new Ajax.Request(this.ajax_url, this.ajax_params);
			if(!v.nh) {
			    this.prevURL = this.currentURL || document.URL;
			    this.currentURL = v.url;
			    if(!Prototype.Browser.IE) window.cpage = document.location.href;
			}
			return false;
		},
		wait: function(f) {
		    if(f == 'unset') document.body.className = '';
		    else document.body.className = 'wait';
		    return;
		},
		checkIframe: function(page) {
		    if(!page) this.go({'url':document.location.pathname,'ni':1});
		    var cp = document.location.href.match(/#page=(.*)/);
		    if(cp) cp = cp[1];
		    else return;
		    if(page != cp) this.go({'url':page,'ni':1});
		},
		error: function( errno, param ){
			if(window.console) console.log('Booster error: '+[errno, param].join(', '));
		},
		
		fetchPage: function( str ){
			if(str) {
				_cleanUp();
				_timeInterval('render');
				//str = '<script id=__onDOMContentLoaded defer src=//:><\/script>' + str;
                str = '<div style="display: none;"><br></div>' + str;
				this.canvas.innerHTML = str;//.replace(_reScripts, '');
				this.scrollPage();
				this.fetchAnchors();
				_timeInterval('render');
				this.fetchBanners();
				this.evaluateScripts();// str.match(_reScripts) );
				this.insertBanners();
				this.callCounters();
				_renderTimings();
				document.title = this.title;
			}
		},
		
		fetchAnchors: function(c){
		    if(!c) c = this.canvas;
		    var a = c.getElementsByTagName('a');
		    var a_len = a.length;
		    for(var i = 0; i<a_len; i++){
			if( a[i].className.indexOf(_clBoosterLink) != -1 ) {
			    a[i].onclick = function(){
				return Booster.go({'url':this.href, 'conf':$(this).readAttribute(_attrBoosterConfirm), 'sc':$(this).readAttribute(_attrBoosterScroll)});
			    };
			}
			if( a[i].className.indexOf(_clBoosterNoHashLink) != -1 ) {
			    a[i].onclick = function(){
				return Booster.go({'url':this.href, 'conf':$(this).readAttribute(_attrBoosterConfirm), 'sc':$(this).readAttribute(_attrBoosterScroll),'nh':1});
			    };
			}				
		    }
			
		},
		
		fetchBanners: function() {
			var banners = this.canvas.select('div[banner]');
			for(var i = 0; i < banners.length; i++) {
				var idx = banners[i].readAttribute('banner').match(/\d+$/)[0];
				_banners[idx] = banners[i].innerHTML;
				banners[i].innerHTML = '';
			}
		},
		
        insertBanners: function() {
            var banners = this.canvas.select('div[banner]');
            _timeInterval('banners');
            for(var i = 0; i < banners.length; i++) {
                var idx = banners[i].readAttribute('banner').match(/\d+$/)[0];
                if(Prototype.Browser.IE) {
	                var s = "javascript:{document.write('<scr" + "ipt>"+
	                        "window.onload = function() {"+
	                        "if(document && document.body) {parent.bannerContent["+ idx +"] = document.body.innerHTML;" +
	                        "parent.Booster.moveBannerIE(" + idx + ");" +
	                        "parent.Booster.showBanner("+idx +");" +
	                        "}};"+
	                        "document.domain=\\\"mail.ru\\\";" +
	                        "document.write(parent.Booster.getBanner("+idx+"));"+
	                        "</scr"+"ipt>');document.close();};"                	
            	}
            	else {
	                var s = "javascript:{document.write('<scr" + "ipt>"+
	                        "window.onload = function() {"+
	                        "if(document && document.body) { while(document.body.firstChild) {"+
	                        "parent.document.getElementById(\\\"pre_banner_"+idx+"\\\").appendChild(document.body.firstChild);}"+
	                        "parent.Booster.showBanner("+idx +");" +
	                        "}};"+
	                        "document.domain=\\\"mail.ru\\\";" +
	                        "document.write(parent.Booster.getBanner("+idx+"));"+
	                        "</scr"+"ipt>');document.close();};";
                }
                var iframe = new Element('iframe');
                iframe.id = 'banner_frame_' + idx;
                iframe.style.display = 'none';
                iframe.src = s;
                this.canvas.appendChild(iframe);
            }
            _timeInterval('banners');
        },

        moveBannerIE: function(idx) {
        	var bc = window.bannerContent[idx];
        	if(!bc || !bc.length) return;
            var flashvars = bc.match(/flashvars\=\"([^\"]+)\"/);
            if(flashvars) flashvars = flashvars[1];
            if(flashvars) {
                bc = bc.replace(/NAME=\"FlashVars\" VALUE=\"/, 'name="FlashVars" value="' + flashvars);
            }
            $('pre_banner_' + idx).update(bc);
        },        

        showBanner: function(idx) {
			var b = $('banner_container_'+idx), pb = $('pre_banner_' + idx);
			if(b && pb) b.appendChild(pb);
            if(this.title) document.title = this.title;
        },

		
		getBanner: function(idx) {
			return _banners[idx];				
		},
		
        evaluateScripts: function(){
        	_timeInterval('scripts');
            var scripts = this.canvas.getElementsByTagName('script');
            var scripts_len = scripts.length;

            if(scripts_len && _pv.incScr == undefined)
            _pv.incScr = [];
            for(var i=0; i<scripts_len; i++){
                if(scripts[i].className.indexOf(_clNoexecScript) == -1) {
                    var act = false;
                    var url = scripts[i].src;
                    if( url != undefined && url != '' ){
                        act = ['inc', url];
                    } else if(scripts[i].text != '') {
                        act = ['evl', scripts[i].text];
                    }

                    if( act )
                        _pv.incScr[_pv.incScr.length] = act;
                }
            }
            this.evalNextScript();
            _timeInterval('scripts');
        },
                	
		evalNextScript: function(){
			if(_pv.incScr != undefined && _pv.incScr.length != 0){
				var script = _pv.incScr.shift();
				
				switch(script[0]){
					case 'inc':
						this.includeScript(script[1]);
						break;
					case 'evl':
						this.inlineScript(script[1]);
						break;
				}
			}
		},
		
		includeScript: function( url ){
			
				
			var filename = _parseFileName( url );
			if(_loadedScripts[filename] == undefined) {
				
				_loadedScripts[filename] = 1;
				_writeScript( url );
				this.evalNextScript();
			
			} else if( _waitingScripts[filename] != undefined ) {
				// already requested, waiting for
			} else {
				this.evalNextScript();
			}
			
		},
		
        inlineScript: function( script ){
            var s = new Element("script");
            s.text = script;
            Booster.storage.appendChild(s);
            this.evalNextScript();
        },		
		
		setPageTitle: function ( title ) {
			title = title.replace(/\r|\n/gi, ' ');
			this.title = title.replace(/\s{2,}/gi, ' ');
			document.title = this.title;
		},
		
		callCounters: function () {
			_timeInterval('counters');
			var tc = $('top_counters');
			var bc = $('bottom_counters');
			tc.innerHTML = ''; bc.innerHTML = '';

			var r = escape(this.prevURL ? this.prevURL : document.referrer);
			var u = escape(this.currentURL ? this.currentURL : document.URL);			
			if(_supported_counters['li']) {
				var url = "http://counter.yadro.ru/hit?r"+
							r + ((typeof(screen)=="undefined") ? "" :
							";s" + screen.width+"*"+screen.height+"*"+(screen.colorDepth ?
							screen.colorDepth : screen.pixelDepth) )+ ";u" + u +
							";" + Math.random();
				var img = new Element('img', {'width': 1, 'height': 1, 'alt': '', 'src': url});
				tc.appendChild(img);
			}
			if(_supported_counters['tns']) {
				var url = "http://www.tns-counter.ru/V13a****mail_ru/ru/CP1251/tmsec=mail_my/?" + Math.floor(Math.random()*1000*1000);
				var img = new Element('img', {'width': 1, 'height': 1, 'alt': '', 'src': url});
				bc.appendChild(img);
			}
			if(_supported_counters['top']) {
				var js = 13;
				var a = ';r='+r+';j='+navigator.javaEnabled()+
						';s='+screen.width+'*'+screen.height+
						';d='+(screen.colorDepth?screen.colorDepth:screen.pixelDepth);
				var url = 'http://top3.mail.ru/counter'+
							'?id=1243438;js='+js+a+';rand='+Math.random();
				var img = new Element('img', {'width': 1, 'height': 1, 'alt': '', 'src': url});
				bc.appendChild(img);
			}
			_timeInterval('counters');
		},
		
		scrollPage: function () {
			if(!this.scroll) return;
			
			if(this.scroll == 'top') {
				window.scroll(0,0);
				this.canvas.scrollTop = 0;
			}
		}
	}
})();










