





  
    
// NextBio name space 
var nbApi = {debug: false};

nbApi.apiKey = "nextbio-test";
nbApi.nameSpace = "nbApi";
nbApi.debug = false;
nbApi.images = {};

nbApi.elsevier = false;
nbApi.system = "PUBLIC";

nbApi.sessionId = "";
nbApi.timeStamp = "";
nbApi.timeoutStamp = "";
nbApi.urlContext = "https://www.nextbio.com/b";
nbApi.imgContext = "https://www.nextbio.com/b/s/img3";

nbApi.debugMode = function(debug)
{           
  nbApi.setDebugMode(debug);
};
nbApi.setDebugMode = function(debug)
{           
  nbApi.debug = debug == true;
};
nbApi.getDebugMode = function()
{           
  return nbApi.debug == true;
};





  
    
// NextBio API utility functions
// requires jsApi.jsp

// LoadFrame(config, bAlert) - load from server in an iFrame 
// configuration parameters:
// config.target - required. Id of a div, span, td, etc. to receive results loaded from server  
// config.url - required. URL of a web service processing AJAX call  
// config.query - optional. List of parameters for the URL (see above)
// config.success - optional. A callback function that will be excuted in case of a successfull call, after results are loaded to the target 
//    success(args) - where args is described below. 
// config.failure - optional. A callback function that will be executed in case of a failed call:  
//    failure(args) - where args is described below. 
// config.args - optional. A string that will be passsed as a parameter to success() and/or failure() callbacks if either one is specified.
// bAlert - optional. True if we want to display alerts.
nbApi.loadFrame = function(config)
{
  var bAlert = nbApi.getDebugMode();
  if (config == null)
  {
    if (bAlert)
      alert("Nextbio API: No configuration object.");
    return false;
  }
  if (config.url == null)
  {
    if (bAlert)
      alert("Nextbio API: Configuration object contains no URL.");
    return false;
  }   
  var target = document.getElementById(config.target);
  if (target == null)
  {
    if (bAlert)
      alert("Nextbio API: Target with id=" + congig.target + " was not found on this page.");
    return;
  } 
  var id = "nbTagCloud_" + config.target;
  var url = config.url;
  if (config.query != null)
    url += "?" + config.query; 
  var target = document.getElementById(config.target);
  target.innerHTML = "<iframe id=\'" + id + "\' style=\'" + config.style + "\'></iframe>";
  target = document.getElementById(id);
  target.src = url;
};
// LoadScript(config, bAlert) - load from server as a javascript script
// configuration parameters:
// config.target - required. Id of a div, span, td, etc. to receive results loaded from server  
// config.url - required. URL of a web service processing AJAX call  
// config.query - optional. List of parameters for the URL (see above)
// config.success - optional. A callback function that will be excuted in case of a successfull call, after results are loaded to the target 
//    success(args) - where args is described below. 
// config.failure - optional. A callback function that will be executed in case of a failed call:  
//    failure(args) - where args is described below. 
// config.args - optional. A string that will be passsed as a parameter to success() and/or failure() callbacks if either one is specified.
// bAlert - optional. True if we want to display alerts.
  // config.scriptTarget - optional.  If id of parent to load script into.  When using wz_tooltip library, should not be in the tooltip popup. Solves DE4705
nbApi.loadScript = function(config)
{
  var bAlert = nbApi.getDebugMode();
  if (config == null)
  {
    if (bAlert)
      alert("Nextbio API: No configuration object.");
    return false;
  }
  var target = document.getElementById(config.target);
  if (target == null)
  {
    if (bAlert)
      alert("Nextbio API: Target doesn't exist.");
    return false;
  }
  if (config.url == null)
  {
    if (bAlert)
      alert("Nextbio API: Configuration object contains no URL.");
    return false;
  } 
       
  var message;
  if (config.message != null)
  {
    if (config.messageStyle == null)
      message = config.message; 
    else
      message = '<span style="' + config.messageStyle + '">' + config.message + '</span>';
    message = '<span class="nbApiLoading">&nbsp;' + message;
    nbApi.setInnerHtml(config.target, message);
    target.style.cursor = "wait";
  }
  var url = config.url;
  var params = "containerId=" + config.containerId + "&targetId=" + config.target + "&callback=" + config.callbackName; 
// setup timeout callback
  var timeoutId = null;
  if (config.timeout != null && config.timeout > 0)
  {
    timeoutId = nbApi.createId("nextbioTimeout");  
    var timeout = setTimeout(function(){nbApi.loadTimeoutHandler(config.target, timeoutId, config.timeoutCallback)}, config.timeout);
    target.setAttribute(timeoutId, timeout);
    params += "&timeoutId=" + timeoutId;
    if (config.timeoutTerminate == true)
    {
      params += "&timeoutTerminate=true";
    }
  }  
  if (config.query == null)
    config.query = params;
  else
    config.query += "&" + params;
  url += "?" + config.query; 
  if (config.addlParams)
  {
  
	url += "&" + config.addlParams; 
  }
  var updateId = target.getAttribute("nextbioUpdateId");
  if (!updateId)
  {
    updateId = 0;
  }
  updateId++;
  target.setAttribute("nextbioUpdateId", updateId);
  url += "&updateId=" + updateId;
  target.nextbioScriptCallback = config.callback;
  nbApi.importScript(url, config.iFrameId, config.scriptTarget||config.target);
};
nbApi.updateTargetHTML = function(json)
{

  if (json == null || json.target == null) 
    return;
  var target = document.getElementById(json.target);
  if (target == null || target.getAttribute("nextbio_allowUpdate") != "true")
    return;
  var updateId = target.getAttribute("nextbioLastDisplayedUpdateId");
  if (!updateId)
  {
    updateId = json.updateId;
  }
  if (updateId > json.updateId)
  {
    return;
  }
  target.setAttribute("nextbioLastDisplayedUpdateId", json.updateId);
  if (json.timeoutId != null)
  {
    var timeoutId = target.getAttribute(json.timeoutId);
    if (timeoutId == null || timeoutId.length == 0)
    {
      if (json.timeoutTerminate == true)
      {
        return;
      }
    }
    else
    {
      clearTimeout(timeoutId);
      target.setAttribute(json.timeoutId, null);
    }
  } 
  target.innerHTML = "";
  target.nextbioIsLoaded = false;

  var bLoaded = json.response != null && json.response.length > 0;
  if (bLoaded)
  {
    target.nextbioIsLoaded = true;
    if (target.nextbioHeader != null && target.nextbioHeader.length > 0)
    {
      var header = document.createElement("div");
      if (target.nextbioHeaderClassName != null)
        header.className = target.nextbioHeaderClassName;
      header.innerHTML = target.nextbioHeader;
      target.appendChild(header);
    }
    var body = document.createElement("div");
    body.className = target.nextbioTargetClassName;
    body.innerHTML = json.response;
    target.appendChild(body);
    if (target.nextbioFooter != null && target.nextbioFooter.length > 0)
    {
      var footer = document.createElement("div");
      if (target.nextbioFooterClassName != null)
        footer.className = target.nextbioFooterClassName;
      footer.innerHTML = target.nextbioFooter;
      target.appendChild(footer);
    }
  }
  target.style.cursor = "auto";
  target.scrollTop = 0; 

  if (target.nextbioScriptCallback != null)
    target.nextbioScriptCallback(json.target, bLoaded, json);
};
nbApi.updateTargetText = function(json)
{
  if (json == null || json.target == null || json.response == null)
    return;
  var target = document.getElementbyId(json.target);
  if (json.timeoutId != null)
  {
    var timeoutId = target.getAttribute(json.timeoutId);
    if (timeoutId == null || timeoutId.length == 0)
    {
      if (json.timeoutTerminate == true)
      {
        return;
      }
    }
    else
    {
      clearTimeout(timeoutId);
      target.setAttribute(json.timeoutId, null);
    }
  } 
  target.innerText = json.response;
  target.textContent = json.response;
  target.style.cursor = "auto";
};

nbApi.setInnerHtml = function(id, html)
{
  if (id == null || html == null)
    return;
  var el = document.getElementById(id);
  if (el == null)
    return;
  el.innerHTML = html;
};
nbApi.setInnerText = function(id, text)
{
  if (id == null || text == null)
    return;
  var el = document.getElementById(id);
  if (el == null)
    return;
  el.innerText = text;
  el.textContent = text;
};
nbApi.getInnerText = function(obj)
{
    if (obj == null)
      return null;
    var el = obj;
    if (typeof(obj) != "object")
    {
      el = document.getElementById(obj);
    }
    if (el == null)
      return null;
    if (el.innerText)
      return el.innerText;
    else
      return el.textContent;
};
nbApi.setAttr = function(id, attr, text)
{
    if (id == null || attr == null)
      return;
    var el;
    if (typeof(id) != "object")
      el = document.getElementById(id);
    else
      el = id;  
    
    if (el == null)
      return;
    if (!text)
      el.removeAttribute(attr);
    else
      el.setAttribute(attr, text);
};
nbApi.normalizeString = function(str)
{
  if (str == null)
    return "";
  str = str.replace(/^\s+/, "");  
  str = str.replace(/\s+$/, "");  
  str = str.replace(/\s+/g, " ");
  return str;  
};
nbApi.trim = function(str)
{
  if (str == null)
    return "";
  str = str.replace(/^\s+/, "");  
  str = str.replace(/\s+$/, "");  
  return str;  
};
// Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
nbApi.addClass = function(element, newClassName)
{
  if (element == null)
    return;
  var className = element.className;
  if (className == null || className.length == 0)
  {
    element.className = newClassName;
    return;
  }
  var nameArray = element.className.split(/\s+/);
  var out = newClassName;
  for (i = 0; i < nameArray.length; i++)
  {
    if (nameArray[i] != newClassName)
    {
      out += " " + nameArray[i];
    }
    
  }
  out = nbApi.trim(out);
  element.className = out;
};
nbApi.removeClass = function(element, className)
{
  if (element == null)
    return;
  if (className == null)
    return;

  var nameArray = element.className.split(/\s+/);
  var out = "";
  for (i = 0; i < nameArray.length; i++)
  {
    if (nameArray[i] != className)
    {
      if (i > 0)
        out += " ";
      out += nameArray[i];
    }
    
  }
  out = nbApi.trim(out);
  if (out.length > 0)
    element.className = out;
  else
    element.className = null;
};

nbApi.getWindowHeight = function() 
{
	var myHeight = 0;
   if( typeof( window.innerWidth ) == 'number' ) {
     //Non-IE
     myHeight = window.innerHeight;
   } else if( document.documentElement &&
       ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
     //IE 6+ in 'standards compliant mode'
     myHeight = document.documentElement.clientHeight;
   } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
     //IE 4 compatible
     myHeight = document.body.clientHeight;
   }
   return myHeight;
};

nbApi.getWindowWidth = function() 
{
	var myWidth = 0;
   if( typeof( window.innerWidth ) == 'number' ) {
     //Non-IE
     myWidth = window.innerWidth;
   } else if( document.documentElement &&
       ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
     //IE 6+ in 'standards compliant mode'
     myWidth = document.documentElement.clientWidth;
   } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
     //IE 4 compatible
     myWidth = document.body.clientWidth;
   }
   return myWidth;
};

nbApi.getElementLeft = function(eElement) 
{
   if (!eElement && this)                    // if argument is invalid
   {                                         // (not specified, is null or is 0)
      eElement = this;                       // and function is a method
   }                                         // identify the element as the method owner

   var bIE = document.all ? true : false; // initialize var to identify IE

   var nLeftPos = eElement.offsetLeft;       // initialize var to store calculations
   var eParElement = eElement.offsetParent;  // identify first offset parent element

   while (eParElement != null)
   {                                         // move up through element hierarchy

      if(bIE)                             // if browser is IE, then...
      {
         if( (eParElement.tagName != "TABLE") && (eParElement.tagName != "BODY") )
         {                                   // if parent is not a table or the body, then...
            nLeftPos += eParElement.clientLeft; // append cell border width to calcs
         }
      }
      else                                   // if browser is Gecko, then...
      {
         if(eParElement.tagName == "TABLE")  // if parent is a table, then...
         {                                   // get its border as a number
            var nParBorder = parseInt(eParElement.border);
            if(isNaN(nParBorder))            // if no valid border attribute, then...
            {                                // check the table's frame attribute
               var nParFrame = eParElement.getAttribute('frame');
               if(nParFrame != null)         // if frame has ANY value, then...
               {
                  nLeftPos += 1;             // append one pixel to counter
               }
            }
            else if(nParBorder > 0)          // if a border width is specified, then...
            {
               nLeftPos += nParBorder;       // append the border width to counter
            }
         }
      }
      nLeftPos += eParElement.offsetLeft;    // append left offset of parent
      eParElement = eParElement.offsetParent; // and move up the element hierarchy
   }                                         // until no more offset parents exist
   if (scroll) {
   		eParElement = eElement;
   		while (eParElement) {
   			if (eParElement.scrollLeft) {
   				nLeftPos -= eParElement.scrollLeft;
   			}
   			eParElement = eParElement.parentNode;
   		}
   }
   return nLeftPos;                          // return the number calculated
};


// get the top offset of the element.  If scroll is true, take scrolling into account
nbApi.getElementTop = function(eElement, scroll) 
{
   if (!eElement && this)                    // if argument is invalid
   {                                         // (not specified, is null or is 0)
      eElement = this;                       // and function is a method
   }                                         // identify the element as the method owner

   var bIE = document.all ? true : false; // initialize var to identify IE

   var nTopPos = eElement.offsetTop;         // initialize var to store calculations
   var eParElement = eElement.offsetParent;  // identify first offset parent element

   while (eParElement != null)
   {                                         // move up through element hierarchy
      if(bIE)                             // if browser is IE, then...
      {
         if( (eParElement.tagName != "TABLE") && (eParElement.tagName != "BODY") )
         {                                   // if parent a table cell, then...
            nTopPos += eParElement.clientTop; // append cell border width to calcs
         }
      }
      else                                   // if browser is Gecko, then...
      {
         if(eParElement.tagName == "TABLE")  // if parent is a table, then...
         {                                   // get its border as a number
            var nParBorder = parseInt(eParElement.border);
            if(isNaN(nParBorder))            // if no valid border attribute, then...
            {                                // check the table's frame attribute
               var nParFrame = eParElement.getAttribute('frame');
               if(nParFrame != null)         // if frame has ANY value, then...
               {
                  nTopPos += 1;              // append one pixel to counter
               }
            }
            else if(nParBorder > 0)          // if a border width is specified, then...
            {
               nTopPos += nParBorder;        // append the border width to counter
            }
         }
      }

      nTopPos += eParElement.offsetTop;      // append top offset of parent
      eParElement = eParElement.offsetParent; // and move up the element hierarchy
   }                                         // until no more offset parents exist
   if (scroll) {
   		eParElement = eElement;
   		while (eParElement) {
   			if (eParElement.scrollTop) {
   				nTopPos -= eParElement.scrollTop;
   			}
   			eParElement = eParElement.parentNode;
   		}
   }
   return nTopPos;                           // return the number calculated
};
// get scroll factor for the element
nbApi.getScrollLeft = function()
{
  if (typeof( window.pageXOffset ) == 'number')
    return window.pageXOffset;
  if (document.body != null && document.body.scrollLeft)
    return document.body.scrollLeft;  
  if (document.documentElement != null && document.documentElement.scrollLeft)
    return document.documentElement.scrollLeft;
  return 0;    
};

