// JavaScript functions used by the Aphia module
var ajax_id='';
var ajax_name='';
var hasSuggests= false;
var highlight = -1;
var timer;
var delay = 500;

// update fuzzy checkbox depending on value of the searchparameter
//
function setFuzzy(val){
 var fuzzy=getObj('fuzzy',0);
 var tComp=getObj('tComp',0);
 if (val=='0'){
  fuzzy.disabled=false;
 }else{
  fuzzy.checked=false;
  fuzzy.disabled=true;
 }
 if (val=='2' || val==3){
  tComp.value='is';
  tComp.disabled=true;
 }else{
  tComp.value='begins';
  tComp.disabled=false;
 }
}

//Fill in the Aphia source properties
//
function fillinSource(val,to){
 val=urldecode(val);
 var strval = new String(val);
 var vals = strval.split('|');
 if (to){
  sendText(vals[0],to+'_id','0');
  sendText(vals[1],to,'0');
 }else{
  sendText(vals[0],'sourcename_id',0);
  sendText(vals[1],'sourcename',0);
 }
}

// Popup Window
//
function showPopup(URL,wd,hg,l,t,scroll){
 if (!hg) hg=555;
 if (!wd) wd=640;
 if (!l) l=200+Math.round(Math.random() * 50);
 if (!t) t=90+Math.round(Math.random() * 20);
 var sb="yes";
 if (scroll==0 || scroll==false || scroll=="no") sb="no";
 //if (winDetail) winDetail.close();
 var winname="win"+Math.round(Math.random()*100);
 var winDetail = window.open(URL,winname,"width="+wd+",height="+hg+",left="+l+",top="+t+",resizable=yes,scrollbars="+sb);
 winDetail.focus();
 return null;
}

// To send text from a popup to the window.opener + close the popup
// cc decides whether to concatenate the text with a |, default: 1 (yes)
//
function sendText(text,obj,cc){
 if (cc==null) cc=1;
 if (opener && !opener.document.closed){
  if (txtfield=getObj(obj)) {
    if (txtfield.value=="" || cc==0){
     txtfield.value=urldecode(text);
    }else{
     txtfield.value=txtfield.value + " | " + urldecode(text);
    }
   }
  }
 window.close();
}

// get *any* object in the child or grand-child window
// gets first the object by id, if its not found, it returns the first object with the name
//
function getObj(name, use_opener){
 var obj;
 var objs;
 if (use_opener == null) use_opener=true;
 if (opener && !window.opener.closed && use_opener==true){
  if (document.getElementById){ obj = opener.document.getElementById(name);}
  else if (document.all) { obj = opener.document.all[name]; }
  else if (document.layers) { obj = opener.document.layers[name]; }
  if(!obj && document.getElementsByName) { objs = opener.document.getElementsByName(name); obj=objs[0]; }
 }else{
  if (document.getElementById){ obj = document.getElementById(name);}
  else if (document.all) { obj = document.all[name]; }
  else if (document.layers) { obj = document.layers[name]; }
  if(!obj && document.getElementsByName) { objs = document.getElementsByName(name); obj=objs[0]; }
 }
 return obj;
}

// decides whether to show the map in an already existing page, else open it in a new page
//
function showMap(id){
 if (window.opener && !window.opener.closed){
   var w1=window.opener;
    if (w1.opener && !w1.opener.closed){
     w1.opener.location.href= "http://www.marbef.org/data/eurobissearch.php?aphia_id="+id;
    }else{
     window.location.href= "http://www.marbef.org/data/eurobissearch.php?aphia_id="+id;
    }
 }else{
   window.location.href= "http://www.marbef.org/data/eurobissearch.php?aphia_id="+id;
 }
}

// similar to the PHP urldecode
//
function urldecode(ch) {
  if (ch) ch = ch.replace(/[+]/g," ")
  return unescape(ch)
}

// close popup+reload window.opener
//
function closeRefresh(){
 if (window.opener!=null && window.opener.open && !window.opener.closed) window.opener.location.href = window.opener.location.href;
 window.close();
}

//AJAX functionality
//
function createXmlHttpRequest(){
  var XmlHttpRequestObject;
  if (typeof XMLHttpRequest != "undefined"){
   XmlHttpRequestObject = new XMLHttpRequest();
  }
  else if (window.ActiveXObject){
   //look up the highest possible MSXML version
   var tryPossibleVersions=["MSXML2.XMLHttp.5.0","MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp","Microsoft.XMLHttp"];

  for (i=0; i< tryPossibleVersions.length; i++){
   try{
      XmlHttpRequestObject = new ActiveXObject(tryPossibleVersions[i]);
      break;
   }
   catch (xmlHttpRequestObjectError){
   //ignore
   }
  }
 }
 return XmlHttpRequestObject;
}

//called by the user/page
//
function aphia_updateList(what,item,div,e,rank_min,rank_max){
 e = e || window.event;
 var key = e.keyCode;
 switch(key) {
   //keyup
   case 38:
     processUp(item);
     break;
  //keydown
  case 40:
     processDown(item);
     break;

  default:
     clearTimeout(timer);
     timer = setTimeout('getSuggests(\''+what+'\',\''+item+'\',\''+div+'\',\''+e+'\',\''+rank_min+'\',\''+rank_max+'\')', delay);
 }
}

