//namespace
if (typeof GP == "undefined") { var GP = {} }
if (typeof GP.stat == "undefined") { GP.stat = {} }

/* NodeList.prototype.toArray = function() { 
	var arr = [] 
	for(var i=0, len = this.length; i < len; i++) 
		arr.push(this[i])
	return arr 
} */

/** Navigation object */
GP.stat.Nav=(function(){
	var $C=YAHOO.util.Connect, $E=YAHOO.util.Event, $D=YAHOO.util.Dom, $=function(x) {return YAHOO.util.Dom.get(x)};
	var $H=YAHOO.util.History;
	var statState;	// holds current state for YUI History
	/** Dynamically created DOM <div>-nodes */
	var l3,l5;
	/** GP.util.Style object */
	var style;
	/**
	 * Selected item extended id for each navigation tree level.
	 * Item extended id is a string like 'l#_@', where
	 * # is level (increased by 1 level number parameter value),
	 * @ is corresponding database record id (or pseudo one)
	 */
	var seldiv = ['l0_0',null,null,null,null,null,null,null]
	var xlidre = /l\d(_\d+){1,3}/;		// xlid regexp
	/** Hash to store marks which sections are loaded */
	var sectionstat = {};
	/** Struct with 5 parameters of delayed navigation request, see 'navigate' function */
	var delayedNavigation = null;
	var helpers = {'l1':{'id':'l1_helper', 'width':85}, 'l2':{'id':'l2_helper', 'width':105}};	// min width of helpers
	var dropdownIcon = null;			// stores node for dropdown marking
	var ssiOrigin;
	
	/** 
	 * Tests if section given by its id is already loaded
	 * @return boolean
	 */
	var isSectionLoaded = function(sid) {
		return (sectionstat["s"+sid] && sectionstat["s"+sid]>1);
	}
	
	/** 
	 * Marks section given as already loaded
	 */
	var markSectionLoaded = function(sid) {
		 if (!isSectionLoaded(sid)) sectionstat["s"+sid] = 2;
	}
	
	/** 
	 * Marks sections given as already loaded
	 */
	var markSectionsLoaded = function(sids) {
		for (var i=0;i<sids.length;i++) markSectionLoaded(sids[i]);
	}
	
	/** 
	 * Tests if section given by its id is already requested (or loaded)
	 * @return boolean
	 */
	var isSectionRequested = function(sid) {
		return (sectionstat["s"+sid] && sectionstat["s"+sid]>0);
	}
	
	/** 
	 * Marks section given as already requested
	 */
	var markSectionRequested = function(sid) {
		 if (!isSectionLoaded(sid)) sectionstat["s"+sid] = 1;
	}
	
	/** 
	 * Marks sections given as already requested
	 */
	var markSectionsRequested = function(sids) {
		for (var i=0;i<sids.length;i++) markSectionRequested(sids[i]);
	}
	
	var getDropdownIconInstance = function() {
		if (!dropdownIcon) {
				dropdownIcon = GP.stat.Util.createSpaceHolder(15,15);
				dropdownIcon.className = 'dropdown_icon';
		}
		var instance = dropdownIcon.cloneNode(true);
		return instance;
	}
	
	/**
	 * Constructs dropdowns from lists, marked with class="dropdown"
	 * @param string|HTMLElement container - root element of processed lists 
	 */
	var constructDropdowns = function(container) {
		var containers = $D.getElementsByClassName('container', 'div', container);
		var elements, element, tmp, lists = [];
		for (var c=0; c<containers.length; c++) {
			elements = $D.getChildren(containers[c]);
			for (var i=0; i<elements.length; i++) {
				element = elements[i];
				if (element.tagName.toLowerCase() != 'div') continue;
				// counting displayable elements in each div
				var count = countDisplayableItems(element);
				if (count > 6) {
					tmp = $D.getElementsByClassName('dropdown', 'ul', element);
					for (var t=0; t<tmp.length; t++) lists.push(tmp[t]);
				}
			}
		}
		
		for (var i=0; i<lists.length; i++) {
			lists[i].parentNode.parentNode.insertBefore(getDropdownIconInstance(lists[i]), lists[i].parentNode);
			$D.addClass(lists[i], 'hidden');
		}
		$E.addListener($D.getElementsByClassName('dropdown_icon', 'img', container),'click', dropdownClickHandler);
	}
	
	/**
	 * counts displayable items (links) in div, recursively
	 */
	var countDisplayableItems = function (div) {
//		if ($D.hasClass(div, 'auto')) return 0;
		var ul = $D.getFirstChild(div);
		var listitems = $D.getChildren(ul);
		var count = listitems.length;
		for (var i=0; i<listitems.length; i++) {
			if (listitems[i].tagName.toLowerCase()!='li') continue;
			var elements = $D.getChildren(listitems[i]);
			for (var j=0; j<elements.length; j++)
				if (elements[j].tagName.toLowerCase()=='div' && !$D.hasClass(elements[j], 'auto'))
					count+=countDisplayableItems(elements[j]);
		}
		return count;
	}
	
	var dropdownClickHandler = function(event) {
		var target = $E.getTarget(event);
		var node = $D.getNextSibling(target);
		if ($D.hasClass(node, 'dropdownactive'))
			$D.removeClass(node, 'dropdownactive');
		else
			$D.addClass(node, 'dropdownactive');
	}
	
	/**
	 * Appends given hash by rules that correspond given level number and extended id of item selected at that level
	 * @param hash rules - rules hash, key = selector, value = semicolon-divided rules
	 * @param integer level - level number
	 * @param string xlid - selected item extended id (see above)
	 */
	var addLevelStyleRules = function(rules, level, xlid) {
		rules['div div ul li'] = 'font-weight:normal;'
		if (1==level){	// gametype selected
			rules['.statblock .container div.'+xlid] = rules['div#l2'] = 'display:block;'	// #l2
		} else
		if (level < 4){ // section selected
			rules['.statblock .container div.'+xlid] = rules['#l3'] = 'display:block;'	// #l3
		} else
		if (level < 7) {
			var cnt=0
			var divs = findDivsByXlid(xlid)
			for (var i=0; i<divs.length; i++) cnt+=divs[i].getElementsByTagName("li").length
			if (level!=4 || cnt>1) {
				rules['.statblock .container div.'+xlid] = 'display:block;'	// #l5
				if (!rules['#l5']) rules['#l5'] = 'display:block;'
			} else if (level==4) {
				rules['#l5'] = 'display:none;'
			}
		}
		rules['div div ul li.'+xlid] = 'font-weight:bold;'
	}
	
	/**
	 * Updates dynamic CSS table styles up to changes made to the navigation choice
	 */
	var updateStyleRules = function() {
		var rules = {}
	   	for (var i=1; i<seldiv.length; i++)
	   		if (seldiv[i])
	   			addLevelStyleRules(rules, i, seldiv[i]);
	   	style.create(rules)
	}

	/**
	 * Requests data of the sections given by their ids (see above)
	 * @param lid lids[] - sections' ids 
	 */
	var requestSections = function(lids) {
		if (!lids) return;
		var sids=[]
		var url = "/stat/navsections"
		//For each section id 
		for (var i=0; i<lids.length; i++)
			//If id presents and is not requested yet, and, also, corresponding LI exists
			if (lids[i] && !isSectionRequested(lids[i]) && 0<findLisByXlid("l2_"+lids[i]).length) {
				sids[sids.length] = lids[i]
				url += "_"+lids[i]
				markSectionsRequested([lids[i]])
			}
		url+="/"
		
		if (sids.length) {
			GP.util.Event.fire("Loading process", true)
			var callback = {
				success: handleSuccess, 
				failure: handleFailure, 
				argument: {sectionsids:sids} 
			} 
			var request = $C.asyncRequest("GET", url, callback)
		}
	}

	/**
	 * Returns substring that is valid xlid
	 * @param string s - item's extended id 
	 * @return xlid 
	 */
	var extractXlid = function(s) {
		if (!s) return null;
		var result = xlidre.exec(s);
		if (result) return result[0];
		else return null;
	}
	
	/**
	 * Returns node that is parental for given xlid
	 * @param string s - item's extended id 
	 * @return node 
	 */
	var getXlidParentNode = function(s) {
		var lis=findLisByXlid(s)
		var node
		if (lis.length)
			for (node=lis[0].parentNode; !extractXlid(node.className) && node; node=node.parentNode) ;
		return node;
	} 
	
	/**
	 * Returns nodes that are children for given xlid
	 * @param string s - item's extended id 
	 * @return node[] 
	 */
	var getXlidChildNodes = function(s) {
		var divs = findDivsByXlid(s)
		var level = (parseInt(s.substr(1,1))+1)+""	//1-based level of children
		var nodes = []
		for (var i=0; i<divs.length; i++)
			nodes = nodes.concat($D.getElementsBy(
				function(node){var x=extractXlid(node.className); return x&&(x.substr(1,1)==level);},"li",divs[i]))
		return nodes
	} 
	
	/**
	 * Returns div nodes that have given xlid in their class names
	 * @param string xlid - given extended id 
	 * @param node root - root of the tree to search in 
	 * @return node[] 
	 */
	var findDivsByXlid = function(xlid, root) {
		return findItemsByXlid(xlid, 'div', root);
	} 
	
	/**
	 * @param string nodetype - 'li' or 'div'
	 */
	var findLevelByXlid = function (xlid, nodetype) {
		var level = xlid.substr(1,1)
		if (nodetype=='li') {
			if (level==1) return 'l1';
			else if (level==2) return 'l2';
			else if (level<5) return 'l3';
			else if (level<8) return 'l5';
		} else {
			if (level==1) return 'l2';
			else if (level<4) return 'l3';
			else if (level<7) return 'l5';
		}
		return null;
	}
	
	var findItemsByXlid = function (xlid, nodetype, root) {
		if (!root) root = findLevelByXlid(xlid, nodetype);
		return $D.getElementsByClassName(xlid,nodetype,root);
	}
	
	/**
	 * Returns li nodes that have given xlid in their class names
	 * @param string xlid - given extended id 
	 * @param node root - root of the tree to search in 
	 * @return node[] 
	 */
	var findLisByXlid = function(xlid, root) {
		return findItemsByXlid(xlid, 'li', root);
	}
	
	/**
	 * Selects node given by its extended id, with opening slave div
	 * @param xlid xlid - section's extended id 
	 * @param boolean followDefault - enables autoopen of default nodes 
	 */
	var select = function(xlid,followDefault) {
		if (xlid) {
			/** extracting level number*/
		   	var level = xlid.substr(1,1);
			var divs = findDivsByXlid(xlid);
		   	if (level && xlid != seldiv[level]) {	// not already selected
			   	for (var i=level; i<seldiv.length; i++) seldiv[i] = null;	// clear nav selection below selected level 
			   	seldiv[level] = xlid;	// set level selection
		   		var section_ids = [];
			   	if (1==level && divs.length) {	// gametype selected, requests some sections
	   				var section_lis = $D.getElementsByClassName('ordinary', 'div', divs[0])[0].getElementsByTagName('li');
	   				for (var i=0; i<section_lis.length; i++) {
			   			var secxlid = extractXlid(section_lis[i].className);
			   			if (secxlid) section_ids.push(secxlid.substr(3));
	   				}
	   				section_lis = $D.getElementsByClassName('global', 'div', divs[0])[0].getElementsByTagName('li');
	   				for (var i=0; i<section_lis.length; i++) {
			   			var secxlid = extractXlid(section_lis[i].className);
			   			if (secxlid) section_ids.push(secxlid.substr(3));
	   				}
			   	}
			   	if (seldiv[2]) section_ids.push(seldiv[2].substr(3));
			   	var li, lis=[];
			   	for (var i=0; i<divs.length; i++)
			   		lis = lis.concat(getXlidChildNodes(extractXlid(divs[i].className)));
	   			for (var i=0; i<lis.length; i++) {
		   			if (followDefault && $D.hasClass(lis[i], "default")) 
   						li = lis[i];
	   				var div = getXlidParentNode(extractXlid(lis[i].className));
	   				if ($D.hasClass(div, "auto"))
   						li = lis[i];
	   			}
		   		if (li)	select(extractXlid(li.className),true);
		   		requestSections(section_ids);
			}
		}
	   	updateStyleRules();
	}

	/**
	* Selects node given by its section id and one of the following:
	* {stageid} or {scopeid} or {stageid,roundno} or {stageid,roundno,layerno}
	* @param integer sectionid DB section id to search in or to upload and delay navigation
	* @param integer stageid DB stage id to search or to search roundno and layerno in
	* @param integer scopeid DB scope id to search
	* @param integer roundno round number to search
	* @param integer layerno layer number to search
	*/
	var navigate = function(gametypeid,sectionid,stageid,scopeid,roundno,layerno) {
		var stageli, scopeli, gametypeli, level;
		if (!sectionid || isSectionLoaded(sectionid)) {
			delayedNavigation = null;
			if (scopeid) {
				scopeli = findLisByXlid("l6_"+scopeid);
				if (scopeli && scopeli.length) scopeli = scopeli[0]; else scopeli = null;
			}
			if (!scopeli && stageid) {
				stageli = findLisByXlid("l5_"+stageid);
				if (stageli && stageli.length) stageli = stageli[0]; else stageli = null;
			}
			if (!scopeli && !stageli && gametypeid) {
				gametypeli = findLisByXlid("l1_"+gametypeid);
				if (gametypeli && gametypeli.length) gametypeli = gametypeli[0]; else gametypeli = null;
			}
			if (scopeli) {  
				for (level=seldiv.length-1; level>6; level--) seldiv[level] = null
				seldiv[level] = extractXlid(scopeli.className);
			}
			if (stageli) {
				for (level=seldiv.length-1; level>5; level--) seldiv[level] = null
				seldiv[level] = extractXlid(stageli.className);
				if (roundno) {
					var id = extractXlid(stageli.className).substr(2)+"_"+roundno+"_"+layerno;
					var li7 = findLisByXlid("l7"+id);
					if (li7.length) {
						seldiv[level+1] = extractXlid(getXlidParentNode("l7"+id).className);
						seldiv[level+2] = "l7"+id;
					}else{
						seldiv[level+1] = "l6"+id;
						seldiv[level+2] = null;
					}
				}
			}
			if (gametypeli) {
				for (level=seldiv.length-1; level>1; level--) seldiv[level] = null;
				seldiv[level] = extractXlid(gametypeli.className);
			}
			if (seldiv[level]) var li = findLisByXlid(seldiv[level]);
			li = ((li.length) ? li[0] : null);
			while (li && level>1) {
				var div = getXlidParentNode(extractXlid(li.className));
				level--;
				seldiv[level] = extractXlid(div.className);
				if (seldiv[level]) li = findLisByXlid(seldiv[level]);
				li = ((li.length) ? li[0] : null);
			}
			GP.util.Event.fire("Navigation loaded", {});
		   	updateStyleRules();
		   	helpersFix();
		} else if (sectionid) {
			requestSections([sectionid]);
			delayedNavigation = {gametypeid:gametypeid,sectionid:sectionid,stageid:stageid,scopeid:scopeid,roundno:roundno,layerno:layerno}
		}
	}

	/** 
	 * Callback success handler, is executed when section info request succeed 
	 */
	var handleSuccess = function(o){
		var div = document.createElement("div")
		var currentSectionToReselect
		if (seldiv[2]) {
			currentSectionToReselect = seldiv[2].substr(3)
			if (isSectionLoaded(currentSectionToReselect)) currentSectionToReselect=null
		}
		
		if(document.createDocumentFragment) {
			var df = document.createDocumentFragment()
			df.appendChild(div)
		}
		
	    if(o.responseText !== undefined) { 
	        div.innerHTML = o.responseText
	        GP.stat.Util.enhashLinks(div)	// enhash links for l3/l5 navigation elements
	        constructDropdowns(div); 
	        markSectionsLoaded(o.argument.sectionsids)
	    }
	    
	    /** Root children of dynamically loaded HTML of the section requested */ 
	    var items = div.childNodes;		// l3 and l5 nodes
	    for (var i=items.length-1; i>=0; i--) {
	    	var item = items[i];
	    	item=$D.getFirstChild(item);
			if (item && ("div"==item.nodeName.toLowerCase()) && (item.id=='l3' || item.id=='l5')) {
				var l = (("l3"==item.id) ? l3 : l5);
	    		/** Children of the root children of dynamically loaded HTML of the section requested (subchildren)*/ 
		    	var subitems = $D.getFirstChild(item);
		    	if(!subitems) continue;
		    	subitems = subitems.childNodes;
		    	
		    	for (var j=subitems.length-1; j>=0; j--) {
		    		var subitem = subitems[j];
		    		if ("div" == subitem.nodeName.toLowerCase()){
		    			//Searching if there is already that div
				    	var olders = findDivsByXlid(extractXlid(subitem.className),l);
				    	for (var k=0; k<olders.length; k++)
				    		if (l == olders[k].parentNode)
						    	l.removeChild(olders[k]);
				    	l.appendChild(subitem);
				    }
				}
	    	}
	    }
		if (delayedNavigation) {
			navigate(delayedNavigation.gametypeid,delayedNavigation.sectionid,delayedNavigation.stageid,delayedNavigation.scopeid,delayedNavigation.roundno,delayedNavigation.layerno);
		} else if (currentSectionToReselect && isSectionLoaded(currentSectionToReselect)) {
	    	seldiv[2] = null;
	    	select("l2_"+currentSectionToReselect,true);
	    	helpersFix();
	    }
	    GP.util.Event.fire("Loading process", false);
	} 
	
	/** 
	 * Callback failure handler, is executed when section info request failed 
	 */
	var handleFailure = function(o){ 
		if (o.status >= 0) {//Request failed
			GP.stat.Error.show("NAV_"+o.status,requestSections,o.argument.sectionsids)
		}
		GP.util.Event.fire("Loading process", false)
	}  
	
	/**
	 * List item (indeed for <A>-node) on-click handler
	 * @param Event e - event happened
	 */
	var onLIClick = function(e) {
	  	var target = $E.getTarget(e);
	  	if (target.nodeName.toLowerCase()=="a"
	  		&& target.href.match(/^([^:]+:\/\/[^\/]+)?\/stat\//i) && !$D.hasClass(target,'externalLink')) { //@todo: сделать регулярку поточнее, чтобы не подхватывала адрес "/stat/" на любом сайте

			var url = GP.stat.Util.extractHash(target.href) || '';
			if (/(gametype\d+\/)|(section\d+\/)/i.test(url)) loadContent(url);
			else {
				// if ssi-constructed page, then we follow enhashed link, else navigate by ourselves
				if (ssiOrigin) return true;  
				try { $H.navigate("url", url) } catch (e) { loadContent(url) }
			} 
			$E.preventDefault(e);

			/** <li>-node */
			if (target) {
			  	var listitem = target.parentNode;
			  	if (!listitem || listitem.nodeName.toUpperCase() != 'LI') return;
				
				/** <li>-node class name is a extended item id (see above)*/
				var xlid = extractXlid(listitem.className);
				select(xlid,true);
		   		helpersFix();
		   		
		   		// check if item in extrablock clicked
				for (var node=listitem.parentNode; node && !($D.hasClass(node, 'extrablockContent')) && !($D.hasClass(node, 'statblock')); node = node.parentNode) ;   
		   		if (node && $D.hasClass(node, 'extrablockContent')) {	// extrablock clicked
					navHelperClickLevelExtrablock(xlid.substr(0,2), false);	// remove dropdown
			   		checkNavItemVisible(xlid);
			   		
//		   			var mainnode = node.parentNode.parentNode.parentNode;		
			   	}
		   		
	   		}	
		} // hide dropdowns if anyway
		for (var node=target; node.className != 'extrablock_helper'; node = node.parentNode)
			if (node.nodeName.toLowerCase() == 'body') {
				for (var helper_name in helpers) navHelperClickLevelExtrablock(helper_name, false);
				break;
			}
	}
	
	/**
	 * checks if item with xlid is visible in selected navigation
	 * @param string xlid - xlid of item to check
	 */
	var checkNavItemVisible = function (xlid) {
		xlid = extractXlid(xlid);
		if (!xlid) return; // bad parameter
		var level = findLevelByXlid(xlid, 'li');
		var listItems = $D.getElementsByClassName(xlid, 'li', level);
		if (!listItems.length) return;		// bad xlid?
		var listitem = listItems[0];		// found
		// find mainnode, contains 'ordinary', 'global' and extrablock
		var mainnodeXlid;
		for (var mainnode=listItems[0].parentNode; mainnode; mainnode=mainnode.parentNode)
			if (mainnodeXlid = extractXlid(mainnode.className))	break;
		// check if mainnode is visible
		if (seldiv[mainnodeXlid.substr(1,1)] != mainnodeXlid) return; // div not visible
		//check if extrablock found
		if (!$D.getElementsByClassName('extrablockContent', 'div',mainnode).length) return;
		// replace
		var sections = {'ordinary':null, 'global':null};
		var alreadyVisible = false;
		for (var sectionClassname in sections) {
			sections[sectionClassname] = $D.getElementsByClassName(sectionClassname, 'div', mainnode);
			sections[sectionClassname] = sections[sectionClassname].length ? sections[sectionClassname][0] : null;
			if ($D.getElementsByClassName(xlid, 'li', sections[sectionClassname]).length) // already found
				alreadyVisible = true;
		}
		if (alreadyVisible) return;
		var targetClassname = $D.hasClass(listitem, 'sectionGlobal') ? 'global' : 'ordinary';
		var targetDiv=sections[targetClassname];
		if (!targetDiv) targetDiv = mainnode.getElementsByTagName('div')[0];	// if no named div found, take first one
		var targetList = $D.getFirstChild(targetDiv);
		listItems = targetList.getElementsByTagName('li');
		targetList.removeChild(listItems[Math.max(listItems.length-2,0)]);
		targetList.appendChild(listitem.cloneNode(true));
	}
	
	/**
	 * Navigation state update event listener
	 */
	var navigateListener = function(data) {
		if(data)
			navigate(data.gametypeid, data.sectionid, data.stageid, data.scopeid, data.roundno, data.layerno);
	}
	
	/**
	 * Shows/hides extrablock in given node
	 * @param node			- node, containing extrablock
	 * @param clicked		- if related helper was clicked
	 * @param selectedClass	- class of the extrablock to show
	 * @param helper		- node of the related helper node  
	 */
	var navHelperExtrablockShow = function (node, clicked, selectedClass, helper) {
		// find extrablock in mainnode
		var extrablock = $D.getElementsByClassName('extrablockContent', 'div', node)
		// check if extrablock has content
		if (extrablock.length) { extrablock = extrablock[0].parentNode.parentNode } else { return }
		
		if (!clicked) { // hide
			if ($D.hasClass(extrablock, 'extrablockActive')) {
				$D.replaceClass(extrablock, 'extrablockActive', 'extrablock')
			}
		} else {
			if ($D.hasClass(extrablock, 'extrablock') && $D.hasClass(node, selectedClass)) {
				$D.replaceClass(extrablock, 'extrablock', 'extrablockActive')
				var coords = $D.getXY(helper) // helper coords
				coords[0]+=3
				coords[1]+=26
				$D.setXY(extrablock, coords)
			} else {
				$D.replaceClass(extrablock, 'extrablockActive', 'extrablock')
			}
		}
	}
	
	/**
	 * Shows/hides extrablock in statblock defined by id
	 * @param level_name	- id of the statblock (i.e. 'l2')
	 * @param clicked		- if related helper was clicked
	 */
	var navHelperClickLevelExtrablock = function (level_name, clicked) {
		var level = level_name.substr(1,1)-1;
		if (!seldiv[level]) return;	// no need for checking
		var container = $D.getElementsByClassName('container', 'div', $(level_name))[0];	// container node
		var nodes = container.childNodes;
		for (var i=0; i<nodes.length; i++)
			if (nodes[i].nodeName.toUpperCase() == 'DIV')
				navHelperExtrablockShow(nodes[i], clicked, seldiv[level], $(helpers[level_name].id));
	}
	
	/**
	 * Handler for helper (de)activation
	 */
	var navHelperCallback = function (e) {
		var node = $E.getTarget(e);
		if (node.tagName.toUpperCase() == 'SPAN')	node = node.parentNode;
		var level = node.id.substr(0,2)	// which popup helper is called
		// show or hide extrablocks by levels
		for (helper_name in helpers)
			navHelperClickLevelExtrablock(helper_name, helper_name == level);
		$E.preventDefault(e);
	}
	
	/**
	 * check if extrablock for selected level found
	 * @param string level_name - statblock id
	 */
	var navHelperExtrablockVisible = function (level_name) {
		var level = level_name.substr(1,1)-1;
		if (!seldiv[level]) return false;	// no need for checking
		var node = $D.getElementsByClassName(seldiv[level],'div',$(level_name))[0];
		var nodes = $D.getElementsByClassName('extrablockContent', 'div', node);
		return nodes.length ? true : false;
	}
	
	/**
	 * sets width for helper area
	 */
	var helpersFix = function(items) {
		if (!items) items = helpers;	// default
		for (var item in items) {
			if (helpers[item]) var helper = helpers[item]; else return; 
			var helper_div = $(helper.id)
			var visible = navHelperExtrablockVisible(item)
			if (visible) { // helper must be seen
				helper_div.style.width = 0;	// ie fix
				helper_div.style.width = Math.max(helper_div.parentNode.offsetWidth-28, helper.width)+'px';
				helper_div.style.display = 'block';
			} else {	// hide if no extrablock found
				helper_div.style.display = 'none';
			}
		}
	}
	
	/**
	 * creates and appends to document shadowed blocks for stages and scopes
	 * @param string createId - id of create block
	 * @param string afterID - id of presceding block 
	 */
	var navCreateStatblock = function (createId, afterId) {
		if ($(createId)) return $D.getFirstChild($(createId));
		var shadowblock = document.createElement('div'); shadowblock.className = 'statshadow';
		var statblock = document.createElement('div'); statblock.className = 'statblock'; statblock.id = createId;
		var container = document.createElement('div'); container.className = 'container';
		statblock.appendChild(container);
		shadowblock.appendChild(statblock);
		$D.insertAfter(shadowblock, $(afterId).parentNode);
		return container;
	}
	
	/**
	 * YUI state changes handler
	 */
	var loadContent = function (state) {
		if (!state) return;
		GP.util.Event.fire("Nav item chosen", {url:'/stat/pure/'+state});
	}
	
	var navigationListener = function () {
		for (var i=1; i<seldiv.length; i++) checkNavItemVisible(seldiv[i]);
	}
 
	/**
	 * Navigation object initiator
	 * @param bolean ssi - if ssi version requested
	 */
	var init = function(ssi) {
		ssiOrigin = ssi;
		GP.stat.Error.init();
		GP.stat.Util.setHashPrefix('#url=');
		l3 = navCreateStatblock('l3', 'l2');
		l5 = navCreateStatblock('l5', 'l3');
		GP.stat.Util.enhashLinks(['l1','l2','l3','l5','statfooters_root']);
		style=new GP.util.Style  //creates dom element <style>
		$E.addListener(["l1","l2","l3","l5"], "click", onLIClick);				// navigation anchor handlers
		$E.addListener(['l1_helper', 'l2_helper'], 'click', navHelperCallback);	// navigation helper handlers
		$E.addListener('frame', "click", onLIClick);							// frame content click handler
		GP.util.Event.addListener("Statistics loaded", navigateListener);
		GP.util.Event.addListener("Navigation loaded", navigationListener, true);	// once executed
		
		var frame = $('frame');
		if (frame && frame.hasChildNodes())	{
			GP.stat.Util.enhashLinks(frame);
			GP.stat.Frame.setNavinfo(frame);
		}
		
		// history
		var bookmarkedState = $H.getBookmarkedState("url");
		var queryState = $H.getQueryStringParameter("url");
		var initialState = bookmarkedState || queryState || '';  
		$H.register("url", initialState, loadContent);
		try {
			$H.initialize("yui-history-field", "yui-history-iframe");
			$H.onReady(function () { currentState = $H.getCurrentState("url"); loadContent(currentState); })
		} catch(e) {
			loadContent(initialState);
		}
	}
	return {init:init,navigate:navigate}
})()