nbApi.getScrollTop = function()
{
  if (typeof( window.pageYOffset ) == 'number')
    return window.pageYOffset;
  if (document.body != null && document.body.scrollTop)
    return document.body.scrollTop;  
  if (document.documentElement != null && document.documentElement.scrollTop)
    return document.documentElement.scrollTop;
  return 0;    
};

nbApi.onEvent = function(event, containerId, param, extra)
{
  if (event == null || containerId == null)
    return;
  var container = document.getElementById(containerId);
  if (container == null)
    return;  
  if (event.type.toLowerCase() == "click")
  {
    if (container.nextbioOnClick != null)
      container.nextbioOnClick(event, containerId, param, extra, container.nextbioUrlContext);
  }
  else if (event.type.toLowerCase() == "mouseover")
  {
    if (container.nextbioOnMouseOver != null)
      container.nextbioOnMouseOver(event, containerId, param, container.nextbioOnMouseOverParam, container.nextbioUrlContext);
  }
  else if (event.type.toLowerCase() == "mouseout")
  {
    if (container.nextbioOnMouseOut != null)
      container.nextbioOnMouseOut(event, containerId, param, container.nextbioOnMouseOutParam, container.nextbioUrlContext);
  }
};
nbApi.showFrame = function(event, popupObj, param, extra, urlContext)
{
    if (event == null || popupObj == null)
      return false;
    var redirect = false;
    var popupId = null;
    var owner = null;
    if (typeof(popupObj) == "object")
    {
      popupId = popupObj.containerId;
      param = popupObj.tagName;
      extra = popupObj.customParam;
      urlContext = popupObj.urlContext;
      redirect = popupObj.redir;
      owner = popupObj.owner;
      if (urlContext == null && nbApi.consumer != null)
        urlContext = nbApi.consumer.targetUrl;
    }
    else
      popupId = popupObj;
    if (popupId == null)
      return false;
    if (param == null)
      return false;
    var url;
    if (urlContext == null)
      urlContext = "https://www.nextbio.com/b";
    var context = "ov";
    if (nbApi.consumer && nbApi.consumer.name == "elsevier.sciencedirect")
    {
      context = "ft";
    }
    if ("1.0" == "1.0.ui2")
    {
      url = urlContext + "/home/home.nb?q=" + encodeURIComponent(param) + "&" + extra;
    }
    else if (param.indexOf('%') >= 0 || param.indexOf('/') >= 0 || param.indexOf('+') >= 0) {
      url = urlContext + "/search/" + context + ".nb?q=" + encodeURIComponent(param) + "&" + extra;
    }
    else
    {
      if (event.urlContext != null)
        context = event.urlContext;
      url = urlContext + "/search/" + context + "/" + encodeURIComponent(param) + "?" + extra;
    }
    if (redirect)
    {
      var targetUrl = encodeURIComponent(url);
      var campaignParam = "";
      campaignParam += "NextBio|";
      campaignParam += "tagType=" + popupObj.type + "|";
      var viewMore = popupObj.viewMore=="yes"?"yes":"no";
      campaignParam += "viewMore=" + viewMore + "|";
      campaignParam += "sessionId=" + popupObj.sessionId + "|";
      campaignParam += "pii=" + popupObj.pii + "|";
      campaignParam += "uiStrategy=" + popupObj.strategy + "|";
      campaignParam += "tagName=" + popupObj.tagName + "|";
      campaignParam += "cloudSize=" + popupObj.size + "|";
      campaignParam += "version=" + nbApi.consumer.version;
      campaignParam = encodeURIComponent(campaignParam);
      url = nbApi.consumer.redirectUrl + "?campaign_par=" + campaignParam + "&targetURL=" + targetUrl;    
    }
    var container = document.getElementById(popupId + "_frame_container");
    var frameProperties = nbApi.getFrameProperties(popupId);
    if (frameProperties == null)
    {
      if (container == null)
        return false; 
      nbApi.removeFrame(popupId);
      var frame = document.createElement("iframe");
      frame.id = popupId + "_frame";
      frame.style.top = "0px";
      frame.style.left = "0px";
//       frame.style.width = "100%";
//       frame.style.height = "100%";
      frame.style.width = container.style.width;
      frame.style.height = container.style.height;
      frame.frameBorder = 1;
      container.appendChild(frame);
      frame.src = url; 
      return false;
    }
    if (frameProperties.iFrame == "true" || frameProperties.iFrame == true)
    {
      if (container == null)
        return false; 
      nbApi.removeFrame(popupId);
      var winWidth = nbApi.getWindowWidth();
      var winHeight = nbApi.getWindowHeight();
//       var frameWidth = Math.min(winWidth - 50, frameProperties.width);
//       var frameHeight = Math.min(winHeight - 50, frameProperties.height);
//       frameWidth = Math.max(frameWidth, 200);
//       frameHeight = Math.max(frameHeight, 150);
      var frameTop = Math.floor((winHeight - frameHeight)/2) + nbApi.getScrollTop() - 20; 
      var frameLeft = Math.floor((winWidth - frameWidth)/2) + nbApi.getScrollLeft() - 10; 
      frameTop = Math.max(frameTop, 0);
      frameLeft = Math.max(frameLeft, 0);
      
//      container.style.position = "absolute";
//       container.style.top = frameTop + "px";
//       container.style.left = frameLeft + "px";
      var frame = document.createElement("iframe");
      frame.id = popupId + "_frame";
      frame.frameBorder = 0;
      container.appendChild(frame);
      container.style.display = "";
      frame.style.position = "relative";
      frame.style.top = "0px";
      frame.style.left = "0px";
//       frame.style.width = "100%";
//       frame.style.height = "100%";
      frame.style.width = container.style.width;
      frame.style.height = container.style.height;
      frame.frameBorder = 1;
      frame.src = url; 
      return false;
    }
  
    var target = frameProperties.target;
    if (owner && (!target))
    {
      if (frameProperties.targetName)
      {
        owner.target = frameProperties.targetName;
      }
      owner.href = url;
      return true;
    }  
  
    var screenWidth = screen.availWidth != null ? screen.availWidth : screen.width;
    var screenHeight = screen.availHeight != null ? screen.availHeight : screen.height - 100; // to account for non-client area of a window. 
    var frameWidth = Math.min(screenWidth - 50, frameProperties.width);
    var frameHeight = Math.min(screenHeight - 50, frameProperties.height);
    frameWidth = Math.max(frameWidth, 200);
    frameHeight = Math.max(frameHeight, 200);
    var frameTop = frameProperties.top < 0 ? Math.floor((screenHeight - frameHeight)/2) : frameProperties.top; 
    var frameLeft = frameProperties.left < 0 ? Math.floor((screenWidth - frameWidth)/2) : frameProperties.left; 
    frameTop = Math.max(frameTop, 0);
    frameLeft = Math.max(frameLeft, 0);
  
    var winParams = "resizable=yes,scrollbars=yes,status=yes,toolbar=yes,location=yes,menubar=no,top=" + frameTop + ",left=" + frameLeft + ",width=" + frameWidth + ",height=" + frameHeight;
    try
    {
      if (window[target] != null)
        window[target].close();
    }
    catch (error){}
    var newWindow = window.open(url, frameProperties.targetName, winParams);
    if (target != null)
      window[target] = newWindow; 
    return false;
};
nbApi.hideFrame = function(popupId)
{
  if (popupId == null)
    return;
  var popup = document.getElementById(popupId + "_frame_popup");
  if (popup == null)
    return;  
  popup.style.display = "none";
  nbApi.removeFrame(popupId);
};
nbApi.removeFrame = function(popupId)
{
  if (popupId == null)
    return;
  var frame = document.getElementById(popupId + "_frame");
  if (frame == null)
    return;
  var container = frame.parentNode;
  if (container == null)
    return;
  container.removeChild(frame);
};
nbApi.getFrameProperties = function(popupId)
{
  if (popupId == null)
    return null;
  var popup = document.getElementById(popupId);  
  if (popup == null)
    return null;
  var properties = {
    target: popup.nextbioTarget,
    iFrame: popup.nextbioIFrame,
    top: popup.nextbioTop,
    left: popup.nextbioLeft,
    width: popup.nextbioWidth, 
    height: popup.nextbioHeight
  };
  return properties;
};

// importScript(url) - load javascript dynamically
// url - url of a javascript file
nbApi.importScript = function(url, iFrameId, targetId)
{
  var bAlert = nbApi.getDebugMode();
  if (url == null)
  {
    if (bAlert)
      alert("Nextbio API: importScript: No URL.");
    return;
  }
  var target = null;
  if (iFrameId != null)
  {
    var iFrame = document.getElementById(iFrameId);
    if (iFrame == null)
    {
      if (bAlert)
        alert("Nextbio API: importScript: iFrame with id '" + iFrameId + "' not found.");
      return;
    }
    var script = iFrame.contentWindow.document.createElement("script");
    script.type = "text/javascript";
    if (targetId)
      target = iFrame.contentWindow.document.getElementById(targetId);
    if (!target)
      target = iFrame.contentWindow.document.body;
    target.appendChild(script);
    script.src = url;
  }
  else 
  {
    var script = document.createElement("script");
    script.type = "text/javascript";
    if (targetId)
      target = document.getElementById(targetId);
    if (!target)
      target = document.body;
    target.appendChild(script);
    script.src = url;
  }
};

//================ extensions ==========
  nbApi.findParentByClassName = function(obj, className)
  {
    if (obj == null || className == null)
      return null;
    while (obj != null)
    {
      if (obj.nodeType == 1 && obj.className != null)
      {
        var names = obj.className.split(" ");
        for (var i = 0; i < names.length; i++)
        {
          if (nbApi.trim(names[i]) == className)
          {
            return obj;
          }
        }
      }  
      obj = obj.parentNode;
    }  
    return null;
  };

nbApi.findParentByTagName = function(obj, tagName)
{
  if (obj == null || tagName == null)
    return null;
  tagName = tagName.toLowerCase();
  obj = obj.parentNode;
  while (obj != null)
  {   
    var objTagName = nbApi.trim(obj.tagName);
    if (objTagName != null)
      objTagName = objTagName.toLowerCase();
    if (obj.nodeType == 1 && objTagName == tagName)
      return obj;  
    obj = obj.parentNode;
  }  
  return null;
};

nbApi.show = function (targetId, bShow)
{
  var element = document.getElementById(targetId);
  if (element != null)
    element.style.display = bShow?"":"none";
};

nbApi.findElementsByName = function(name, type)
{
  var group = document.getElementsByName(name);
  if ((group == null || group.length == 0) && type != null)
    group = document.getElementsByTagName(type);
  if (group == null || group.length == 0)
    return group;
  var out = new Array();
  var j = 0;            
  for (i = 0; i < group.length; i++)
  {
    var nameAttr = group[i].getAttribute("name");
    if (nameAttr == name)
    {
      out[j] = group[i];
      j++;
    }
  }
  return out;
};

nbApi.getViewportScrollX = function() {
  var scrollX = 0;
  if( document.documentElement && document.documentElement.scrollLeft ) {
    scrollX = document.documentElement.scrollLeft;
  }
  else if( document.body && document.body.scrollLeft ) {
    scrollX = document.body.scrollLeft;
  }
  else if( window.pageXOffset ) {
    scrollX = window.pageXOffset;
  }
  else if( window.scrollX ) {
    scrollX = window.scrollX;
  }
  return scrollX;
};

nbApi.getViewportScrollY = function() {
  var scrollY = 0;
  if( document.documentElement && document.documentElement.scrollTop ) {
    scrollY = document.documentElement.scrollTop;
  }
  else if( document.body && document.body.scrollTop ) {
    scrollY = document.body.scrollTop;
  }
  else if( window.pageYOffset ) {
    scrollY = window.pageYOffset;
  }
  else if( window.scrollY ) {
    scrollY = window.scrollY;
  }
  return scrollY;
};
nbApi.findPosition = function(obj) 
{
  var left = 0;
  var top = 0;
  if (obj.getBoundingClientRect) {
    try {
      var r = obj.getBoundingClientRect();
      left = r.left+nbApi.getViewportScrollX();
      top = r.top+nbApi.getViewportScrollY();
    } catch (e) { // DE5012.  If the ancestor of the element has had it's HTML overwritten, IE throws an exception
      top = left = -10000;
    }
  } else {
    var iter = obj;
    if (obj.offsetParent) 
    {
  	  do 
      {
  		left += obj.offsetLeft;
  		top += obj.offsetTop;
  	  } while (obj = obj.offsetParent);
    }
    else
    {
      left = obj.offsetLeft;
      top = obj.offsetTop;   
    }

	while (iter) 
  {
    if (iter.tagName && iter.tagName.toLowerCase() == "body")
    {
      break;
    }
  	if (iter.scrollLeft) 
    {
  		left -= iter.scrollLeft;
  	}
  	if (iter.scrollTop) 
    {
  		top -= iter.scrollTop;
  	}
  	iter = iter.parentNode;
	}
//   left = nbApi.getElementLeft(obj, true);
//   top = nbApi.getElementTop(obj, true);
  }
  return {left: left, top: top};
};

nbApi.createId = function(id) 
{
  var date1 = new Date();
  date1.setFullYear(2000,1,1);
  var date2 = new Date();
  id += date2.getTime() - date1.getTime();
  return id;
};