function getSuggests(what,item,div,e,rank_min,rank_max){
   var requestObject = createXmlHttpRequest();
   var cacheBuster = new Date() * 1;
   var val=document.getElementById(item).value;
   if (rank_min == "undefined") rank_min=10;
   if (rank_max == "undefined") rank_max=280;
   if (val=="" || val.length<2) {
     hide_suggestion(item+'_suggestions',1);
     hasSuggests = false;
     return null;
   }
   if (what=='tu' || what =='aphia') requestObject.open("GET","cumacea.php?p=ajax&a=list_"+what+"&val="+val+"&rank_min="+rank_min+"&rank_max="+rank_max+"&"+cacheBuster,true);
    else requestObject.open("GET","cumacea.php?p=ajax&a=list_"+what+"&val="+val+"&"+cacheBuster,true);
   requestObject.onreadystatechange = function ProcessRequest(){
   if (requestObject.readyState == 4)
     {
        if (requestObject.status == 200)
        {
          var result = requestObject.responseText;
          var cw = getObj(item,0).clientWidth || 200;
		  getObj(div,0).style.width=cw+'px';
          getObj(div,0).innerHTML=result;
          var opts = getObj(div,0).childNodes;
          if (opts && opts.length==1 && result!='(No matches)' && what!='collectors') {
		   getObj(item,0).value=opts[0].title;
		  }
		  hide_suggestion(div,0);
		  		  hasSuggests = true;
        }
        else
        {
          document.getElementById(div).innerHTML='[xmlHttp Error]';
        }
    }
 }
 requestObject.send(null);
}

// show/hide an element
//
function hide_suggestion(object,hide) {
  if (document.getElementById) {
    if (hide==1)
      document.getElementById(object).style.visibility = 'hidden';
    else
      document.getElementById(object).style.visibility = '';
  }

  else if (document.layers && document.layers[object] != null) {
    if (hide==1)
      document.layers[object].visibility = 'hidden';
    else
      document.layers[object].visibility = '';
  }

  else if (document.all) {
    if (hide==1)
      document.all[object].style.visibility = 'hidden';
    else
      document.all[object].style.visibility = '';
  }
    hasSuggests = false;
  highlight = -1;
  //return false;
}


// handle up event
//
function processUp(item)
{
    if (hasSuggests) {
        var suggest = getObj(item+'_suggestions',0);
        var opts = suggest.childNodes;
        if (highlight > 0) {
            opts[highlight].style.backgroundColor='#ffffff';
			opts[highlight].style.color='#000000';
            highlight--;
            opts[highlight].style.backgroundColor='#000099';
			opts[highlight].style.color='#ffffff';
            var item = getObj(item,0);
            ajax_name=opts[highlight].title;
            ajax_id=opts[highlight].id;
            autoComplete(item);
        }
    }
}

// handle down event
//
function processDown(item)
{
    if (hasSuggests) {
        var suggest = getObj(item+'_suggestions',0);
        var opts = suggest.childNodes;
        if (highlight < opts.length - 1) {
            if (highlight >= 0) {
			 opts[highlight].style.backgroundColor='#ffffff';
			 opts[highlight].style.color='#000000';
			}
			highlight++;
			opts[highlight].style.backgroundColor='#000099';
			opts[highlight].style.color='#ffffff';
			var item = getObj(item,0);
			ajax_name=opts[highlight].title;
			ajax_id=opts[highlight].id;
            autoComplete(item);
        }
    }
}

// do autocomplete to str in input elm
//
function autoComplete(elm, str)
{
    elm.value = ajax_name;
}

// Used for the taxon match file type
//
function updateRowCol(){
 var mf=getObj('matchfile');
 var reg = new RegExp("\.(xls)", "i");
 var dr = getObj('delim_row');
 var dc = getObj('delim_col');
 if(reg.test(mf.value)) {
   dr.value="";
   dr.disabled=true;
   dc.value="";
   dc.disabled=true;
 }else{
   dr.value="13+10";
   dr.disabled=false;
   dc.value="9";
   dc.disabled=false;
 }
}

// Add fields for a n-n relation
//
function cloneField(obj) {
  var field=getObj(obj,false);
  var newfield = field.cloneNode(true);
  var insertHere=getObj(obj+'_more',false);
  //stupid IE/FF errors
  if (newfield.childNodes[0].value) newfield.childNodes[0].value='';
  if (newfield.childNodes[1].value) newfield.childNodes[1].value='';
  if (newfield.childNodes[2]!=null && newfield.childNodes[2].value) newfield.childNodes[2].value='';
  if (newfield.childNodes[3]!=null && newfield.childNodes[3].value) newfield.childNodes[3].value='';
  //show remove sign
  if (newfield.childNodes[2].style) newfield.childNodes[2].style.visibility='';
  if (newfield.childNodes[3].style) newfield.childNodes[3].style.visibility='';
  if (newfield.childNodes[4]!=null && newfield.childNodes[4].style) newfield.childNodes[4].style.visibility='';
  if (newfield.childNodes[5]!=null && newfield.childNodes[5].style) newfield.childNodes[5].style.visibility='';
  insertHere.parentNode.insertBefore(newfield,insertHere);
}

// remove an from a n-n relation
//
function delField(instance) {
 instance.parentNode.parentNode.removeChild(instance.parentNode);
}

// change between DD and DMS
//
function changeDMS(suffix,to,ll){
 var field=getObj('span_'+suffix,false);
 if (to.value=='dms'){
  if (getObj(suffix,false) != undefined){
   to.value="dd";
   var value=getObj(suffix,false).value;
   var d=(value==parseFloat(value))?Math.floor(Math.abs(value)):"";
   var m=(value==parseFloat(value))?Math.floor(((Math.abs(value)-d)*100)*0.6):"";
   var s=(value==parseFloat(value))?Math.round(((((Math.abs(value)-d)*100)*0.6)-m)*60):"";
   if (s==60) {
    s=0;
    m++;
   }
   if (m==60) {
    m=0;
	d++;
   }
   var cardinal='';
   if (ll=='lat') cardinal = '<option value="+">N</option><option value="-"' + ((value<0)?' selected="selected"':'') + '>S</option>';
    else cardinal = '<option value="+">E</option><option value="-"' + ((value<0)?' selected="selected"':'') + '>W</option>';
   field.innerHTML='<input type="text" name="'+suffix+'_d" size="3" maxlength="3" value="'+d+'" onkeyup="cleanComma(this,\'dms\')" /><sup>&deg;</sup><select name="'+suffix+'_sign">' + cardinal + '</select><input type="text" name="'+suffix+'_m" size="2" value="'+m+'" onkeyup="cleanComma(this,\'dms\')" /><sup>\'</sup><input type="text" name="'+suffix+'_s" size="2" value="'+s+'" onkeyup="cleanComma(this,\'dms\')" /><sup>"</sup>';
  }
 }else{
  if (getObj(suffix+'_d',false) != undefined){
   to.value="dms";
   var d=getObj(suffix+'_d',false).value;
   var m=getObj(suffix+'_m',false).value;
   var s=getObj(suffix+'_s',false).value;
   var sign=getObj(suffix+'_sign',false).value;
   if (d!=parseInt(d)) d=0;
   if (m!=parseInt(m)) m=0;
   if (s!=parseInt(s)) s=0;
   var dd=Math.abs(parseInt(d))+Math.abs(parseInt(m))/60+Math.abs(parseInt(s))/3600;
   dd=Math.round(dd*100000)/100000;
   if (dd==0) dd="";
    else if (sign=="-") dd= -1*dd;
   field.innerHTML='<input type=text name="'+suffix+'" size="8" value="'+dd+'" onkeyup="cleanComma(this,\'dd\')" />';
  }
 }
}