nbApi.loadTimeoutHandler = function(targetId, timeoutId, timeoutCallback)
{
  var bAlert = nbApi.getDebugMode();
  if (targetId == null)
  {
    if (bAlert)
      alert("Nextbio API: Timeout Handler: No target id.");
    return false;
  }
  if (timeoutId == null)
  {
    if (bAlert)
      alert("Nextbio API: Timeout Handler: No timeout id.");
    return false;
  }
  var target = document.getElementById(targetId);
  if (target == null)
  {
    if (bAlert)
      alert("Nextbio API: Timeout Handler: Target doesn't exist.");
    return false;
  }
  target.setAttribute(timeoutId, null);

  if (timeoutCallback == null)
  {
    return true;
  }
  return timeoutCallback(target);
};
/**
 * Determine the URL for a given search.  When calling from code that only runs within the nextbio
 * context, consider using searchUrl() instead.
 * @param root The root for the current contect e.g. "/b"
 * @param page The search page, e.g. "disc" or "lit"
 * @param searchTerm The term being searched for e.g. "esr1"
 * @param query the query arguments to appear after the ?, e.g. "id=1234&type=gene"
 * @return search URL
 */
nbApi.searchUrl = function(root,page,searchTerm,query, doNotEncode) 
{
  var encTerm = doNotEncode ? searchTerm : encodeURIComponent(searchTerm);
  // there is a problem on our server if the url contains an encoded % (%25) or / (%2F)
  // if the search term contains a %, included it as part of the q, otherwise
  // we include it as part of the URL
  if (searchTerm.indexOf('%') >= 0 || searchTerm.indexOf('/') >= 0 || searchTerm.indexOf('+') >= 0) {
      return root + "/search/" + page + ".nb?" + _nextbio_setParam(query,"q",encTerm);
  }
  if (query == null || query.length == 0) {
      query = "";
  } else {
      query = "?" + query;
  }
  return root + "/search/" + page + "/" + encTerm + query;
};
/**
 * Return the standard icon for a given type
 * @param type, type, e.g. "gene", "biogroup"
 * @param opaque if true, use IE6 opaque icons if available.  These icons will work on a white
 * background.  This should only be set if the use of the Png class doesn't prevent
 * blue backgrounds
 * @return base url for the icon
 */
nbApi.getIcon = function(type, root, bLabel, opaque) 
{
  if (type == null || root == null)
    return null;
  type = type.toLowerCase();

  if (bLabel == true)
    root += "/labels/";
  else
    root += "/icons/";  
    
  opaque = (opaque && navigator.userAgent.indexOf("MSIE 6") > 0)?"_opq":"";

  if (type == "gene" || type == "feature" || type == "genes") return root + "gene" + opaque + ".png";
  if (type == "tissue" || type == "tissues" || type == "body atlas") return root + "tissue" + opaque + ".png";
  if (type == "treatment" || type == "compound" || type == "treatments") return root + "treatment" + opaque + ".png";
  if (type == "disease" || type == "diseases") return root + "disease" + opaque + ".png";
  if (type == "bioset") return root + "bioset" + opaque + ".png";
  if (type == "biogroup" || type == "biogroups") return root + "biogroup" + opaque + ".png";
  if (type == "biosource") return root + "biosource.png";
  if (type == "biodesign") return root + "biodesign.png";
  if (type == "genemode") return root + "genemode.png";
  if (type == "snp") return root + "snp" + opaque + ".png";
  if (type == "sequence region") return root + "seqRegion" + opaque + ".png";
  if (type == "author") return root + "author" + opaque + ".png";
  if (type == "study") return root + "study" + opaque + ".png";
  if (type == "organism") return root + "organism" + opaque + ".png";
  return root + "unknown.png";
};
nbApi.setCookie = function(cookieName, value, time)
{
    var cookie=cookieName + "=" + escape(value) + ";path=/;";
    if (time != null)
    {
      var date=new Date();
      date.setFullYear(date.getFullYear() + time);
      cookie += ";expires=" + date.toGMTString();
    }
    document.cookie=cookie;
};
nbApi.getCookie = function(cookieName)
{
  if (document.cookie.length > 0)
  {
    cookieStart = document.cookie.indexOf(cookieName + "=");
    if (cookieStart != -1)
    { 
    cookieStart = cookieStart + cookieName.length + 1; 
    cookieEnd = document.cookie.indexOf(";", cookieStart);
    if (cookieEnd == -1) cookieEnd = document.cookie.length;
    return unescape(document.cookie.substring(cookieStart, cookieEnd));
    } 
  }
  return "";
};

nbApi.visibility = function(targetId, bShow)
{
  var element = document.getElementById(targetId);
  if (element != null)
    element.style.visibility = bShow?"visible":"hidden";
};

  nbApi.getElementsByClassName = function(el, clazz, tag)  {
    var a = [];
    var j;
    var re = new RegExp('\\b' + clazz + '\\b');
    var els = el.getElementsByTagName(tag||"*");
    for(var i=0,j=els.length; i < j; i++)
        if(re.test(els[i].className)) a.push(els[i]);   
    return a;
  }
  
/**
 * Give a parameter string (e.g. a=x&b=y), look up a parameter in it
 * @param str parameter string
 * @param param parameter to look up
 * @return parameter value
 */

nbApi.getParam = function(str, param)
{
  if (str == null)
    return null;
  var paramHead = param + "="; 
  var begin = str.indexOf(paramHead);
  if (begin < 0) // str doesn't have this param - return null
    return null;

  if (begin > 0) 
  {
    begin = str.indexOf("&" + paramHead); // make sure we found exact match
    if (begin < 0) // str doesn't have this param - return null
      return null;
    begin++; // skip "&"
  }
  var end = str.indexOf("&", begin);  
  if (end < 0) // this is the last param 
  {
    end = str.length;
  }
  var result = str.substring(begin + paramHead.length, end);
  return result;
};
// make sure that the element's position is within window bounds
nbApi.getAdjustedPosition = function(element, y, x, rightBound, bottomBound)
{
  if (element == null)                             
    return;
  var left = x;  
  var top = y - element.offsetHeight; // x, y are coords of bottom left corner  
  var bottom = bottomBound, right = rightBound;
  if (bottom == null)
    bottom = nbApi.getWindowHeight() - 10; 
  if (right == null)
    right = nbApi.getWindowWidth() - 10; 
  
  var deltaY = top;
  var deltaX = right - (left + element.offsetWidth);
  scrollTop = nbApi.getScrollTop();
  top += scrollTop; // to account for page scrolling  
  if (deltaY < 0)
  {
    top -=  deltaY; 
    if (top < scrollTop)
      top = scrollTop + 5; 
  }
  var newY = top + element.offsetHeight;

  scrollLeft = nbApi.getScrollLeft();
  left += scrollLeft; // to account for page scrolling  
  if (deltaX < 0)
  {
    left += deltaX;
    if (left < scrollLeft)
      left = scrollLeft;
  }
  return {x: left, y: newY};
};
nbApi.truncateWithEllipses = function(str, maxSize, terminator)
{
  if (str == null || maxSize == null)
    return null;
  if (terminator == null)
    terminator = "...";  
  var out = str.substr(0, maxSize);
  if (maxSize < str.length)
  {
    out += terminator;
  }
  return out; 
};
nbApi.cancelEvent = function(event)
{
	if (!event) 
    event = window.event;
  if (event)
  {
    event.cancelBubble = true;
  	if (event.stopPropagation)
    { 
      event.stopPropagation();
    }
  }
};

nbApi.addEvent = function(elem, evtType, func, capture) 
{
  capture = (capture) ? capture : false;
  if (elem.addEventListener) 
  {
    elem.addEventListener(evtType, func, capture);
    return true;
  } 
  if (elem.attachEvent) 
  {
    elem.attachEvent("on" + evtType, func);
    return true;
  } 
  return false;
}; 
nbApi.addEventHandler = function(element, eventName, handler) 
{
  nbApi.removeEvent(element, eventName, handler, false)
  var rc = nbApi.addEvent(element, eventName, handler, false);
  if (rc) return;
  eventName = "on" + eventName;  
  var chain = (element[eventName]) ? element[eventName] : function(){};
  element[eventName] = function(event) 
  {
    chain(event);
    handler(event);
  }
};
nbApi.removeEvent = function(elem, evtType, handler, capture) 
{
  capture = (capture) ? capture : false;
  if (elem.removeEventListener) 
  {
    elem.removeEventListener(evtType, handler, capture);
  } 
  else if (elem.attachEvent) 
  {
    elem.detachEvent("on" + evtType, handler);
  } 
  else 
  {
    elem["on" + evtType] = null;
  }
};
nbApi.showSection = function(targetId, bShow)
{
  var element = document.getElementById(targetId);
  if (element != null)
    element.style.display = bShow?"block":"none";
};
nbApi.updateSNPLink = function(obj) 
{
	var popup = nbApi.findParentByClassName(obj, "nbApiExPopup");
	if (popup && obj){
		var it = popup.getAttribute("nbItemTitle");
		var url = obj.getAttribute("url");
		if (url && it){
			var urlAry = [];
			urlAry = url.split('?');
			if (urlAry.length < 2)
				url = url + '?' + it;
			else
				url = url + '&' + it;
			obj.setAttribute("href",url); 
			return true;
		}
	}
	
	return false;
};


nbApi.whichElement = function(e)
{
  var target;
  if (!e) var e = window.event;
  if (e.target) target = e.target;
  else if (e.srcElement) target = e.srcElement;
  if (target.nodeType == 3) // work around Safari bug
  target = target.parentNode;
  return target;
}

nbApi.loadCss = function(url) {
  if(document.createStyleSheet) {
    document.createStyleSheet(url);
  } else {
    var styles = "@import url('"+url+"');";
    var newSS=document.createElement('link');
    newSS.rel='stylesheet';
    newSS.href='data:text/css,'+escape(styles);
    document.getElementsByTagName("head")[0].appendChild(newSS);
  }
}

nbApi.sendFeedback = function(config)
{
  var target = "nextbio_feedback";
  if (config && config.target)
    target = config.target;                                                                     
  var url = nbApi.urlContext + "/corp/feedback.nb?url=" + encodeURIComponent(window.location.href);  
  var winParams = "resizable=yes,scrollbars=yes,status=yes,toolbar=yes,location=yes,menubar=yes";
  try
  {
    if (window[target] != null)
      window[target].close();
  }
  catch (error){}
  var newWindow = window.open(url, target, winParams);
  if (target)
    window[target] = newWindow; 
  return false;
}
nbApi.pseudoUid = function()
{
  //make a pseudo unique id based on milliseconds since the beginning of 2010 + random number
  var date = new Date();
  var time = date.getTime();
  var baseTime = Math.round(time / 100000) * 100000;
  var lValue = (time - baseTime);
  var rand =  Math.random();
  var lRand = Math.round(rand * 100000);
  var out = lRand + "-" + lValue;
  return out;
}
nbApi.isHidden = function(obj)
{
  //return true if the object or any of the parents are hidden
  var next = obj;
  while (next)
  {
    if (next && next.style)
    {
      if (next.style.display == "none")
      {
        return true;
      }
    }
    next = next.parentNode;
  }
  return false;
}

nbApi.pageSize = function()
{
  var pageWidth = 0;
  var pageHeight = 0;
  if( window.innerHeight && window.scrollMaxY ) // Firefox 
  {
    pageWidth = window.innerWidth + window.scrollMaxX;
    pageHeight = window.innerHeight + window.scrollMaxY;
  }
  else if( document.body.scrollHeight > document.body.offsetHeight ) // all but Explorer Mac
  {
    pageWidth = document.body.scrollWidth;
    pageHeight = document.body.scrollHeight;
  }
  else // works in Explorer 6 Strict, Mozilla (not FF) and Safari
  { 
    pageWidth = document.body.offsetWidth + document.body.offsetLeft; 
    pageHeight = document.body.offsetHeight + document.body.offsetTop; 
  }
  return {width: pageWidth, height: pageHeight};
}  
/**
 *  Expand/collapse sections defined by config.id
 *  {id: 'section-,link-', imgId: this.id}
 *  config.id - comma separated list of ids of sections
 *  imgId - id of expand icon 
 */ 
nbApi.expandSection = function(config)
{
  if (!config || ! config.id)
    return;
  var img;
  if (config.imgId)
    img = document.getElementById(config.imgId);
  var sectionList = config.id.split(",");
  for (var i = 0; i < sectionList.length; i++)
  {
    var section = document.getElementById(sectionList[i]);
    if (section && section.style.display == "none") //expand
    {
      section.style.display = ""; 
      if (img)
      {
        var collapse = img.getAttribute("nbCollapse");
        if (collapse)
        {
          img.src = collapse;
        }
        else
        {
          img.src = nb.img + "/framed/collapse.png";
        }
      }
    }
    else
    {
      if (!config.forceExpand)
      {
        if (section)
          section.style.display = "none"; 
        if (img)
        {
          var expand = img.getAttribute("nbExpand");
          if (expand)
          {
            img.src = expand;
          }
          else
          {
            img.src = nb.img + "/framed/expand.png";
          }
        }
      }
    }
  }
}

  
    
// NextBio autocomplete widget support 
// requires jsApi.jsp, jsUtils.jsp

// preload images
nbApi.imgPath = nbApi.urlContext + "/s/img3";
nbApi.images.biogroup = new Image();
nbApi.images.biogroup.src = nbApi.getIcon("biogroup", nbApi.imgPath, true);

nbApi.images.compound = new Image();
nbApi.images.compound.src = nbApi.getIcon("compound", nbApi.imgPath, true);

nbApi.images.disease = new Image();
nbApi.images.disease.src = nbApi.getIcon("disease", nbApi.imgPath, true);

nbApi.images.gene = new Image();
nbApi.images.gene.src = nbApi.getIcon("gene", nbApi.imgPath, true);

nbApi.images.tissue = new Image();
nbApi.images.tissue.src = nbApi.getIcon("tissue", nbApi.imgPath, true);

nbApi.images.biosource = new Image();
nbApi.images.biosource.src = nbApi.getIcon("biosource", nbApi.imgPath, true);

nbApi.images.biodesign = new Image();
nbApi.images.biodesign.src = nbApi.getIcon("biodesign", nbApi.imgPath, true);

nbApi.images.genemode = new Image();
nbApi.images.genemode.src = nbApi.getIcon("genemode", nbApi.imgPath, true);

nbApi.images.snp = new Image();
nbApi.images.snp.src = nbApi.getIcon("snp", nbApi.imgPath, true);

nbApi.images.author = new Image();
nbApi.images.author.src = nbApi.getIcon("author", nbApi.imgPath, true);

nbApi.images.organism = new Image();
nbApi.images.organism.src = nbApi.getIcon("organism", nbApi.imgPath, true);
  