// do some basic checking on lat/lon fields
//
function cleanComma(isField,type){
 var pos = getSelectionStart(isField);
 if (type=='dd') isField.value = isField.value.replace(/[,]/g,".");
 if (type=='dms') isField.value = isField.value.replace(/[,\.-]/g,"");
 isField.value = isField.value.replace(/[a-zA-Z\+°]/g,"");
 setCaretPosition(isField,pos)
}

// set caret position of a textbox
//
function setCaretPosition(elem, caretPos) {
    if(elem != null) {
        if(elem.createTextRange) {
            var range = elem.createTextRange();
            range.move('character', caretPos);
            range.select();
        }
        else {
            if(elem.selectionStart) {
                elem.focus();
                elem.setSelectionRange(caretPos, caretPos);
            }
            else
                elem.focus();
        }
    }
}

// find out the caret position of a text box
//
function getSelectionStart(o) {
	if (o.createTextRange) {
		var r = document.selection.createRange().duplicate()
		r.moveEnd('character', o.value.length)
		if (r.text == '') return o.value.length
		return o.value.lastIndexOf(r.text)
	} else return o.selectionStart
}


// Show a loading ... image while matching
//
function showLoading(){
 document.getElementById('match_loading').style.visibility='visible';
 document.getElementById('match_preview').style.visibility='hidden';
 document.getElementById('match_options').style.visibility='hidden';
}

// replace a text box by a file upload
//
function aphiaChangePDF(element_name, oType) {
 var oldObject=getObj(element_name,false);
 var newObject = document.createElement('input');
 newObject.type = oType;
 if(oldObject.size) newObject.size = oldObject.size;
 if(oType=='text') newObject.value = 'http://';
 if(oldObject.name) newObject.name = oldObject.name;
 if(oldObject.id) newObject.id = oldObject.id;
 if(oldObject.className) newObject.className = oldObject.className;
 oldObject.parentNode.replaceChild(newObject,oldObject);
 return newObject;
}

//end Aphia specific JS-functions

// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2005, Richard Heyes, Harald Radi                   |
// | All rights reserved.                                                  |
// |                                                                       |
// | Redistribution and use in source and binary forms, with or without    |
// | modification, are permitted provided that the following conditions    |
// | are met:                                                              |
// |                                                                       |
// | o Redistributions of source code must retain the above copyright      |
// |   notice, this list of conditions and the following disclaimer.       |
// | o Redistributions in binary form must reproduce the above copyright   |
// |   notice, this list of conditions and the following disclaimer in the |
// |   documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote      |
// |   products derived from this software without specific prior written  |
// |   permission.                                                         |
// |                                                                       |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
// |                                                                       |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@phpguru.org>                           |
// |         Harald Radi <harald.radi@nme.at>                              |
// +-----------------------------------------------------------------------+
// $Id: TreeMenu.js,v 1.21 2005/03/02 02:39:26 richard Exp $


function restorePosition(){
  window.scrollTo(0, window.location.search.substr(1));
}


function setScroll(X,Y){
    if((X > 0)||(Y > 0)){
        if((top.opera)&&(typeof window.pageYOffset != 'undefined')){
            window.pageYOffset = Y;
            window.pageXOffset = X;
        }else if((window.document.compatMode)&&
                  (window.document.compatMode != 'BackCompat')){
            window.document.documentElement.scrollLeft = X;
            window.document.documentElement.scrollTop = Y;
        }else if((window.document.body)&&
              (typeof window.document.body.scrollTop != 'undefined')){
            window.document.body.scrollLeft = X;
            window.document.body.scrollTop = Y;
        }else{
            window.scrollTo(X, Y);
        }
    }
   // window.scrollTo(X, Y);
} 


/**
* Function to create copies of objects which are
* normally passed around by references (Arrays for example)
*/
function arrayCopy(input)
{
	var output = new Array(input.length);

	for (i in input) {
		if (typeof(input[i]) == 'array') {
			output[i] = arrayCopy(input[i]);
		} else {
			output[i] = input[i];
		}
	}

	return output;
}

/**
* TreeMenu class
*/
	function TreeMenu(iconpath, myname, linkTarget, defaultClass, usePersistence, noTopLevelImages)
	{
		// Properties
		this.iconpath         = iconpath;
		this.myname           = myname;
		this.linkTarget       = linkTarget;
		this.defaultClass     = defaultClass;
		this.usePersistence   = usePersistence;
		this.noTopLevelImages = noTopLevelImages;
		this.n                = new Array();
		this.output           = '';

		this.nodeRefs       = new Array();
		this.branches       = new Array();
		this.branchStatus   = new Array();
		this.layerRelations = new Array();
		this.childParents   = new Array();
		this.cookieStatuses = new Array();

		this.preloadImages();
	}