nbApi.images.unknown = new Image();
nbApi.images.unknown.src = nbApi.getIcon("unknown", nbApi.imgPath, true);
  
nbApi.getAutocompleteConfig = function()
{        
  var config = {
    callback: function(targetId, bLoaded, json){
      var target = document.getElementById(targetId); 
      target.style.display = "inline";
    },
    message: null,
    searchBox: null,
    searchBoxId: null,
    targetId: null, //drop list id
    target: null,   // drop list
    numResults: null, // number of results to request from server
    getItemsDelay: 250,
    defaultText: "Search...",
    defaultTextClass: "",  // class to set when showing defaultText
    serviceUrl: nbApi.urlContext + "/autocomplete/autocomplete.nbs",
    header: null,   // header to display on top of the drop list
    headerClassName: null,   // CSS class to display header
    headerStyle: null,   // style to display header
    footer: null,   // footer to display at the bottom of the drop list
    footerClassName: null,   // CSS class to display footer
    footerStyle: null,   // style to display footer
    pageId: "ov", // default page id for onclick url
    focus: false, // if true, set focus to input after creating it
    className: "nbApiAutocompleteList", // class for the drop list
    callbackName: encodeURIComponent("nbApi.updateTargetHTML"),
    onKeyup: function(event){return nbApi.autocompleteOnKeyUp(event);},
    onKeydown: function(event){return nbApi.autocompleteOnKeyDown(event);},
    onClick: function(owner, cfg){return nbApi.autocompleteOnClickHandler(owner, cfg);},
    onBlur: function(event){return nbApi.autocompleteOnBlur(event);},
    onFocus: function(event){return nbApi.autocompleteOnFocus(event);},
    itemType: null, // name of type of item (e.g. "GENE"), or array of types (e.g. ["GENE","BIOGROUP"]) to lookup
    addedQuery: null, // additional arguments to be added to the query
    // parameters prefixed with nb are intended for internal NextBio use only, not for
    // the public API
    nbMessageId: null, // Use this to look up a message on the server for the bottom of the autocomplete
    topOffset: 0
  };     
  return config;
};
nbApi.autocompleteGetItems = function(owner, cfg, bNow)
{ 
  if (owner == null || cfg == null)
  {
    return true;
  }
  if (bNow != true)
  {
    if (cfg != null && cfg.targetId != null)
    {
      var target = document.getElementById(cfg.targetId);
      if (target != null)
        target.setAttribute("nextbio_allowUpdate", "true");
    }
    if (cfg.getItemsTimeout != null)
    {
      clearTimeout(cfg.getItemsTimeout);
      cfg.getItemsTimeout = null;
    }
    cfg.getItemsTimeout = setTimeout(function(){nbApi.autocompleteGetItems(owner, cfg, true)}, cfg.getItemsDelay);  
    return true;
  }
  cfg.getItemsTimeout = null;
  var query = nbApi.normalizeString(owner.value);
  if (query == null || query.length == 0)
    return;
  if (query == cfg.query)
  {
    nbApi.autocompleteShowList(cfg, true);
    return;
  }
  cfg.query = query;  
  query = "query=" + encodeURIComponent(query);
  query += "&apiKey=" + nbApi.apiKey;
  query += "&nameSpace=" +  "nbApi";
  query += "&boxId=" +  owner.id;
  if (cfg.nbMessageId) query += "&messageId="+cfg.nbMessageId;
  if (cfg.itemType) {
    if (typeof cfg.itemType == 'object') { // array
      for (var i=0; i<cfg.itemType.length; i++) {
        query += "&itemType=" + cfg.itemType[i];
      }
    } else {
      query += "&itemType="+cfg.itemType;
    }
  }
  if (cfg.numResults) {
    query += "&numResults=" + cfg.numResults;
  }
  if (cfg.addedQuery) {
    query += "&" + cfg.addedQuery;
  }
  var config = 
  {
    target: cfg.targetId,
    callbackName: cfg.callbackName,
    callback: function(target, bLoaded, json){nbApi.autocompleteShowList(cfg, json.response && json.response.length > 0)},
    url: cfg.serviceUrl,
    query: query,
    message: cfg.message,
    messageStyle: cfg.messageStyle
  };  
  cfg.currentItem = null;
  nbApi.loadScript(config);  
  return true;
};
// create autocomplete box base on configuration object
// if existing boxId is supplied - use existing input control
// if targetId is supplied - create a box inside the target div 
nbApi.attachAutocomplete = function(config)
{   
  var bAlert = nbApi.getDebugMode();
  var searchBox = null;     
  if (!config.autoUid)
  {
    config.autoUid = nbApi.pseudoUid();  
    window.nbScrollTimeout = {};
    window.nbHideTimeout = {};
  }
  // add target div here
  if (config.searchBoxId == null)
  {
    if (bAlert == true)
      alert("Nextbio Autocomplete: search box id not specified.");
    return;    
  }
  if (!config.indent)
    config.indent = 0;  
  // if search box exists - convert it to autocomplete
  searchBox = document.getElementById(config.searchBoxId);
  if (searchBox == null)
  {
    if (bAlert == true)
      alert("Nextbio Autocomplete: search box not found.");
    return;
  }
  var dropListId = config.searchBoxId + "_drop_list";
  config.searchBox = searchBox;
  config.targetId = dropListId;
  searchBox.nextbioConfig = config;
  searchBox.setAttribute("autocomplete", "off");
  config.onKeyupChain = searchBox.onkeyup;
  searchBox.onkeyup = config.onKeyup;
  config.onKeydownChain = searchBox.onkeydown;
  searchBox.onkeydown = config.onKeydown;
  config.onBlurChain = searchBox.onblur;
  searchBox.onblur = config.onBlur;
  config.onFocusChain = searchBox.onfocus;
  searchBox.onfocus = config.onFocus;
  config.onClickChain = searchBox.onclick;
  searchBox.onclick = config.onClick;
  var value = nbApi.normalizeString(searchBox.value);
  if (value == null || value.length == 0)
  {
    searchBox.value = config.defaultText;
  }
  // if an element with the id of the drop list has already been created,
  // delete it.  This can occur, if a previous autocomplete has been created
  // for an input with the same ID, and then the input has been deleted
  var oldAc = document.getElementById(dropListId);
  if (oldAc && oldAc.parentNode) {
    oldAc.parentNode.removeChild(oldAc);
  }
  
  // add div for drop list
  var target = document.createElement("div");
  config.target = target;
  target.id = dropListId;
  target.style.display = "none";
  target.style.position = "absolute";
  target.className = "nbApiAutocompleteList";
  var position = nbApi.findPosition(searchBox);
  target.style.width = (searchBox.offsetWidth - config.indent) + "px";
  target.style.left = (position.left + config.indent) + "px";
  var top = position.top + searchBox.offsetHeight + (config.topOffset||0);
  target.style.top = top + "px";
  target.style.zIndex = "1000";
  target.nextbioTargetClassName = config.className;
  target.nextbioAutocompleteConfig = config;
  document.body.appendChild(target);
  config.onClickBodyChain = document.body.onclick;
  document.body.onclick = function(event){nbApi.autocompleteOnClickBody(event, config)};
  config.onKeyPressBodyChain = document.body.onkeypress;
  document.body.onkeypress = function(event){nbApi.autocompleteOnKeyPressBody(event, config)};
  config.onClickWindowChain = window.onclick;
  window.onclick = function(event){nbApi.autocompleteOnClickWindow(event, config)};
  config.onKeyPressWindowChain = window.onkeypress;
  window.onkeypress = function(event){nbApi.autocompleteOnKeyPressWindow(event, config)};
  config.onResizeWindowChain = window.onresize;
  window.onresize = function(event){nbApi.autocompleteOnResizeWindow(event, config)};

  if (config.focus) {
    searchBox.focus();
  }
};

// default onclick action for autocomplete drop list
// prepare a configuration object and call an onClick method provided in autocomplete configuration object.
nbApi.autocompleteOnClick = function(owner)
{ 
  if (owner == null)
    return false;
  var config = nbApi.autocompleteFindConfig(owner);
  if (config == null || config.onClick == null)
    return false;
  var cfg = {};
  cfg.uid = owner.getAttribute("nextbioUid"); 
  cfg.searchBoxId = config.searchBoxId; 
  cfg.searchBox = config.searchBox; 
  cfg.targetId = config.targetId;
  cfg.target = config.target;
  cfg.id = owner.getAttribute("nextbioId"); 
  cfg.name = owner.getAttribute("nextbioName"); 
  cfg.symbol = owner.getAttribute("nextbioSymbol"); 
  cfg.alias = owner.getAttribute("nextbioAlias"); 
  cfg.synonym = owner.getAttribute("nextbioSynonym");
  cfg.type = owner.getAttribute("nextbioType");
  cfg.pageId = config.pageId;
  return config.onClick(owner, cfg);
};
nbApi.autocompleteOnClickHandler = function(owner, config)
{ 
  if (owner == null || config == null)
    return false;

  var page = config.pageId;
  var type = config.type;
  if (config.searchBox != null)
  {
    config.searchBox.value = config.symbol;
  }
  if (type != null && type.toLowerCase() == "author")
  {
    window.location = nbApi.urlContext + "/search/author/" + encodeURIComponent(config.symbol); 
  }
  else
  {
    if (config.id == null || config.type == null || config.type == "text")
    {
      window.location = nbApi.searchUrl(nbApi.urlContext,"ov",config.symbol);
    }
    else
    {
      window.location = nbApi.searchUrl(nbApi.urlContext,page,config.symbol,
        "id=" + encodeURIComponent(config.id) + 
        "&type=" + config.type +
        "&synonym=" + encodeURIComponent(config.synonym));
    }
  }
  nbApi.autocompleteShowList(config, false);
  nbApi.autocompleteDeselectItem(config.currentItem, true);
  return true;
};
// default mouseover action for autocomplete drop list
nbApi.autocompleteOnMouseOver = function(owner)
{   
  if (window.nbMouseOverIgnore)
  {
    window.nbMouseOverIgnore = false;
    return;
  }
  nbApi.autocompleteSelectItem(owner);
};
// default mouseout action for autocomplete drop list
nbApi.autocompleteOnMouseOut = function(owner)
{   
  nbApi.autocompleteDeselectItem(owner);
};
// select drop list item
nbApi.autocompleteSelectItem = function(owner, force, expanderId)
{   
  var config = nbApi.autocompleteFindConfig(owner);
  if (config == null || config.lock && force != true)
    return;
  if (force != true)
    config.lock = true;  

  var items = nbApi.findElementsByName(config.searchBoxId + "_autocomplete_item", "tr");
  var i = 0;
  for (i = 0; i < items.length; i++)
  {
    nbApi.removeClass(items[i], 'nbApiAutocompleteSelectedItem');
  }  
  nbApi.addClass(owner, 'nbApiAutocompleteSelectedItem');
  config.currentItem = owner;  
  if (force != true)
    config.lock = false;  
  
  if (expanderId)
  {
    window.nbMouseOverIgnore = true;
    nbApi.expandSection({id: expanderId + '-children', imgId: expanderId + '-expand', forceExpand: true});  
  }  
};
// select next drop list item
nbApi.autocompleteSelectNextItem = function(config)
{   
  if (config == null || config.lock)
    return;
  config.lock = true;  
  var currentItem = config.currentItem;  
  var items = nbApi.findElementsByName(config.searchBoxId + "_autocomplete_item", "tr");
  var index = -1;
  var i = 0;

  for (i = 0; i < items.length; i++)
  {
    if (items[i] === currentItem)
    {
      index = i;
      break;
    }
  }  
  index++;
  var nextItem = null;
  if (index >= items.length)
    index = items.length - 1;
  nextItem = items[index];
//  nbApi.autocompleteDeselectItem(config.currentItem);
  var expanderId = null;
  if (currentItem)
  {
    expanderId = currentItem.getAttribute("nextbioBoxedId");
  }
  nbApi.autocompleteSelectItem(nextItem, true, expanderId);
  config.lock = false;  
};

// select previous drop list item
nbApi.autocompleteSelectPrevItem = function(config)
{   
  if (config == null || config.lock)
    return;
  config.lock = true;  
  var currentItem = config.currentItem;  
  var items = nbApi.findElementsByName(config.searchBoxId + "_autocomplete_item", "tr");
  var index = items.length;
  var i = 0;
  for (i = 0; i < items.length; i++)
  {
    if (items[i] === currentItem)
    {
      index = i;
      break;
    }
  }
  var currentIindex = index;  
  index--;
  var prevItem = null;
  if (index < 0)
    index = 0;
  prevItem = items[index];

  var expanderId = null;
  for (i = index; i >= 0; i--)
  {
    expanderId = items[i].getAttribute("nextbioBoxedId");
    if (expanderId)
    {
      window.nbMouseOverIgnore = true;
      nbApi.expandSection({id: expanderId + '-children', imgId: expanderId + '-expand', forceExpand: true});  
      break;
    }
  }

  nbApi.autocompleteSelectItem(prevItem, true);
  config.lock = false;  
};
// deselect drop list item
nbApi.autocompleteDeselectItem = function(owner, force)
{   
  nbApi.removeClass(owner, 'nbApiAutocompleteSelectedItem');
  var config = nbApi.autocompleteFindConfig(owner);
  if (config == null)
    return;
  if (force == true || config.currentItem == owner)
    config.currentItem = null;  
};

nbApi.autocompleteOnKeyUp = function(event)
{ 
  if (event == null)
  { 
    event = window.event;
  }
  var owner = event.target;
  if (owner == null)
    owner = event.srcElement;
  if (owner == null)
  {
    return true;
  }
  var config = owner.nextbioConfig;  
  var key = null;
  if (event.keyCode != null)
    key = event.keyCode;
  else if (event.which != null)
    key = event.which;

  if (key == null)
    return true;

  var rc = true;
  var query = nbApi.normalizeString(owner.value);
  var empty = (query == null || query.length == 0);
  if (empty || key == 27) 
  {
    if (config != null && config.targetId != null)
    {
      nbApi.autocompleteShowList(config, false);
      nbApi.autocompleteDeselectItem(config.currentItem, true);
    }
    return true;
  }
  else if (key == 13) 
  {
    if (config != null && config.targetId != null)
    {
      var target = document.getElementById(config.targetId);
      if (target != null)
        target.setAttribute("nextbio_allowUpdate", "false");
      if (config.currentItem != null)
      {
        nbApi.autocompleteOnClick(config.currentItem);
        return false;
      }
      nbApi.autocompleteShowList(config, false);
    }
    return true;
  }
  else if (config != null && config.target != null && config.target.nextbioIsLoaded == true)
  {
    if (key == 40) // down arrow
    {                                                         
      nbApi.autocompleteShowList(config, true);
      nbApi.autocompleteSelectNextItem(config);
      return true; 
    }
    if (key == 38) // up arrow
    {
      nbApi.autocompleteShowList(config, true);
      nbApi.autocompleteSelectPrevItem(config);
      return true;
    }
  }
  if (key != 37 && key != 39) // left and right arrows
  {
    rc = nbApi.autocompleteGetItems(owner, config);
  }
  if (config.onKeyupChain != null)
    return config.onKeyupChain(event);
  return rc;
};