/**
* Adds a node to the tree
*/
	TreeMenu.prototype.addItem = function (newNode)
	{
		newIndex = this.n.length;
		this.n[newIndex] = newNode;
		
		return this.n[newIndex];
	}

/**
* Preload images hack for Mozilla
*/
	TreeMenu.prototype.preloadImages = function ()
	{
		//var plustop    = new Image; plustop.src    = this.iconpath + '/plustop.gif';
                var plustop    = null;
		var plusbottom = new Image; plusbottom.src = this.iconpath + '/plastnode.gif';
		var plus       = new Image; plus.src       = this.iconpath + '/pnode.gif';
	
		//var minustop    = new Image; minustop.src    = this.iconpath + '/minustop.gif';
		var minustop    = null;
		var minusbottom = new Image; minusbottom.src = this.iconpath + '/mlastnode.gif';
		var minus       = new Image; minus.src       = this.iconpath + '/mnode.gif';
	
		//var branchtop    = new Image; branchtop.src    = this.iconpath + '/branchtop.gif';
                var branchtop    = null;
		var branchbottom = new Image; branchbottom.src = this.iconpath + '/lastnode.gif';
		var branch       = new Image; branch.src       = this.iconpath + '/node.gif';
	
		var linebottom = new Image; linebottom.src = this.iconpath + '/blank.gif';
		var line       = new Image; line.src       = this.iconpath + '/vertline.gif';
	}

/**
* Main function that draws the menu and assigns it
* to the layer (or document.write()s it)
*/
	TreeMenu.prototype.drawMenu = function ()// OPTIONAL ARGS: nodes = [], level = [], prepend = '', expanded = false, visbility = 'inline', parentLayerID = null
	{
		/**
	    * Necessary variables
	    */
		var output        = '';
		var modifier      = '';
		var layerID       = '';
		var parentLayerID = '';
	
		/**
	    * Parse any optional arguments
	    */
		var nodes         = arguments[0] ? arguments[0] : this.n
		var level         = arguments[1] ? arguments[1] : [];
		var prepend       = arguments[2] ? arguments[2] : '';
		var expanded      = arguments[3] ? arguments[3] : false;
		var visibility    = arguments[4] ? arguments[4] : 'inline';
		var parentLayerID = arguments[5] ? arguments[5] : null;

		var currentlevel  = level.length;

		for (var i=0; i<nodes.length; i++) {
		
			level[currentlevel] = i+1;
			layerID = this.myname + '_' + 'node_' + this.implode('_', level);

			/**
            * Store this object in the nodeRefs array
            */
			this.nodeRefs[layerID] = nodes[i];

			/**
	        * Store the child/parent relationship
	        */
			this.childParents[layerID] = parentLayerID;
	
			/**
	        * Gif modifier
	        */
			if (i == 0 && parentLayerID == null) {
				modifier = nodes.length > 1 ? "top" : 'single';
			} else if(i == (nodes.length-1)) {
				modifier = "lastnode";
			} else {
				modifier = "node";
			}
	
			/**
	        * Single root branch is always expanded
	        */
			if (!this.doesMenu() || (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages))) {
				expanded = true;
	
			} else if (nodes[i].expanded) {
				expanded = true;
	
			} else {
				expanded = false;
			}
	
			/**
	        * Make sure visibility is correct based on parent status
	        */
			visibility =  this.checkParentVisibility(layerID) ? visibility : 'none';

			/**
	        * Setup branch status and build an indexed array
			* of branch layer ids
	        */
			if (nodes[i].n.length > 0) {
				this.branchStatus[layerID] = expanded;
				this.branches[this.branches.length] = layerID;
			}
	
			/**
	        * Setup toggle relationship
	        */
			if (!this.layerRelations[parentLayerID]) {
				this.layerRelations[parentLayerID] = new Array();
			}
			this.layerRelations[parentLayerID][this.layerRelations[parentLayerID].length] = layerID;
	
			/**
	        * Branch images
	        */
			var gifname  = nodes[i].n.length && this.doesMenu() && nodes[i].isDynamic ? (expanded ? 'm' : 'p') : '';
			var iconName = expanded && nodes[i].expandedIcon ? nodes[i].expandedIcon : nodes[i].icon;
			var iconimg  = nodes[i].icon ? this.stringFormat('<img src="{0}/{1}" width="16" height="22" align="top" id="icon_{2}">', this.iconpath, iconName, layerID) : '';
			
			/**
			* Add event handlers
			*/
			var eventHandlers = "";
			for (j in nodes[i].events) {
				eventHandlers += this.stringFormat('{0}="{1}" ', j, nodes[i].events[j]);
			}

			/**
	        * Build the html to write to the document
			* IMPORTANT:
			* document.write()ing the string: '<div style="display:...' will screw up nn4.x
	        */
			var layerTag  = this.doesMenu() ? this.stringFormat('<div id="{0}" style="display: {1}" class="{2}">', layerID, visibility, (nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass)) : this.stringFormat('<div class="{0}">', nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass);
			var onMDown   = this.doesMenu() && nodes[i].n.length  && nodes[i].isDynamic ? this.stringFormat('onmousedown="{0}.toggleBranch(\'{1}\', true)" style="cursor: pointer; cursor: hand"', this.myname, layerID) : '';
			var imgTag    = this.stringFormat('<img src="{0}/{1}{2}.gif" width="16" height="22" align="top" border="0" name="img_{3}" {4}>', this.iconpath, gifname, modifier, layerID, onMDown);
			/** edit bartv 2008-02-12 **/
			if (nodes[i].icon=="vertline.gif") var imgTag ="";			
			var linkTarget= nodes[i].linkTarget ? nodes[i].linkTarget : this.linkTarget;
			var linkStart = nodes[i].link ? this.stringFormat('<a href="{0}" target="{1}">', nodes[i].link, linkTarget) : '';

			var linkEnd   = nodes[i].link ? '</a>' : '';

			this.output += this.stringFormat('{0}<nobr>{1}{2}{3}{4}<span {5}>{6}</span>{7}</nobr><br></div>',
			                  layerTag,
							  prepend,
			                  parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages) ? '' : imgTag,
							  iconimg,
							  linkStart,
							  eventHandlers,
							  nodes[i].title,
							  linkEnd);

			/**
	        * Traverse sub nodes ?
	        */
			if (nodes[i].n.length) {
				/**
	            * Determine what to prepend. If there is only one root
				* node then the prepend to pass to children is nothing.
				* Otherwise it depends on where we are in the tree.
	            */
				if (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages)) {
					var newPrepend = '';
	
				} else if (i < (nodes.length - 1)) {
					var newPrepend = prepend + this.stringFormat('<img src="{0}/vertline.gif" width="16" height="22" align="top">', this.iconpath);
	
				} else {
					var newPrepend = prepend + this.stringFormat('<img src="{0}/blank.gif" width="16" height="22" align="top">', this.iconpath);
				}

				this.drawMenu(nodes[i].n,
				              arrayCopy(level),
				              newPrepend,
				              nodes[i].expanded,
				              expanded ? 'inline' : 'none',
				              layerID);
			}
		}
	}