nbApi.autocompleteOnKeyDown = function(event)
{ 
  if (event == null)
  { 
    event = window.event;
  }
  var owner = event.target;
  if (owner == null)
    owner = event.srcElement;
  if (owner == null)
  {
    return true;
  }
  var key = null;
  if (event.keyCode != null)
    key = event.keyCode;
  else if (event.which != null)
    key = event.which;

  var config = owner.nextbioConfig;
  var value = nbApi.normalizeString(owner.value);
  if (value == config.defaultText)
  {
    owner.value = "";
  }  
  if (key == 13) // enter
  {
    if (config != null && config.targetId != null)
    {
      var target = document.getElementById(config.targetId);
      if (target != null)
        target.setAttribute("nextbio_allowUpdate", "false");
      
      if (config.currentItem != null)   // IE doesn't respond to KeyUp
      {
//        nbApi.autocompleteOnClick(config.currentItem);
        return false;
      }
      nbApi.autocompleteShowList(config, false);
    }
    return true;
  }
  if (config.onKeydownChain != null)
    return config.onKeydownChain(event);
  return true;

};

// find autocomplete config object for any object within drop list
nbApi.autocompleteFindConfig = function(owner)
{ 
  if (owner == null)
    return false;
  var parent = nbApi.findParentByTagName(owner, "div");  
  var config = null;  
  while (parent != null)
  {
    config = parent.nextbioAutocompleteConfig;
    if (config != null)
      break;
    parent = nbApi.findParentByTagName(parent, "div");
  }  
  return config;
};

nbApi.autocompleteOnClickBody = function(event, config)
{
  nbApi.autocompleteShowList(config, false);
  nbApi.autocompleteDeselectItem(config.currentItem, true);
  if (config != null && config.onClickBodyChain != null)
    return config.onClickBodyChain(event);
  return true;
};
nbApi.autocompleteOnClickWindow = function(event, config)
{
  nbApi.autocompleteShowList(config, false);
  nbApi.autocompleteDeselectItem(config.currentItem, true);
  if (config != null && config.onClickWindowChain != null)
    return config.onClickWindowChain(event);
  return true;
};
nbApi.autocompleteOnKeyPressBody = function(event, config)
{
  if (event == null)
  { 
    event = window.event;
  }
  var key = null;
  if (event.keyCode != null)
    key = event.keyCode;
  else if (event.which != null)
    key = event.which;
  if (key == 27)  // escape key
  {
    nbApi.autocompleteShowList(config, false);
    nbApi.autocompleteDeselectItem(config.currentItem, true);
  }
  if (config != null && config.onKeyPressBodyChain != null)
    return config.onKeyPressBodyChain(event);
  return true;
};
nbApi.autocompleteOnKeyPressWindow = function(event, config)
{
  if (event == null)
  { 
    event = window.event;
  }
  var key = null;
  if (event.keyCode != null)
    key = event.keyCode;
  else if (event.which != null)
    key = event.which;
  if (key == 27)  // escape key
  {
    nbApi.autocompleteShowList(config, false);
    nbApi.autocompleteDeselectItem(config.currentItem, true);
  }
  if (config != null && config.onKeyPressWindowChain != null)
    return config.onKeyPressWindowChain(event);
  return true;
};

nbApi.autocompleteOnResizeWindow = function(event, config)
{
  if (event == null)
  { 
    event = window.event;
  }
  if (config == null)
  {
    return;
  }
  if (config.target == null || config.searchBox == null)
  {
    if (config.onResizeWindowChain != null)
      return config.onResizeWindowChain(event);
    return true;
  }
  if (!config.indent)
    config.indent = 0;  
  var position = nbApi.findPosition(config.searchBox);
  config.target.style.width = (config.searchBox.offsetWidth - config.indent) + "px";
  config.target.style.left = (position.left + config.indent) + "px";
  var top = position.top + config.searchBox.offsetHeight + (config.topOffset||0);
  config.target.style.top = top + "px";
  if (config.onResizeWindowChain != null)
    return config.onResizeWindowChain(event);
  return true;
};

nbApi.autocompleteOnScrollWindow = function(config)
{
  if (config == null)
  {     
    return;
  }
  if (!config.indent)
    config.indent = 0;  
  var position = nbApi.findPosition(config.searchBox);
  config.target.style.width = (config.searchBox.offsetWidth - config.indent) + "px";
  config.target.style.left = (position.left + config.indent) + "px";
  var top = position.top + config.searchBox.offsetHeight + (config.topOffset||0);
  config.target.style.top = top + "px";
  window.nbScrollTimeout[config.autoUid] = setTimeout(function(){nbApi.autocompleteOnScrollWindow(config)}, 200);  
  return true;
};
nbApi.autocompleteOnHideBox = function(config)
{
  if (config == null || !window.nbHideTimeout[config.autoUid])
  {
    return;
  }
  clearTimeout(window.nbHideTimeout[config.autoUid]);
  window.nbHideTimeout[config.autoUid] = null;
  if (nbApi.isHidden(config.searchBox))
  {
    nbApi.autocompleteShowList(config, false);
    return;
  }
   
  window.nbHideTimeout[config.autoUid] = setTimeout(function(){nbApi.autocompleteOnHideBox(config)}, 200);  
  return true;
};

nbApi.autocompleteOnBlur = function(event)
{
  if (event == null)
  { 
    event = window.event;
  }
  var owner = event.target;
  if (owner == null)
    owner = event.srcElement;
  if (owner == null)
  {
    return true;
  }
  var config = owner.nextbioConfig;
  var value = nbApi.normalizeString(owner.value);
  if (value == null || value.length == 0)
  {
    owner.value = config.defaultText;
    if (config.defaultTextClass) {
      nbApi.addClass(owner, config.defaultTextClass);
    }
  }  
  if (config.onBlurChain != null)
    return config.onBlurChain(event);
  return true;
};  

nbApi.autocompleteOnFocus = function(event)
{
  if (event == null)
  { 
    event = window.event;
  }
  var owner = event.target;
  if (owner == null)
    owner = event.srcElement;
  if (owner == null)
  {
    return true;
  }
  var config = owner.nextbioConfig;
  var value = nbApi.normalizeString(owner.value);
  if (value == config.defaultText)
  {
    owner.value = "";
    if (config.defaultTextClass) {
      nbApi.removeClass(owner, config.defaultTextClass);
    }
  }  
  if (config.onFocusChain != null && config.onFocusChain != nbApi.autocompleteOnFocus)
    return config.onFocusChain(event);
  return true;
};  

nbApi.autocompleteShowList = function(config, bShow)
{
  if (config == null || config.target == null || config.searchBox == null)
    return;
  if (!config.indent)
    config.indent = 0;  
  var position = nbApi.findPosition(config.searchBox);
  var width = (config.searchBox.offsetWidth - config.indent);
  config.target.style.width = width + "px";
  config.target.style.left = (position.left + config.indent) + "px";
  var top = position.top + config.searchBox.offsetHeight + (config.topOffset||0);
  config.target.style.top = top + "px";
  if (width == 0 || nbApi.isHidden(config.searchBox))
  {
    bShow = false;
  }
  nbApi.show(config.targetId, bShow);
  if (window.nbScrollTimeout[config.autoUid])
  {
    clearTimeout(window.nbScrollTimeout[config.autoUid]);
    window.nbScrollTimeout[config.autoUid] = null;
  }
  if (window.nbHideTimeout[config.autoUid])
  {
    clearTimeout(window.nbHideTimeout[config.autoUid]);
    window.nbHideTimeout[config.autoUid] = null;
  }
  if (bShow)
  {
    window.nbScrollTimeout[config.autoUid] = setTimeout(function(){nbApi.autocompleteOnScrollWindow(config)}, 200);  
    window.nbHideTimeout[config.autoUid] = setTimeout(function(){nbApi.autocompleteOnHideBox(config)}, 200);  
  }
};

nbApi.createAutocomplete = function(cfg, debugMode)
{
  nbApi.setDebugMode(debugMode == true);
  var config = nbApi.getAutocompleteConfig();
  var idx;
  for (idx in cfg)
  {
    config[idx] = cfg[idx];
  }
  nbApi.attachAutocomplete(config);
  return config;
};
nbApi.submitSearch = function(config)
{
  if (!config)
    return false;
  var searchBox = document.getElementById(config.searchBoxId);
  if (!searchBox)
    return false;
  var v = nbApi.normalizeString(searchBox.value);
  if (v && v != config.defaultText) {
    var loc = nbApi.searchUrl(nbApi.urlContext,"ov",searchBox.value); 
    window.location = loc;
  }
  return false;  // prevent the form from submitting itself
}

  
    


nbApi.highlightSteps = 25;
nbApi.highlightColor = "#93B6E0";
nbApi.highlightDuration = 1000;

  
  nbApi.highlight1 = function(el, step, target, startTime) 
  { 
        if (el == null)
        {
          return;
        }
        if (step == null || step < 1)  
          step = 0;

        if (target == null)
        {
          target = document.createElement("div");
          target.className = "nbApiHighlightTarget";
          target.style.display = "none";
          target.style.position = "absolute";
          document.body.appendChild(target);
          target.style.background = nbApi.highlightColor;
          target.style.display = "inline";
        }
        var position = nbApi.findPosition(el);
        target.style.top = position.top + "px";
        target.style.left = position.left + "px";
        target.style.width = el.offsetWidth + "px";
        target.style.height = el.offsetHeight + "px";
  
        var date = new Date();
        var time = 0; 
        var timeLeft = 0;
        if (startTime == null)
        {
          startTime = date;
        }
        timeLeft = nbApi.highlightDuration - (date.getTime() - startTime.getTime());
        if (timeLeft < 0)
          timeLeft = 0;
        var stepTime = nbApi.highlightDuration / nbApi.highlightSteps;
        var stepsLeft = timeLeft / stepTime;

        var opacity = stepsLeft / nbApi.highlightSteps;
        if (opacity != 0)
        {
          target.style.opacity = new Number(opacity).toFixed(2).toString();
          target.style["-ms-filter"] = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + new Number(opacity * 100).toFixed(0).toString() + ")";
          target.style.filter = "alpha(opacity=" + new Number(opacity * 100).toFixed(0).toString() + ")";
        }

        if (timeLeft < 1 || stepsLeft < 1)
        {
          if (target != null)
            document.body.removeChild(target);
          return;
        }
        var nextStep = step + 1;  
        var timeout = setTimeout(function(){nbApi.highlight1(el, nextStep, target, startTime)}, stepTime);
    
  };
  nbApi.highlight = function(el) 
  { 
    
  }
  
  
  nbApi.rgb2json = function(rgb)
  {
    if (rgb == null || rgb.length == 0 || rgb.indexOf("#") != 0)
      return {r: 0, g: 0, b: 0};
    var rStr, gStr, bStr;
    rStr = gStr = bStr ="00";
    rStr = rgb.substr(1, 2); 
    if (rgb.length > 3)  
      gStr = rgb.substr(3, 2);
    if (rgb.length > 5)  
      bStr = rgb.substr(5, 2);
    
    var json = {r: parseInt(rStr, 16), g: parseInt(gStr, 16), b: parseInt(bStr, 16)};
    return json;  
  }

  
  nbApi.rgb2hsl = function(R, G, B)
  {  
    var H, S, L;
    var var_R = ( R / 255 );                     //RGB from 0 to 255
    var var_G = ( G / 255 );
    var var_B = ( B / 255 );
    
    var var_Min = Math.min( var_R, var_G, var_B );    //Min. value of RGB
    var var_Max = Math.max( var_R, var_G, var_B );    //Max. value of RGB
    var del_Max = var_Max - var_Min;             //Delta RGB value
    
    L = ( var_Max + var_Min ) / 2 ;
    if ( del_Max == 0 )                     //This is a gray, no chroma...
    {
       H = 0;                                //HSL results from 0 to 1
       S = 0;
    }
    else                                    //Chromatic data...
    {
      if ( L < 0.5 ) 
        S = del_Max / ( var_Max + var_Min );
      else           
        S = del_Max / ( 2 - var_Max - var_Min );
      
      var del_R = ( ( ( var_Max - var_R ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
      var del_G = ( ( ( var_Max - var_G ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
      var del_B = ( ( ( var_Max - var_B ) / 6 ) + ( del_Max / 2 ) ) / del_Max;
      
      if ( var_R == var_Max ) 
        H = del_B - del_G;
      else if ( var_G == var_Max ) 
        H = ( 1 / 3 ) + del_R - del_B;
      else if ( var_B == var_Max ) 
        H = ( 2 / 3 ) + del_G - del_R;
      
      if ( H < 0 ) 
        H += 1;
      if ( H > 1 )
        H -= 1;
    }  
    return {h: H, s: S, l: L}
  }  
  
  
  nbApi.hsl2rgb = function(H, S, L)
  {
    var R, G, B, var_1, var_2;
    if ( S == 0 )                       //HSL from 0 to 1
    {
       R = L * 255;                      //RGB results from 0 to 255
       G = L * 255;
       B = L * 255;
    }
    else
    {
      if ( L < 0.5 ) 
        var_2 = L * ( 1 + S );
      else           
        var_2 = ( L + S ) - ( S * L );
      
      var_1 = 2 * L - var_2;
      
      R = 255 * nbApi.Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) );
      G = 255 * nbApi.Hue_2_RGB( var_1, var_2, H );
      B = 255 * nbApi.Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) );
    }
    return {r: new Number(R).toFixed(0), g: new Number(G).toFixed(0), b: new Number(B).toFixed(0)}; 
  }

  
  nbApi.Hue_2_RGB = function( v1, v2, vH )             //Function Hue_2_RGB
  {
    if ( vH < 0 ) 
      vH += 1;
    if ( vH > 1 ) 
      vH -= 1;
    if ( ( 6 * vH ) < 1 ) 
      return v1 + ( v2 - v1 ) * 6 * vH;
    if ( ( 2 * vH ) < 1 ) 
      return v2;
    if ( ( 3 * vH ) < 2 ) 
      return v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6;
    return v1;
  }


  
    // Functions related to popup windows.  
nbApi.nextbio_popupId; // visible popup id
nbApi.popupMisc = {};
nbApi.popupMisc.FAKE_ID = 1000000000;
nbApi.popupEvenHandlers = {};
nbApi.popupEvenHandlers.onmouseover = function(event){nbApi.cancelTimeout();};
nbApi.popupEvenHandlers.onclick = function(event)
{
  nbApi.popupEvenHandlers.stopMove(event);
  nbApi.UnTip(true); 
};
nbApi.popupEvenHandlers.startMove = function(event, objId)
{
  objId = "WzTtDiV";
  var clientY = event.clientY;
  var clientX = event.clientX;
  nbApi[objId + "_y"] = clientY;
  nbApi[objId + "_x"] = clientX;
  var obj = document.getElementById(objId); 
  nbApi[objId] = obj;
  if (!obj)
    return;
  var pos = nbApi.findPosition(obj);
  nbApi[objId + "_pos"] = pos;
}
nbApi.popupEvenHandlers.stopMove = function(event, objId)
{
  objId = "WzTtDiV";
  nbApi[objId] = null;
}
nbApi.popupEvenHandlers.move = function(event, objId)
{
  objId = "WzTtDiV";
  var obj = nbApi[objId];
  if (!obj)
    return;
  var newY = event.clientY;
  var newX = event.clientX;
  var oldY = nbApi[objId + "_y"];
  var oldX = nbApi[objId + "_x"];
  var pos = nbApi[objId + "_pos"];
  var top = pos.top + (newY - oldY);
  var left = pos.left + (newX - oldX);
  obj.style.top = top + "px";
  obj.style.left = left + "px";
}
// load and display data in a "popup". Intended to work with a popupLink.tag  
// popupId - popupId used in popupLink tag
// url - ajax call url
// paramList - ajax call url parameters 
// y. x, height , width - desired position and size of a popup window
// forceLoad - force reload of the popup (ajax call) every time we display it
//             valid values: "hard" - unconditional reload, "soft" - reload if popupId changed since last call   
nbApi.displayPopup = function(eventSource, popupId, url, paramList, event, height, width, forceLoad, forwardUrl, title, prefix, bPersistent)
{ 
  nbApi.UnTip(true); 
  var popup = document.getElementById(popupId);
  window.nextbioPopup = popup;
  if (popup == null)
    return;
  popup.setAttribute("nbSwitchTab", "true");  

  var termName;
  if (eventSource && typeof(eventSource) == "object")
  {
    var termName = eventSource.getAttribute("nbTagName");
    popup.setAttribute("nbName", termName);
    popup.setAttribute("nbTermFilter", "tf=" + encodeURIComponent(termName));
    var tagId = nbApi.getParam(paramList, "id");
    popup.setAttribute("nbTagId", "id=" + tagId);
    var termArr = nbApi.findElementsByName(popupId + "_term", "span");
    if (termArr && termArr.length)
    {
      for (var i = 0; i < termArr.length; i++)
      {
        termArr[i].innerHTML = nbApi.truncateWithEllipses(termName, 15);
      }
    }
  }
  nbApi.nextbio_popupId = popupId;
  popup.style.display = "none";
  if (paramList == null)    
    paramList = "";
  var type = nbApi.getParam(paramList, "type");
  var searchTerm = paramList.indexOf("linkOutSearchTerm");
  var subTitle;
  if (title != null && searchTerm < 0)  
  {
    var titleEnd = title.indexOf(",");  	
  	if (titleEnd < 0 )
  		subTitle = title;
  	else
  		subTitle = title.substring(0, titleEnd);
  	// strip replace {comma} with ","	
  	if (subTitle.length > 1)
  		subTitle = subTitle.replace(/\{comma\}/g, ",");
		paramList += "&linkOutSearchTerm=" + encodeURIComponent(subTitle);
  }
  var query = null;
  if (subTitle != null)  
  {
    query = subTitle;
    paramList += "&q=" + encodeURIComponent(query);
  }
  if (query != null && ("treatment" == type || "tissue" == type || "disease" == type))  
  {
    paramList += "&synonym=" + encodeURIComponent(query);
  }
   
  var tagName = nbApi.getParam(paramList, "q");  
  popup.setAttribute("nbTagName", "q=" + tagName);
  
  //if tag is not associated with any tag type then assign  synonym as searchTerm and update popupTerm
  if(!(eventSource && popup.getAttribute("nbName"))){
  	popup.setAttribute("nbName",query);
  	var termArr = nbApi.findElementsByName(popupId + "_term", "span");
    if (termArr && termArr.length)
    {
      for (var i = 0; i < termArr.length; i++)
      {
        termArr[i].innerHTML = nbApi.truncateWithEllipses(query, 15);
      }
    }
  }
  
  var scrollTop = nbApi.getScrollTop();
  var scrollLeft = nbApi.getScrollLeft();
  var clientY = event.clientY;
  var clientX = event.clientX;

  var bForceLoad = (forceLoad == "hard") || (forceLoad == "soft" && nbApi.nextbio_popupId != popupId);
  if (bForceLoad == true || popup.loaded == null || popup.loaded == false)
  {
    popup.loaded = true;
    var targetId = popupId + "_content";
    var iFrameId = popupId + "_scriptLoader";        
    popup.setAttribute("nbDynamicUrl", url);
    popup.setAttribute("nbDynamicQuery", paramList);

    var sdUrl = popup.getAttribute("sdUrl");
    var publicationId = popup.getAttribute("publicationId");
    nbApi.enablePopupTabs = true;
    nbApi.addEventHandler(document.body, "click", nbApi.popupEvenHandlers.onclick);
        nbApi.TagToTip(popupId, 
          BGCOLOR, '', 
          BORDERWIDTH, 0,          
          PADDING, 0,
          ABOVE, true, 
          FIX, [-5000, -5000],
          FONTSIZE, '',
          COPYCONTENT, false,
          STICKY, true
        );
        var pos = nbApi.getAdjustedPosition(popup, clientY - 10, clientX + 10);
        nbApi.TagToTip(popupId, 
          BGCOLOR, '', 
          BORDERWIDTH, 0,
          PADDING, 0,
          ABOVE, true, 
          FIX, [pos.x, pos.y],
          FONTSIZE, '',
          COPYCONTENT, false,
          STICKY, true
        );
    nbApi.updatePopupEx({popupId: popupId, iFrameId: iFrameId, initial:true, callback: function(target, bLoaded, json){
      // nbApi.trackUrl("/interact/popup/"+type+"/"+query);
    }});
  } 
  else
  {
    nbApi.addEventHandler(document.body, "click",nbApi.popupEvenHandlers.onclick);
    nbApi.TagToTip(popupId, 
      BGCOLOR, '', 
      BORDERWIDTH, 3,
      PADDING, 0,
      ABOVE, true, 
      OFFSETX, -10,
      FONTSIZE, '',
      COPYCONTENT, false,
      STICKY, true,
      SHADOW, true
    );
  }
  var forwardHTML = '';
  if (forwardUrl != null)
  {
    var modParamList = paramList.replace("type=treatment","type=compound");  // DE2561
    var href = forwardUrl + "?" + modParamList;
    nbApi.setInnerHtml(popupId + "_forwardText" , "VIEW COMPLETE DETAILS");
    nbApi.setAttr(popupId + "_forwardUrl" , "href", href);
    var img = '<img src="'+nb.img+'/vert/OverviewTiny.png" style="vertical-align:middle;margin-right:5px" />'
    forwardHTML= '<a href="' +href + '" target="_blank">' + img + '</a>';
  }
  if (termName)
    title = termName;
  if (title != null)
  {
    title = title.replace(/,/g, ", ");
 		title = title.replace(/\{comma\}/g, ",");
    title = nbApi.normalizeString(title);
    var shortTitle = nbApi.truncateWithEllipses(title, 45); 
//    nbApi.setAttr(popupId + "_title" , "title", title);
    // nbApi.setInnerText(popupId + "_title" , shortTitle);
    nbApi.setInnerHtml(popupId + "_title" , forwardHTML + query.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"));
  }
  if (prefix != null)
  {
    prefix = prefix.toLowerCase();
    if (prefix == "feature")
      prefix = "gene";
    if (prefix == "treatment")
      prefix = "compound";
    if (prefix == "disease")
      prefix = "phenotype";
    nbApi.setInnerHtml(popupId + "_prefix" , prefix);  
  }
  var icon = document.getElementById(popupId+"_img");
  if (icon)
    icon.src = nbApi.getIcon(prefix, nbApi.imgContext, null, true);
    
  return popup;
}

nbApi.cancelTimeout = function()
{
  if (nbApi.popupWindow.pendingTimeoutId != null)
    clearTimeout(nbApi.popupWindow.pendingTimeoutId);
  nbApi.popupWindow.pendingTimeoutId = null;

}
// pops up a list of items from itemList or an item popup if there is only one item in the list
nbApi.popupItems = function(/* eventSource, popupId, url, itemsList, event, bNow, delay, popupCb */)
{
  // old index doesn't provide eventSource
  // this is a hack to get around this problem
  var eventSource;
  var popupId;
  var url;
  var itemsList;
  var event;
  var bNow;
  var delay;
  var popupCb;
  eventSource = popupId = url = itemsList = event = bNow = delay = filter = popupCb = null;
  var i = 0;  
  if (typeof(arguments[0]) == "object")
  {
    eventSource = arguments[i++];
  }
  if (i < arguments.length)
  {
    popupId = arguments[i++];  
  }
  if (i < arguments.length)
  {
    url = arguments[i++];  
  }
  if (i < arguments.length)
  {
    itemsList = arguments[i++];  
  }
  if (i < arguments.length)
  {
    event = arguments[i++];  
  }
  if (i < arguments.length)
  {
    bNow = arguments[i++];  
  }
  if (i < arguments.length)
  {
    delay = arguments[i++];  
  }
  if (i < arguments.length)
  {
    popupCb = arguments[i++];  
  }
// end hack
  nbApi.cancelEvent(event);
  if (delay == null)
    delay = 500;
  if (bNow != true)
  {
    nbApi.cancelTimeout();
//    document.body.onmouseover = function(event){nbApi.cancelTimeout();};
    nbApi.addEventHandler(document.body, "mouseover", nbApi.popupEvenHandlers.onmouseover);
    var myEvent = new Object();
    myEvent.clientX = event.clientX;
    myEvent.clientY = event.clientY;
    myEvent.nbEvent = true;
    
    nbApi.popupWindow.pendingTimeoutId = setTimeout(function(){nbApi.popupItems(eventSource, popupId, url, itemsList, myEvent, true, null, popupCb)}, delay);
    this.onmouseout = function()
    {
      nbApi.cancelTimeout();
    };
    return;
  }
  if (popupId == null || url == null || itemsList == null || itemsList.length == 0)
    return;    
  if (popupCb) popupCb();  // if there is a callback when we actually popup up the popup, call it.
  //url = nbApi.urlContext + url;  
  var item;
  if (itemsList.length == 1)
  {
    item =  itemsList[0];
    var query = "id=" + item[1] + "&type=" + item[0];
    nbApi.popupMisc[popupId + "_termId"] = item[1];
    var popup = nbApi.displayPopup(eventSource, popupId, url, query, event, null, null, "hard", nbApi.urlContext + "/search/ov.nb", item[2], item[0], false);
		return;
  }
  var html = "<div id='NbPopupListFrameId' class='NbPopupListFrame' onmouseover='nbApi.cancelEvent(event);' onmousemove='nbApi.cancelEvent(event);' onclick='nbApi.cancelEvent(event);' >";
  for (i = 0; i < itemsList.length; i++)
  { 
    var item =  itemsList[i];
    var itemId = item[1];
    var itemType = item[0];
    var itemTitle = item[2].replace(/\'/g, "\\x27");
    itemTitle = itemTitle.replace(/\"/g, "\\x22"); 
    itemTitle = nbApi.normalizeString(itemTitle);   
    var image;  
    var bPopup = true;
    image = nbApi.getIcon(itemType, nbApi.imgContext, true);  
    var doPopup;
    var title = item[2];
  	if (title.length > 1)
  		title = title.replace(/\{comma\}/g, ",");
    if (bPopup)
    {
      doPopup = 'nbApi.displayPopup(null, "' + popupId + '","' + url + '","id=' + itemId + '&type=' + itemType + 
        '",event,null,null, "hard", nbApi.urlContext + "/search/ov.nb", "' +  itemTitle + '", "' + itemType + '", false);';
    }
    else
    {
      doPopup = 'submitFilter(\"' + itemTitle + '\");';
      title = "filter by <b>" + title + "</b>";
    }
    if (bPopup == false && _noFilter)
      break;
    if (bPopup == false || i < 10)
    {
      html += "<div class='NbPopupListEntry' onmouseover='this.className=\"NbPopupListSelected\"' ";
      html += "onmouseout='this.className=\"NbPopupListEntry\"' ";
      html += "onclick='" + doPopup + "'>";
      html += "<img class='LinkIcon' src='" + image + "' alt='' />&nbsp;" + title;
      html += "</div>";
    }
  }
  html += "</div>";
  var dlg = nbApi.createPopupWindow(eventSource, "_popupList", "NbPopupList", html);  
}
nbApi.popupWindow = new Object;

nbApi.createPopupWindow = function(eventSource, id, className, html)
{
	// create a dialog if we don't already have one
    nbApi.Tip(html, 
      BGCOLOR, '', 
      BORDERWIDTH, 0,
      PADDING, 0,
      ABOVE, true, 
      OFFSETX, 0,
      JUMPHORZ, true,
      JUMPVERT, true, 
      FONTSIZE, '',
      COPYCONTENT, false,
      STICKY, true,
      CLICKCLOSE, true
    );
}
// hide tabs that are not supposed to be displayed for this publication
// config={tabs:tabs, publicationId: publicationId}
nbApi.hideInvalidTabs = function(config)
{
  if (!config || !config.popup)
    return;
    
  var tabs = config.tabs;
  if (!tabs || tabs.length == 0)
    return;
  var publicationId = config.popup.getAttribute("nbPublicationId");
  
  for (var i = 0; i < tabs.length; i++)
  {
    var realIdOnly = tabs[i].getAttribute("nbRealIdOnly");
    if ("true" == realIdOnly && publicationId && publicationId >= nbApi.popupMisc.FAKE_ID)
    {
      tabs[i].style.display = "none";
    }
    else
    {
      var subTabs = document.getElementsByName(tabs[i] + "_subTab");
      nbApi.hideInvalidTabs({tabs: subTabs, popup: config.popup});
    } 
  }
  return;
} 
// deselect all tabs and return default tab index
nbApi.deselectTabs = function(tabs)
{
  if (!tabs || tabs.length == 0)
    return null;
  var theTab = 0;  
  for (var i = 0; i < tabs.length; i++)
  {
    var isDefaultTab = tabs[i].getAttribute("nbDefault");
    if ("true" == isDefaultTab && tabs[i].style.display != "none")
    {
      theTab = i;
    }
    tabs[i].className = "nbApiExTab";
  }
  return tabs[theTab];
} 
// deselect all subtabs and return default tab 
// if config.subTabId is not null, return matching tab
nbApi.deselectSubTabs = function(config)
{
  if (!config || !config.popupId || !config.tabId)
    return null;
  var subTabsContainer = document.getElementById(config.popupId + "_SubTabs");   
  if (!subTabsContainer || !subTabsContainer.childNodes)
    return null;  
  var subTabs = new Array();
  var j = 0;
  for (var i = 0; i < subTabsContainer.childNodes.length; i++)
  {
    var tabId;
    if (!subTabsContainer.childNodes[i].getAttribute)
      continue;
    tabId = subTabsContainer.childNodes[i].getAttribute("tabId");
    if (!tabId || tabId != config.tabId)
      continue;
    subTabs[j++] = subTabsContainer.childNodes[i];
  }
  if (subTabs.length == 0)
    return null;
  var theSubTab = 0;
  for (var i = 0; i < subTabs.length; i++)
  {
    var isDefaultSubTab = subTabs[i].getAttribute("nbDefault");
    if (!config.subTabId && "true" == isDefaultSubTab && subTabs[i].style.display != "none" || subTabs[i].id == config.subTabId)
    {
      theSubTab = i;
    }
    subTabs[i].className = "nbApiExSubTab";
  }
  return subTabs[theSubTab];
}
//select tab
nbApi.selectTab = function(tab)
{
  if (!tab)
    return;
  tab.className = "nbApiExSelectedTab";
  tab.style.display="";
}
//select subtab
nbApi.selectSubTab = function(tab)
{
  if (!tab)
    return;
  tab.className = "nbApiExSelectedSubTab";
//    setTimeout(function() {tab.blur()},0);
}
nbApi.showSubTabs = function(popupId, tab, subTabs)
{
  if (!popupId || !tab)
    return;
  var tabConts = nbApi.findElementsByName(popupId + "_subTabContainer", "div");
  var tabContainer = document.getElementById(popupId + "_SubTabs");
  var subTabsId = tab.id + "_subTabs";
  var showSubTabs = false;
  if (tabConts != null && tabConts.length > 0)
  {                               
    for (var i = 0; i < tabConts.length; i++)
    {
      var contId = tabConts[i].id; 
      if (subTabs && contId == subTabsId)
      {
        tabContainer.innerHTML = tabConts[i].innerHTML;
        showSubTabs = true;
        break;
      }
    }
  }
  
  tabContainer.style.display = showSubTabs?"":"none";
  var content = document.getElementById(popupId + "_content");
  if (content && showSubTabs)
  {
    tabContainer.style.display = "";
    content.className = "nbApiExPopupText";
  }
  else
  {
    tabContainer.style.display = "none";
    content.className = "nbApiExPopupTextNoSubTabs";
  }
}



/**
 * find and select tab and subtab. If config doesn't have this information - use default tab/subtab
 * 
 *  config.popupId - popup id 
 *  config.tabId - tab to select 
 *  config.subTabId - subtab to select 
 */  
nbApi.setPopupTabEx = function(config)               
{
  if (!config.popupId)
    return null;
  var tabs = document.getElementsByName(config.popupId + "_tab");
  if (!tabs || tabs.length == 0)
    return null;

  nbApi.hideInvalidTabs({popup: config.popup, tabs: tabs});
 
  if (!config.tabId && !config.subTabId) // use default tab and subtab
  {
    // find default tab. If none - use first available
    var theTab = nbApi.deselectTabs(tabs);
    if (!theTab)
      return null;
    nbApi.selectTab(theTab);
    config.tabId = theTab.id;
    //get subtabs
    var subTabs = document.getElementsByName(theTab.id + "_subTab");    
    // show subtabs
    nbApi.showSubTabs(config.popupId, theTab, subTabs && subTabs.length);
    // if no subtabs - return main tab
    if (!subTabs || subTabs.length == 0)
      return theTab;
    var theSubTab = nbApi.deselectSubTabs(config); 
    nbApi.selectSubTab(theSubTab);
    return theSubTab;
  }

  if (config.tabId && !config.subTabId) // use default subtab
  {
    nbApi.deselectTabs(tabs);
    var theTab = document.getElementById(config.tabId);
    if (!theTab)
      return null; 
    nbApi.selectTab(theTab);
    config.tabId = theTab.id;
    //get subtabs
    var subTabs = document.getElementsByName(theTab.id + "_subTab");    
    // show subtabs
    var theSubTab = nbApi.showSubTabs(config.popupId, theTab, subTabs && subTabs.length);
    if (!subTabs || subTabs.length == 0)
      return theTab;
    var theSubTab = nbApi.deselectSubTabs(config); 
    nbApi.selectSubTab(theSubTab);
    return theSubTab;
  }
  if (config.tabId && config.subTabId) 
  {
    nbApi.deselectTabs(tabs);
    var theTab = document.getElementById(config.tabId);
    if (!theTab)
      return null; 
    nbApi.selectTab(theTab);
    config.tabId = theTab.id;
    //get subtabs
    var subTabs = document.getElementsByName(theTab.id + "_subTab");    
    // show subtabs
    var theSubTab = nbApi.showSubTabs(config.popupId, theTab, subTabs && subTabs.length);
    if (!subTabs || subTabs.length == 0)
      return theTab;
    var theSubTab = nbApi.deselectSubTabs(config); 
    nbApi.selectSubTab(theSubTab);
    return theSubTab;
  }
}
/**
 * Update content section of a popup
 * popupId - id of a popup
 * tabId - id of a tab
 * subTabId - id of a subTab 
 * url - url to update from
 * paramList - query part of the url
 * limit - max number of entries to return
 * filter - if true, use filter
 * switchTabs - if true, swutch tabs 
 * 
 */       
nbApi.updatePopupEx = function(config)
{ 
  if (!nbApi.enablePopupTabs)
    return;
  if (!config)
    return;
  if (!config.popupId)
    return;

  if (config.updateTabs == null)
    config.updateTabs = true;  
  var popup = config.popup;
  if (!popup)
  {
    popup = window.nextbioPopup;
    config.popup = popup;
  }
  if (!popup)
  {
    popup = document.getElementById(config.popupId);
    config.popup = popup;
  }
  if (config.initial)
  {
    var switchable = popup.getAttribute("nbSwitchable");
    nbApi.visibility(popup.id + "_tabBar", (switchable == "false"));  
  }
  if (config.updateTabs)
  {
    var tab = nbApi.setPopupTabEx(config); // select tab or subtab
    if (!tab)
      return;
    popup.setAttribute("nbSelectedTabId", tab.id);  
    config.currentTabId = tab.id;  
    config.tabMessage = "Please wait while the information is loaded."
    nbApi.setTabMessage(config);  
    nbApi.visibility(popup.id + "_buttonBar", false);  
    nbApi.setButtonBar(config);
    if (!config.url) // load default tab              
    {
      tab.onclick();
      if (config.callback)
        config.callback();
      return false;
    }
  }
  if (!config.paramList)    
    paramList = "";
  var modUrl = config.url;
  if (config.fixUrl)
    modUrl = nbApi.urlContext + config.url;
  var targetId = config.popupId + "_content";
  var target = document.getElementById(targetId);
  if (target)
  {
    target.innerHTML = "<span class='nbApiLoading'>&nbsp;</span>Loading...";
  }
  
  var publicationId = popup.getAttribute("nbPublicationId");
  var modParamList = config.paramList;
  if (config.fixQuery)
  {
    if (config.limit && config.limit > 0)
      modParamList += "&maxNumRow=" + config.limit;
    if (config.filter && publicationId && publicationId.length)
      modParamList += "&tf=" + encodeURIComponent(popup.getAttribute("nbName"));
    modParamList += "&nameSpace=" +  "nbApi";
    modParamList += "&js=e";
    modParamList += "&small=" + popup.getAttribute("small");
    modParamList += "&log=false&logType=POPUPTAB";
    var tracking = popup.getAttribute("nbTracking");
    if (tracking) {
      modParamList += "&"+tracking + "&logTagName=" + encodeURIComponent(popup.getAttribute("nbName"));
    }
    if (config.tabId)
    {
      modParamList += "&logTab=" + config.tabId.replace(config.popupId+"_","");
    }
    if (config.subTabId) {
      modParamList += "&logSubtab=" + config.subTabId.replace(config.popupId+"_","");
    }
    else if (config.menuId)
    {
      modParamList += "&logSubTab=" + config.menuId;
    }
  }
  if (navigator.userAgent.indexOf("MSIE 6") >= 0 || navigator.userAgent.indexOf("MSIE 7") >= 0) {
    
    var charset = document.charset;
    if (charset)
    {
      modParamList += "&charset=" + charset;
    }
  }
  var loadScriptConfig = {};
  var switchCfg = {popupId: config.popupId, force: true, loadConfig: loadScriptConfig};
  switchCfg.timeout = setTimeout(function(){nbApi.switchTab(switchCfg)}, 5000);
  var callback = function(targetId, bLoaded, json)
  {
    if (switchCfg.timeout) 
    {
      clearTimeout(switchCfg.timeout);
      switchCfg.timeout = null;      
    } 
    else if (switchCfg.isSwitchable)
    {
      return;
    }
    if (nbApi.switchTab(config))
      return;
    config.tabMessage = null;
    nbApi.setTabMessage(config);  
    nbApi.visibility(popup.id + "_tabBar", true);  
    nbApi.dynamicSubTabs(targetId, bLoaded, json);
    if (config.callback) {
      config.callback(targetId, bLoaded, json);
    }
    nbApi.checkButtonBar();
    nbApi.visibility(popup.id + "_buttonBar", true);  
  }
  // save parameters for posible future use
  popup.setAttribute("nbLastUrl", modUrl);
  popup.setAttribute("nbLastQuery", modParamList);  
  if (config.iFrameId)
    popup.setAttribute("nbIFrameId", config.iFrameId);
  loadScriptConfig = 
  {
    containerId: config.popupId, 
    target: targetId, 
    scriptTarget: config.popupId+"_script", 
    iFrameId: config.iFrameId, 
    url: modUrl, 
    query: modParamList, 
    callbackName: "nbApi.updateTargetHTML", 
    callback: callback
  };
  nbApi.loadScript(loadScriptConfig);
  if (!config.initial) {
    var u = "/interact/popupTab/";
    if (config.tabId)
    {
      u += config.tabId.replace(config.popupId+"_","");
    }
    if (config.subTabId) {
      u += "/"+config.subTabId.replace(config.popupId+"_","");
    }
    else if (config.menuId)
    {
      u += config.menuId;
    }
    // nbApi.trackUrl(u);
  }
  
}
nbApi.checkButtonBar = function() {
  var obb = document.getElementById("nextbio_overrideButtonBar0");
  if (obb) {
    nbApi.setButtonBar({buttonBarId: "nextbio_overrideButtonBar0", element: obb}); 
  } else {
    obb = document.getElementById("nextbio_overrideButtonBar");
    if (obb) {
      nbApi.setButtonBar({buttonBarId: "nextbio_overrideButtonBar", element: obb}); 
    }
  }
}
// see if there is a fallback request withing the last loaded tab
// if there is on - switch to that tab
nbApi.switchTab = function(config) 
{
  if (!config)
    return false;
  config.timeout = null;  
  var popup = window.nextbioPopup;
  if (!popup)
    popup = document.getElementById(config.popupId);
  if (!popup)
    return false;
  var nbSwitchTab  = popup.getAttribute("nbSwitchTab");
  if (config.switchTabs == "false" || (nbSwitchTab != "true"))
    return false;  
  popup.removeAttribute("nbSwitchTab");
  if (!config.force)
  {
    var tabSwitcher = document.getElementById("nextbio_tabSwitcher");
    if (!tabSwitcher)
      return false;
  }
  var selectedTabId  = popup.getAttribute("nbSelectedTabId");
  if (!selectedTabId || !selectedTabId.length)
    return false;
  var selectedTab = document.getElementById(selectedTabId); 
  if (!selectedTab) 
    return false;
  var nextTabId = selectedTab.getAttribute("nbNextTabId");
  if (!nextTabId || !nextTabId.length)
    return false;
  var nextTab = document.getElementById(nextTabId); 
  if (!nextTab)
    return false;
  config.isSwitchable = true;
  config.loadConfig = null; // to prevent load results from showing up
  selectedTab.style.display="none";
  nextTab.onclick();  
  return true;
}

nbApi.updateMenuEx = function(config)
{
  if (!config ||!config.menuId || !config.itemId)
    return;
  var items = document.getElementsByName(config.menuId + "_menuItem");
  if (!items)
    return;
  var selectedMenu;
  for (var i = 0; i < items.length; i++)
  {
    if (items[i].id == config.itemId)
    {
      items[i].className = "nbApiExSelectedSubTab";
      selectedMenu = items[i];
    }
    else
    {
      items[i].className = "nbApiExSubTab";
    }
  }   
//   nbApi.updatePopupTerm({element: selectedMenu});
  nbApi.setTabMessage({currentTabId: selectedMenu.id, element: selectedMenu}); 
  nbApi.setButtonBar({currentTabId: selectedMenu.id, element: selectedMenu}); 
}
/**
 * Build dynamic query string
 * config.query: query string
 * config.dQuery: list of attribute names containing query strings
 * config.popupId - id of a popup
 */ 
nbApi.buildPopupQuery = function(config)
{
  var dQuery = "";  
  if (!config)
    return dQuery;
  var popup;
  if (config.popup)
  {
    popup = config.popup;
  }
  else if (window.nextbioPopup)
  {
    popup = window.nextbioPopup;
  }
  else if (config.element) 
  {
    popup = nbApi.findParentByClassName(config.element, "nbApiExPopup");
  }
  else if (config.popupId && config.popupId.length) 
  {
    popup = document.getElementById(config.popupId);
  }
  if (!popup)
    return dQuery;
  if (config.dQuery && config.dQuery.length)
  {
    var qArray = config.dQuery.split(",");
    for (var i = 0; i < qArray.length; i++)
    {
      var dynamicQuery = popup.getAttribute(nbApi.trim(qArray[i]));
      if (dynamicQuery && dynamicQuery.length)
      {
        if (i > 0)
          dQuery += "&";  
        dQuery += dynamicQuery;
      }
    }
  }
  var query = "";
  if (dQuery.length)
    query = dQuery;
  if (dQuery.length && config.query && config.query.length) 
    query += "&";
  if (config.query && config.query.length)
    query += config.query;

  var hash = "";
  if (config.dHash && config.dHash.length)
  {
    var qArray = config.dHash.split(",");
    for (var i = 0; i < qArray.length; i++)
    {
      var dynamicHash = popup.getAttribute(nbApi.trim(qArray[i]));
      if (dynamicHash && dynamicHash.length)
      {
        if (i > 0)
          hash += "&";  
        hash += dynamicHash;
      }
    }
  }
  if (hash.length) 
  {
    query += "#";
    query += hash;
  }

  return query;
}
/**
 * Build a URL out of url, query and dynamic query, if any. Fix URL if needed
 * if config.obj is provided, set obj.href to new url value 
 * config.url - url
 * config.query: query string
 * config.dQuery: list of attribute names containing query strings
 * congig.popupId - popup id
 * config.fixUrl - prepend urlContext, if true  
 * config.obj - anchor that requests new url 
 *    
 */ 
nbApi.buildPopupUrl = function(config)
{
  if (!config)
    return "";
  var url = config.url;
  if (config.fixUrl)
  {
    url = nbApi.urlContext + url;
  }
  var query = nbApi.buildPopupQuery(config);
  if (query && query.length)
    url += "?" + query;
  if (config.element)
    config.element.href = url;
  return url;
}
/**
 * find dynamically defined menu and set it instead of current sub tabs
 */ 
nbApi.dynamicSubTabs = function()
{
  var menuArr = nbApi.getElementsByClassName(document, "nbApiExPopupMenu", "div");
  if (!menuArr || !menuArr.length)
    return;
  var menu = menuArr[0];  
  var popup = window.nextbioPopup;
  if (!popup)
  {
    popup = nbApi.findParentByClassName(menu, "nbApiExPopup");
  }
  var text = nbApi.findParentByClassName(menu, "nbApiExPopupTextNoSubTabs");
  if (popup)
  {
    nbApi.updatePopupTerm({popup:popup, menuId: menu.id});
    var subTabs = document.getElementById(popup.id + "_SubTabs");
    if (subTabs)
    {
      subTabs.innerHTML = menu.innerHTML;
      subTabs.style.display="";
      if (text)
        text.className = "nbApiExPopupText";
    }
    var menuItemArr = document.getElementsByName(menu.id + "_menuItem");      
    if (!menuItemArr || !menuItemArr.length)
      return;
    for (var i = 0; i < menuItemArr.length; i++)
    {
      if (menuItemArr[i].className == "nbApiExSelectedSubTab")
      {
        nbApi.setTabMessage({currentTabId: menuItemArr[i].id, popup: popup}); 
        nbApi.setButtonBar({currentTabId: menuItemArr[i].id, popup: popup}); 
        break;
      }
    }
  }
}
/**
 * find and set tab message for the tab
 * config.tabId - id of a tab 
 * config.popupId 
 * config.element - oject that requests this operation 
 */ 
nbApi.setTabMessage = function(config)
{
  if (!config || !config.currentTabId)
    return;    
  var popup = window.nextbioPopup;
  if (!config.popup)
    config.popup = popup;  
  if (!config.popupId && !config.popup && !config.element)  
    return; 
  if (!config.popupId && config.popup && config.popup.id)
    config.popupId = config.popup.id; 
  if (!config.popupId) 
  {
    var popup = nbApi.findParentByClassName(config.element, "nbApiExPopup");
    config.popupId = popup.id;
  }
  var tabTitle = document.getElementById(config.popupId + "_PopupTabTitle");
  if (!tabTitle)
    return;
  if (config.tabMessage)
  {
    tabTitle.innerHTML = config.tabMessage;
  }
  else
  {  
  var tabMessage = document.getElementById(config.currentTabId + "_message");
  if (tabMessage)
    tabTitle.innerHTML = tabMessage.innerHTML;
  else
    tabTitle.innerHTML = "&nbsp;";
  }  
}
nbApi.setButtonBar = function(config)
{
  if (!config || (!config.currentTabId && !config.buttonBarId))
    return;    
  var popup = window.nextbioPopup;
  if (!config.popup)
    config.popup = popup;
  if ((!config.popupId || !config.popupId.length) && config.popup && config.popup.id)
    config.popupId = config.popup.id; 
  if (!config.popupId && !config.element)  
    return; 
  if (!config.popupId || !config.popupId.length) 
  {
    var popup = nbApi.findParentByClassName(config.element, "nbApiExPopup");
    config.popupId = popup.id;
  }
  var tabButtonBar = document.getElementById(config.buttonBarId || (config.currentTabId + "_buttonBar"));
  var buttonBar = document.getElementById(config.popupId + "_buttonBar");
  if (!buttonBar)
    return;
  buttonBar.innerHTML = "";
  if (!tabButtonBar)
    return;
  buttonBar.innerHTML = tabButtonBar.innerHTML;
}


// updates popup's content area. Fixes url and query
// config.updateTabs - if true or null - update tabs in a regular way. Otherwise - do not update tabs.
nbApi.simpleUpdateEx = function(config)
{
  var popup = window.nextbioPopup;
  if (!popup)
    return;
  nbApi.updatePopupEx({popupId: popup.id,
                                 popup: popup, 
                                 tabId: config.tabId, 
                                 subTabId: config.subTabId, 
                                 url: config.url, 
                                 paramList: nbApi.buildPopupQuery({popupId: popup.id, query: config.query, dQuery: config.dQuery}), 
                                 filter: config.filter, 
                                 fixUrl:true, 
                                 fixQuery:true, 
                                 switchTabs: config.switchTabs, 
                                 updateTabs: config.updateTabs});  
}

nbApi.updatePopupTerm = function(config)
{
  var popup;
  if (config.popup)
  {
    popup = config.popup;
  }
  else if (window.nextbioPopup)
  {
    popup = window.nextbioPopup;
  }
  else if (config.element) 
  {
    popup = nbApi.findParentByClassName(config.element, "nbApiExPopup");
  }
  else if (config.popupId) 
  {
    popup = document.getElementById(popupId);
  }
  else
    return;
  var termName = popup.getAttribute("nbName");
  termName = nbApi.truncateWithEllipses(termName, 15);   
  var id = config.menuId?config.menuId:popup.id; 
  var termArr = nbApi.findElementsByName(id + "_term", "span");
  if (!termArr && termArr.length)
    return;
  
  for (var i = 0; i < termArr.length; i++)
  {
    termArr[i].innerHTML = termName;
  }
  
}

//report false positive term
nbApi.reportFalseTerm = function(config){
    if (!confirm ("Would you like to report this term as false positive?")) {
        return false;
    } 
    var pageUrl = document.URL;       
    var paramList;
    var modurl;
    if(config && config.target){
    	modurl = "https://www.nextbio.com/b" + config.url;
    	paramList = "term=" + config.term + "&type=" + config.type + "&article=" +config.article + "&publicationId=" +config.publicationId + "&pageUrl=" +pageUrl;
    	nbApi.loadScript({target: config.target, url: modurl, query: paramList, scriptTarget:config.scriptTarget});
    	alert("An email has been sent. Thank you for reporting.");
    }    
    
}
/**
 *  load matching sentences filtered by a string of space separated terms
 *  
 *  config.element - <a> tag that calls this function
 *  config.targetId - id of a target div
 *  config.filterId - id of an <input> that contains the filter string
 *  
 */      
nbApi.filterSentences = function(config)
{
  // find filter string
  if (!config || !config.event)
    return;
  if (config.event.type == "keypress" && config.event.keyCode != 13)
    return;  
  var filter = document.getElementsByName(config.filterName);
  if (!filter || !filter.length)
      return;
  var value = "";
  for (var i = 0; i < filter.length; i++)
  {
    if (filter[i].value && filter[i].value.length)
    {
      if (config.clear)
      {
        filter[i].value = "";
      }
      else
      {
        value = nbApi.normalizeString(filter[i].value);
        filter[i].value = value;
        break;
      }
    }
  }
  var query = config.query;
  if (value.length)
    query += "&filter=" + encodeURIComponent(value);
  nbApi.simpleUpdateEx(    
    {
      tabId: config.tabId, 
      url: config.url, 
      query: query,
      dQuery: config.dQuery,
      filter: true,
      switchTabs: "false",
      updateTabs: false
      });
  return;
}

/**
 *  expand or collapse a matching sentence
 *  config.element - element that requested the operation
 *  config.targetId - id of an elemen that will receive the new content
 *  config.number  number of the sentence being updated
 *  config.padding - number of the sentences before and afer the sentence in question
 *  config.url - url of the sentence server
 *  config.query - query    
 */  
nbApi.updateSentence = function(config)
{
  if (!config || !config.element)
    return;
  var popup = window.nextbioPopup;
  if (!popup)
    return;  
  var url = popup.getAttribute("nbLastUrl");
  var query = popup.getAttribute("nbLastQuery");
  var iFrameId = popup.getAttribute("nbIFrameId");
  if (!query)
    query = "";
  query += "&encNum=" + config.encryptedNumber; 
  if (config.encryptedDocumentId)
    query += "&encId=" + config.encryptedDocumentId;
  var expanded = config.element.getAttribute("nbExpanded");
  var target = document.getElementById(config.targetId);
  var original = document.getElementById(config.targetId + "_collapsed");
  if ("true" == expanded)
  {
    config.element.className = "nbApiExpand";
    config.element.setAttribute("nbExpanded", "false");
    target.style.display = "none";
    if (original)
    {
      original.style.display = "";
    }
  } 
  else
  {
    original.style.display = "none";
    config.element.className = "nbApiCollapse";
    config.element.setAttribute("nbExpanded", "true");
    var loaded = config.element.getAttribute("nbLoaded");
    if (loaded == "true")
    {
      target.style.display = "";
      return;
    }
    target.innerHTML = "<span class='nbApiLoading'>&nbsp;</span>Loading...";
    target.style.display = "";
    config.element.setAttribute("nbLoaded", "true");
    var callback = function()
    {
    }
    nbApi.loadScript({containerId: popup.id, target: config.targetId, scriptTarget:config.popupId+"_script", iFrameId: iFrameId, url: url, query: query, callbackName: "nbApi.updateTargetHTML", callback: callback});
  }
}

/**
 *  expand or collapse a matching sentence in a multi-document environment
 *  config.element - element that requested the operation
 *  config.targetId - id of an elemen that will receive the new content
 *  config.number  number of the sentence being updated
 *  config.padding - number of the sentences before and afer the sentence in question
 *  config.url - url of the sentence server
 *  config.query - query  
 *  config.encryptedDocumentId - encrypted doc id   
 *  config.documentId - doc id   
 */  
nbApi.updateSentenceMultiDoc = function(config)
{

  if (!config || !config.element)
    return;
  var url;
  var query;
  var iFrameId;
  var popup = window.nextbioPopup;
  if (popup)
  {
    url = popup.getAttribute("nbLastUrl");
    query = popup.getAttribute("nbLastQuery");
    iFrameId = popup.getAttribute("nbIFrameId");
  }
  else
  {
    var doc = document.getElementById("doc-" + config.docId);
    if (!doc)
    {
      doc = document.getElementById("doc-id");  
      if (!doc){
        doc = document.getElementById(config.clipboardPrefix + config.encryptedDocumentId);
        if(!doc){
        	return;
        }      	
      }        
    }
    var cbdoc = document.getElementById(config.clipboardPrefix + config.encryptedDocumentId);
    if(cbdoc){
    	doc = cbdoc;
    }
    url = doc.getAttribute("nbLastUrl");
    query = doc.getAttribute("nbLastQuery");
    iFrameId = doc.getAttribute("nbIFrameId");
  }
  if (!query)
    query = "";
  if (query.indexOf("js=") == -1)
    query += "&js=e";
  query += "&encNum=" + config.encryptedNumber; 
  if (config.encryptedDocumentId)
    query += "&encId=" + config.encryptedDocumentId;
  var expanded = config.element.getAttribute("nbExpanded");
  var target = document.getElementById(config.targetId);
  var original = document.getElementById(config.targetId + "_collapsed");
  if ("true" == expanded)
  {
    config.element.className = "nbApiExpand";
    config.element.setAttribute("nbExpanded", "false");
    target.style.display = "none";
    if (original)
    {
      original.style.display = "";
    }
  } 
  else
  {
    original.style.display = "none";
    config.element.className = "nbApiCollapse";
    config.element.setAttribute("nbExpanded", "true");
    var loaded = config.element.getAttribute("nbLoaded");
	var addlParams = "";
	if (config.hasAdditionalParams)
	{
		addlParams  = doc.getAttribute("addlParams");
	}
	
    if (loaded == "true")
    {
      target.style.display = "";
      return;
    }
    target.innerHTML = "<span class='nbApiLoading'>&nbsp;</span>Loading...";
    target.style.display = "";
    config.element.setAttribute("nbLoaded", "true");
    var callback = function()
    {
    }
    if (popup)
    {
      nbApi.loadScript({containerId: popup.id, target: config.targetId, scriptTarget:config.popupId+"_script", iFrameId: iFrameId, url: url, query: query, callbackName: "nbApi.updateTargetHTML", callback: callback, addlParams:addlParams});
    }
    else
    {
      nbApi.loadScript({target: config.targetId, scriptTarget:config.docId+"_script", url: url, query: query, callbackName: "nbApi.updateTargetHTML", callback: callback, addlParams:addlParams});
    }
  }
}

  
    



nbApi.Tip = function(){tt_Tip(arguments, null);};
nbApi.UnTip = function(force){
	tt_OpReHref();
	if(tt_aV[DURATION] < 0 && (tt_iState & 0x2))
	tt_tDurt.Timer("tt_HideInit()", -tt_aV[DURATION], true);
	else if(force || !(tt_aV[STICKY] && (tt_iState & 0x2)))
	tt_HideInit(); 
									};
nbApi.TagToTip = function(){var t2t = tt_GetElt(arguments[0]);
					 if(t2t)
					 tt_Tip(arguments, t2t); };



         