/**
* Writes the output generated by drawMenu() to the page
*/
	TreeMenu.prototype.writeOutput = function ()
	{
		document.write(this.output);
	}

/**
* Toggles a branches visible status. Called from resetBranches()
* and also when a +/- graphic is clicked.
*/
	TreeMenu.prototype.toggleBranch = function (layerID, updateStatus) // OPTIONAL ARGS: fireEvents = true
	{
		var currentDisplay = this.getLayer(layerID).style.display;
		var newDisplay     = (this.branchStatus[layerID] && currentDisplay == 'inline') ? 'none' : 'inline';
		var fireEvents     = arguments[2] != null ? arguments[2] : true;

		for (var i=0; i<this.layerRelations[layerID].length; i++) {

			if (this.branchStatus[this.layerRelations[layerID][i]]) {
				this.toggleBranch(this.layerRelations[layerID][i], false);
			}
	
			this.getLayer(this.layerRelations[layerID][i]).style.display = newDisplay;
		}
	
		if (updateStatus) {
			this.branchStatus[layerID] = !this.branchStatus[layerID];
	
			/**
	        * Persistence
	        */
			if (this.doesPersistence() && !arguments[2] && this.usePersistence) {
				this.setExpandedStatusForCookie(layerID, this.branchStatus[layerID]);
			}

			/**
			* Fire custom events
			*/
			if (fireEvents) {
				nodeObject = this.nodeRefs[layerID];
	
				if (nodeObject.ontoggle != null) {
					eval(nodeObject.ontoggle);
				}
				
				if (newDisplay == 'none' && nodeObject.oncollapse != null) {
					eval(nodeObject.oncollapse);
				} else if (newDisplay == 'inline' && nodeObject.onexpand != null){
					eval(nodeObject.onexpand);
				}
			}

			// Swap image
			this.swapImage(layerID);
		}

		// Swap icon
		this.swapIcon(layerID);
	}

/**
* Swaps the plus/minus branch images
*/
	TreeMenu.prototype.swapImage = function (layerID)
	{
		var imgSrc = document.images['img_' + layerID].src;
	
		var re = /^(.*)(p|m)(last)?.gif$/
		if (matches = imgSrc.match(re)) {
	
			document.images['img_' + layerID].src = this.stringFormat('{0}{1}{2}{3}',
			                                                matches[1],
															matches[2] == 'p' ? 'm' : 'p',
															matches[3] ? matches[3] : 'node',
															'.gif');
		}
	}

/**
* Swaps the icon for the expanded icon if one
* has been supplied.
*/
	TreeMenu.prototype.swapIcon = function (layerID)
	{
		if (document.images['icon_' + layerID]) {
			var imgSrc = document.images['icon_' + layerID].src;
	
			if (this.nodeRefs[layerID].icon && this.nodeRefs[layerID].expandedIcon) {
				var newSrc = (imgSrc.indexOf(this.nodeRefs[layerID].expandedIcon) == -1 ? this.nodeRefs[layerID].expandedIcon : this.nodeRefs[layerID].icon);
	
				document.images['icon_' + layerID].src = this.iconpath + '/' + newSrc;
			}
		}
	}

/**
* Can the browser handle the dynamic menu?
*/
	TreeMenu.prototype.doesMenu = function ()
	{
		return (is_ie4up || is_nav6up || is_gecko || is_opera7);
	}

/**
* Can the browser handle save the branch status
*/
	TreeMenu.prototype.doesPersistence = function ()
	{
		return (is_ie4up || is_gecko || is_nav6up || is_opera7);
	}

/**
* Returns the appropriate layer accessor
*/
	TreeMenu.prototype.getLayer = function (layerID)
	{
		if (is_ie4) {
			return document.all(layerID);
	
		} else if (document.getElementById(layerID)) {
			return document.getElementById(layerID);
	
		} else if (document.all && document.all(layerID)) {
			return document.all(layerID);
		}
	}

/**
* Save the status of the layer
*/
	TreeMenu.prototype.setExpandedStatusForCookie = function (layerID, expanded)
	{
		this.cookieStatuses[layerID] = expanded;
		this.saveCookie();
	}

/**
* Load the status of the layer
*/
	TreeMenu.prototype.getExpandedStatusFromCookie = function (layerID)
	{
		if (this.cookieStatuses[layerID]) {
			return this.cookieStatuses[layerID];
		}

		return false;
	}

/**
* Saves the cookie that holds which branches are expanded.
* Only saves the details of the branches which are expanded.
*/
	TreeMenu.prototype.saveCookie = function ()
	{
		var cookieString = new Array();

		for (var i in this.cookieStatuses) {
			if (this.cookieStatuses[i] == true) {
				cookieString[cookieString.length] = i;
			}
		}
		
		document.cookie = 'TreeMenuBranchStatus=' + cookieString.join(':');
	}

/**
* Reads cookie parses it for status info and
* stores that info in the class member.
*/
	TreeMenu.prototype.loadCookie = function ()
	{
		var cookie = document.cookie.split('; ');

		for (var i=0; i < cookie.length; i++) {
			var crumb = cookie[i].split('=');
			if ('TreeMenuBranchStatus' == crumb[0] && crumb[1]) {
				var expandedBranches = crumb[1].split(':');
				for (var j=0; j<expandedBranches.length; j++) {
					this.cookieStatuses[expandedBranches[j]] = true;
				}
			}
		}
	}

/**
* Reset branch status
*/
	TreeMenu.prototype.resetBranches = function ()
	{
		if (!this.doesPersistence()) {
			return false;
		}
		
		this.loadCookie();

		for (var i=0; i<this.branches.length; i++) {
			var status = this.getExpandedStatusFromCookie(this.branches[i]);
			// Only update if it's supposed to be expanded and it's not already
			if (status == true && this.branchStatus[this.branches[i]] != true) {
				if (this.checkParentVisibility(this.branches[i])) {
					this.toggleBranch(this.branches[i], true, false);
				} else {
					this.branchStatus[this.branches[i]] = true;
					this.swapImage(this.branches[i]);
				}
			}
		}
	}

/**
* Checks whether a branch should be open
* or not based on its parents' status
*/
	TreeMenu.prototype.checkParentVisibility = function (layerID)
	{
		if (this.in_array(this.childParents[layerID], this.branches)
		    && this.branchStatus[this.childParents[layerID]]
			&& this.checkParentVisibility(this.childParents[layerID]) ) {
			
			return true;
	
		} else if (this.childParents[layerID] == null) {
			return true;
		}
		
		return false;
	}

/**
* New C# style string formatter
*/
	TreeMenu.prototype.stringFormat = function (strInput)
	{
		var idx = 0;
	
		for (var i=1; i<arguments.length; i++) {
			while ((idx = strInput.indexOf('{' + (i - 1) + '}', idx)) != -1) {
				strInput = strInput.substring(0, idx) + arguments[i] + strInput.substr(idx + 3);
			}
		}
		
		return strInput;
	}

/**
* Also much adored, the PHP implode() function
*/
	TreeMenu.prototype.implode = function (seperator, input)
	{
		var output = '';
	
		for (var i=0; i<input.length; i++) {
			if (i == 0) {
				output += input[i];
			} else {
				output += seperator + input[i];
			}
		}
		
		return output;
	}

/**
* Aah, all the old favourites are coming out...
*/
	TreeMenu.prototype.in_array = function (item, arr)
	{
		for (var i=0; i<arr.length; i++) {
			if (arr[i] == item) {
				return true;
			}
		}
	
		return false;
	}

/**
* TreeNode Class
*/
	function TreeNode(title, icon, link, expanded, isDynamic, cssClass, linkTarget, expandedIcon)
	{
		this.title        = title;
		this.icon         = icon;
		this.expandedIcon = expandedIcon;
		this.link         = link;
		this.expanded     = expanded;
		this.isDynamic    = isDynamic;
		this.cssClass     = cssClass;
		this.linkTarget   = linkTarget;
		this.n            = new Array();
		this.events       = new Array();
		this.handlers     = null;
		this.oncollapse   = null;
		this.onexpand     = null;
		this.ontoggle     = null;
	}

/**
* Adds a node to an already existing node
*/
	TreeNode.prototype.addItem = function (newNode)
	{
		newIndex = this.n.length;
		this.n[newIndex] = newNode;
		
		return this.n[newIndex];
	}

/**
* Sets an event for this particular node
*/
	TreeNode.prototype.setEvent = function (eventName, eventHandler)
	{
		switch (eventName.toLowerCase()) {
			case 'onexpand':
				this.onexpand = eventHandler;
				break;

			case 'oncollapse':
				this.oncollapse = eventHandler;
				break;

			case 'ontoggle':
				this.ontoggle = eventHandler;
				break;

			default:
				this.events[eventName] = eventHandler;
		}
	}

/**
* That's the end of the tree classes. What follows is
* the browser detection code.
*/
	

//<!--
// Ultimate client-side JavaScript client sniff. Version 3.03
// (C) Netscape Communications 1999-2001.  Permission granted to reuse and distribute.
// Revised 17 May 99 to add is_nav5up and is_ie5up (see below).
// Revised 20 Dec 00 to add is_gecko and change is_nav5up to is_nav6up
//                      also added support for IE5.5 Opera4&5 HotJava3 AOLTV
// Revised 22 Feb 01 to correct Javascript Detection for IE 5.x, Opera 4,
//                      correct Opera 5 detection
//                      add support for winME and win2k
//                      synch with browser-type-oo.js
// Revised 26 Mar 01 to correct Opera detection
// Revised 02 Oct 01 to add IE6 detection

// Everything you always wanted to know about your JavaScript client
// but were afraid to ask. Creates "is_" variables indicating:
// (1) browser vendor:
//     is_nav, is_ie, is_opera, is_hotjava, is_webtv, is_TVNavigator, is_AOLTV
// (2) browser version number:
//     is_major (integer indicating major version number: 2, 3, 4 ...)
//     is_minor (float   indicating full  version number: 2.02, 3.01, 4.04 ...)
// (3) browser vendor AND major version number
//     is_nav2, is_nav3, is_nav4, is_nav4up, is_nav6, is_nav6up, is_gecko, is_ie3,
//     is_ie4, is_ie4up, is_ie5, is_ie5up, is_ie5_5, is_ie5_5up, is_ie6, is_ie6up, is_hotjava3, is_hotjava3up,
//     is_opera2, is_opera3, is_opera4, is_opera5, is_opera5up
// (4) JavaScript version number:
//     is_js (float indicating full JavaScript version number: 1, 1.1, 1.2 ...)
// (5) OS platform and version:
//     is_win, is_win16, is_win32, is_win31, is_win95, is_winnt, is_win98, is_winme, is_win2k
//     is_os2
//     is_mac, is_mac68k, is_macppc
//     is_unix
//     is_sun, is_sun4, is_sun5, is_suni86
//     is_irix, is_irix5, is_irix6
//     is_hpux, is_hpux9, is_hpux10
//     is_aix, is_aix1, is_aix2, is_aix3, is_aix4
//     is_linux, is_sco, is_unixware, is_mpras, is_reliant
//     is_dec, is_sinix, is_freebsd, is_bsd
//     is_vms
//
// See http://www.it97.de/JavaScript/JS_tutorial/bstat/navobj.html and
// http://www.it97.de/JavaScript/JS_tutorial/bstat/Browseraol.html
// for detailed lists of userAgent strings.
//
// Note: you don't want your Nav4 or IE4 code to "turn off" or
// stop working when new versions of browsers are released, so
// in conditional code forks, use is_ie5up ("IE 5.0 or greater")
// is_opera5up ("Opera 5.0 or greater") instead of is_ie5 or is_opera5
// to check version in code which you want to work on future
// versions.

/**
* Severly curtailed all this as only certain elements
* are required by TreeMenu, specifically:
*  o is_ie4up
*  o is_nav6up
*  o is_gecko
*/

    // convert all characters to lowercase to simplify testing
    var agt=navigator.userAgent.toLowerCase();

    // *** BROWSER VERSION ***
    // Note: On IE5, these return 4, so use is_ie5up to detect IE5.
    var is_major = parseInt(navigator.appVersion);
    var is_minor = parseFloat(navigator.appVersion);

    // Note: Opera and WebTV spoof Navigator.  We do strict client detection.
    // If you want to allow spoofing, take out the tests for opera and webtv.
    var is_nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
                && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
                && (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
    var is_nav6up = (is_nav && (is_major >= 5));
    var is_gecko = (agt.indexOf('gecko') != -1);


    var is_ie     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    var is_ie4    = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) );
    var is_ie4up  = (is_ie && (is_major >= 4));
	
	var is_opera  = (agt.indexOf("opera") != -1);
	var is_opera7 = (is_opera && is_major >= 7) || agt.indexOf("opera 7") != -1;

	// Patch from Harald Fielker
    if (agt.indexOf('konqueror') != -1) {
        var is_nav    = false;
        var is_nav6up = false;
        var is_gecko  = false;
        var is_ie     = true;
        var is_ie4    = true;
        var is_ie4up  = true;
    }
//--> end hide JavaScript


var aphia_fn="cumacea.php?"
/* --------------------------------*/
/* Created by Jan Feyereisl - 2007 */
/* --------------------------------*/
/* modified for Aphia by bartv 2009-08-20 */

// LIMITATIONS:
// This gadget calls Google Scholar in the same fashion as a web-browser would
// Suitable Scholar API needs to be first released in order to get a more useful and accurate code
// The gadget looks at the first 100 results returned by Scholar and caculates
// citations from those by simple string tokenization and the resulting int addition.
// The position of the author is not taken into account.
// Citation is calculated as long as the searched person is one of the named authors on the paper.

// !ISSUE! - 'enter' key has an issue in Googgle Chrome Browser
// !ISSUE! - Assumes that the number of citations per paper does not exceed 9999
// When using or - the operation has to be in capital letters if in "other" search term box

// HTML variable to generate html code to be printed out
var cit_html = "";

// Global variable to hold the total number of returned results by Google
var cit_totalResults = null;

// Variable which determines the number of returned paper records
var cit_ret_results = 100;

// Global search variables
var cit_author = "";
var cit_other = "";

// Needs to be declared global otherwise it doesnt get recognized by the setTimeout() method
var cit_citePages = new Array();
var cit_publications = 0;
var cit_pages = 0;
var cit_done = false;
var cit_gAuthor = "";
var cit_gOther = "";

// -----------------------
function queryScholar(otherterm){

    // Display loading icon first
    document.getElementById("aphia_citations_loading").style.display="inline";

	// HTML variable to generate html code to be printed out
	cit_html = "";

    // Clear global variables for subsequent use
    cit_citePages = new Array();
    cit_publications = 0;
    cit_pages = 0;
    cit_done = false;
    cit_totalResults = null;
    ret_results = 100;
    cit_other = otherterm;
    cit_gOther = "";

	// Convert search string into the correct Google search format
	// (e.g. add "+" in-between search terms in order for Boolean operations to work)
    cit_gOther = cit_other.replace(/ /gi, "+");
  	// End of global variables declaration

  	// Fetch Information about total number of results returned by Google
  	getTotalResultsInfo("", cit_gOther);
}

// ----------------------
// Function to fetch vital information for retreiving full citations from multiple pages
// ----------------------
function getTotalResultsInfo(cit_gAuthor, cit_gOther){

    // Generate correct http request
  	var url_to_get = "as_q="+cit_gOther+"&num="+ret_results+"&as_sauthors="+cit_gAuthor;

    aphia_FetchContent(url_to_get, function(responseText){
        if (responseText == null){
            _gel("aphia_citations").innerHTML = "<i>Invalid data.</i>";
            //alert("There is no data.");
    	    return;
        }

        // Variables used to find the correct location of the total number of returned results
        var pre = 'of about <b>';
		var post = /<\/b>\.\s*\(<b>/;

        // Locate the place where the total results value is positioned
        var resultPositionPre = responseText.search(pre) + pre.length;
        var resultPositionPost = responseText.search(post);
        var resultLength = resultPositionPost - resultPositionPre;

        // Extract the total number of results returned
        var tResults = responseText.substr(resultPositionPre, resultLength);

        // Remove the comma representing thousands - it prevents js to treat the string as a number
        while(tResults.search(',') != -1){
            tResults = tResults.substr(0, tResults.search(',')) + tResults.substr(tResults.search(',')+1, tResults.length);
        }

        // Calculate how many pages we need to fetch
        if(tResults > 100){
            pages = (tResults)/ret_results;

            // Temporary counter as an array indexer
            var counter = 0;

            // Fetch all fetchable pages (i.e. fetch 'pages' pages[Google's limit] or 10 pages)
            if(pages < 10){
                for(var i = 0; i < pages; i++)
	            {
	                start = i * 100;
                    var url_to_get = "as_q="+cit_gOther+"&num="+ret_results+"&as_sauthors="+cit_gAuthor+"&start="+start;

                    aphia_FetchContent(url_to_get, function(responseText1){
                        if (responseText == null){
                            _gel("aphia_citations").innerHTML = "<i>Invalid data.</i>";
                            //alert("There is no data.");
    	                    return;
                        }
	                    cit_citePages[counter++] = getCitationCount(responseText1);
	                    return;
	                });
	            }
	            done = true;
            }else{
                for(var i = 0; i < 10; i++)
	            {
	                start = i * 100;
	                var url_to_get = "as_q="+cit_gOther+"&num="+ret_results+"&as_sauthors="+cit_gAuthor+"&start="+start;

                    aphia_FetchContent(url_to_get, function(responseText2){
                        if (responseText == null){
                            _gel("aphia_citations").innerHTML = "<i>Invalid data.</i>";
                            //alert("There is no data.");
    	                    return;
                        }
                        cit_citePages[counter++] = getCitationCount(responseText2);
	                    return;
	               });
	            }
	            done = true;
	        }

            // Repeatedly wait until we receive all the results from the fetched pages
	        setTimeout("wait()", 3000);

	    }else{
	        var url_to_get = "as_q="+cit_gOther+"&num="+ret_results+"&as_sauthors="+cit_gAuthor;

            aphia_FetchContent(url_to_get, function(responseText3){
                if (responseText == null){
                    _gel("aphia_citations").innerHTML = "<i>Invalid data.</i>";
                    //alert("There is no data.");
                    return;
                }
                cit_citePages[0] = getCitationCount(responseText3);
                totalCites();
                return;
           });
	    } // End of checking if more than 100 results are returned if(tResults > 100)
    });
}

function wait(){
    if(done != true){
        // TODO - TEST THIS IF IT EVER OCCURS
        //alert("Waiting...: " + citePages.length);
        setTimeout("wait()", 3000);
    }else{
        // alert("Done: " + citePages.length);
        totalCites();
    }
    return;
}

function totalCites(){
    // Calculate the total number of citations from all fetched pages
    var total_citations = 0;

    for(var i = 0; i < cit_citePages.length; i++){
        var citeArray = cit_citePages[i];
	    for(var j = 0; j < citeArray.length; j++){
	        // The multiplication by one is a hack to convert the string type into a numerical type
	        total_citations += citeArray[j]*1;
        }
    }

    // Print out the result to the screen
    cit_html += "Citations for " + cit_other + ": <b>" + total_citations + "</b><br />";
    cit_html += "Cited publications: <b>" + cit_publications + "</b><br />";
    cit_html += "H-Index: <b>" + h_index() + "</b><br />";
    cit_html += "[<a href='http://scholar.google.com/scholar?as_q=" + cit_gOther + "&as_sauthors=' target='_blank'>view publications</a>]<br />";

    // Output html in div.
    _gel("aphia_citations").innerHTML = cit_html;

    // Hide loading icon first
    document.getElementById("aphia_citations_loading").style.display="none";

    // Display the results
    document.getElementById("aphia_citations").style.display="block";

}

function h_index(){
    var hArray = new Array();
    var x = 0;
    for(var i = 0; i < cit_citePages.length; i++){
        var citeArray = cit_citePages[i];
	    for(var j = 0; j < citeArray.length; j++){
	        // The multiplication by one is a hack to convert the string type into a numerical type
	        hArray[x++] = citeArray[j]*1;
        }
    }
    hArray.sort(sortNumber);
    for(var i = 0; i < hArray.length; i++){
        if(i > hArray[i]){
            return hArray[i-1];
        }
    }
}

function sortNumber(a,b)
{
    return b - a
}

// ----------------------
// Function to tokenize returned HTML response and sum up the Author's citation count
// ----------------------
function getCitationCount(responseText){
	if (responseText == null){
		_gel("aphia_citations").innerHTML = "<i>Invalid data.</i>";
    		return;
            }

	var cite_exists = 1;
	var cite_str_len = 14;
	var len_of_Cite_by_str = 9;
	var citeArray = new Array();
	for(var i = 0; cite_exists > 0; i++)
	{
		cite_exists = responseText.search('Cited by');
		if(cite_exists == -1){
			//alert("No more citations for given Author!");
			//return;
		}else{
			var tmp_string = responseText.substr(cite_exists, cite_str_len);
			var end = (tmp_string.indexOf("<")-len_of_Cite_by_str);
			citeArray[i] = tmp_string.substr(len_of_Cite_by_str, end);
			cit_publications++;
			responseText = responseText.substr(cite_exists+cite_str_len, responseText.length);
		}
	}
	return citeArray;
}
// ------------------------

function aphia_FetchContent(url,callback){
   var requestObject = createXmlHttpRequest();
   var result;

   requestObject.open("GET",aphia_fn+"p=ajax&a=list_scholar&url="+escape(url),true);
   requestObject.onreadystatechange = function ProcessRequest(){
   if (requestObject.readyState == 4)
     {
        if (requestObject.status == 200)
        {
          result = requestObject.responseText;
        }
        else
        {
          result=null;
        }
        callback(result);
    }
 }
 requestObject.send(null);
}


function _gel(id){
 return document.getElementById(id);
}