Portal.Portlet.Sequence_ViewerReport = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created Sequence_ViewerReport");
		this.base(path, name, notifier);
		
		var notifier = Notifier.getInstance();
	    notifier.setListener(null, "CONTENT_LOADER_FINISHED", this.DownloaderFinished );   
	    notifier.setListener(null, "CONTENT_LOADER_CANCELED", this.DownloaderCancelled ); 
		
		var report = this.getValue('dopt');
		if (report == 'asn1'){
		    // no region data will be sent. Set to true so that 'Bitmask' does not wait for this message
		    Portal.Portlet.Sequence_ViewerReport.regionAvailable = true;
		} // for these reports, no messages will be sent
		else if (report != 'genbank' && report != 'genpept' && report != 'fasta' && report != 'gbwithparts' 
		&& report != 'gpwithparts'){
		    this.CreateReport();
		}
	},
	
	listen: {
	        // events
	        
	    "ShowSequenceLink<click>": function(e, target, name){
	        this.setValue('dopt', target.getAttribute('report'));
	        Portal.Portlet.Sequence_ViewerReport.displayLongRecord = true;
	        this.ReportPage();
	    },
	
        // messages
	    'SelectedRegion' : function(sMessage, oData, sSrc) {
	        console.info("Region oData=" , oData)
	    //    console.info('message received. f:'+oData.from+' to:'+ oData.to + ' itemid:' + oData.itemid); //for debug
            Portal.Portlet.Sequence_ViewerReport.from = oData.from;  
            Portal.Portlet.Sequence_ViewerReport.to = oData.to; 
            Portal.Portlet.Sequence_ViewerReport.itemid = oData.itemid;
            if (oData.master){
                 Portal.Portlet.Sequence_ViewerReport.master = oData.master;
            }
             
	        Portal.Portlet.Sequence_ViewerReport.regionAvailable = true;	        
	        if (Portal.Portlet.Sequence_ViewerReport.bitmasksAvailable){
	            this.CreateReport(); 
	        }
	    },
	    
	    'Bitmask' : function(sMessage, oData, sSrc) {
	        console.info("Customize oData=" , oData)
	     //   console.info('message received. extrafeat:' + oData.extrafeat + ',fmt_mask:' + oData.fmt_mask 
	     //       + ',strand:' + oData.strand + ',maxplex:' + oData.maxplex); //for debug
	        Portal.Portlet.Sequence_ViewerReport.extrafeat = oData.extrafeat;
	        Portal.Portlet.Sequence_ViewerReport.fmt_mask = oData.fmt_mask;
	        Portal.Portlet.Sequence_ViewerReport.strand = oData.strand;
	        Portal.Portlet.Sequence_ViewerReport.maxplex = oData.maxplex;
	        Portal.Portlet.Sequence_ViewerReport.show_sequence = oData.show_sequence;	        
	        if (oData.master){
                 Portal.Portlet.Sequence_ViewerReport.master = oData.master;
            }


	        Portal.Portlet.Sequence_ViewerReport.bitmasksAvailable = true;	        
	        if (Portal.Portlet.Sequence_ViewerReport.regionAvailable){
	            this.CreateReport(); 
	        }
	    }

	},
	
	
	CreateReport: function(){
	    // decide what type of page to show
        var PageType = this.DecideToShowReport();
        
        // if an instance of downloader was started and not finished or cancelled, cancel it before 
        // making another xml-http call to viewer or downloader
        if (Portal.Portlet.Sequence_ViewerReport.downloader == 'on'){
            Portal.Portlet.Sequence_ViewerReport.downloader = 'off';
            var notifier = Notifier.getInstance();
            notifier.Notify(null, "LOADER_CANCELED", {} ); 
        }
        // clear the current contents
        document.getElementById('maincontent').innerHTML = "";
        ContentLoader.removeCancelMessages();
        
        // create specific type of page
        if (PageType == 'report'){
            this.ViewerPage();
        }
        else if (PageType == 'downloader'){
            this.DownloaderPage();
        }
        
    },
    
    ViewerPage: function(){
        var oThis = this;   
    	
        var oPh = document.getElementById('maincontent');
        oPh.style.display = "block";
    	
    	var x = Portal.Portlet.Sequence_ViewerReport;
    	
	    // Overload remote data provider callbacks
	    x.oRemoteDataProvider.onSuccess = function(oObj) {
	    
    	    if (oObj.responseText.indexOf("OUTPUT_TOO_BIG") != -1) {
    	        oPh.innerHTML = "";
    	        oThis.DownloaderPage();
    	    } else {
    	        // render the page
	            var report = oThis.getValue('dopt');
	            var elem = ( report == "genbank" || report == "genpept" || report ==  "gbwithparts" || 
	                        report ==  "gpwithparts") ? "div" : "pre";
     	        setTimeout(function() { oPh.innerHTML = "<" + elem + ">" 
     	                                                + oObj.responseText + "</" + elem +">" ; }, 100);
    	    }
    	    
	    }
	    
	    // on error, make a second attempt at download by using the downloader
	    x.oRemoteDataProvider.onError = function(oObj) {
	        oPh.innerHTML = "";
            oThis.DownloaderPage();
	    }
	    
	    x.oRemoteDataProvider.onStart = function(oObj) {
	        // show text when started loading the page
	        oPh.innerHTML = "<div class='loading'>Loading ... " +
	            "<img src='/core/extjs/ext-2.1/resources/images/default/grid/loading.gif'" 
	            + " alt='record loading animation'/></div>";
	    }

        // create url to viewer.fcgi and call asynchronous HTTP request to viewer.fcgi

	    var sRequest = oThis.CreateUrl()
	      + "&maxdownloadsize=" + oThis.getValue('maxdownloadsize');
	    
	    x.oRemoteDataProvider.Get(sRequest);
	        
    },
    
    DownloaderPage: function(){

        //create an instance of the loading bar
        var sb = new LoadingBar();                
        //initialize the loading bar
        sb.init();
        
        //build the path to the file that we want to request
        var lFile = "/sviewer/viewer.cgi?" + this.CreateUrl();
        
        //alert("lFile=" + lFile);

        var report = this.getValue('dopt');
        var elem = ( report == "genbank" || report == "genpept" || report ==  "gbwithparts" || report ==  "gpwithparts") ? "div" : "pre";

        //create a new instance of the ContentLoader specifing the id of the output location and the file url
        var cl = new ContentLoader("maincontent", lFile);
        
        //Start the rendering of the file                                           
        Portal.Portlet.Sequence_ViewerReport.downloader = 'on'; 
        cl.startFetch(); 
    }, 
    
    // create main part of URL to viewer (don't include report param)
    CreateUrl: function(){
        // create URL to fetch report
        var x = Portal.Portlet.Sequence_ViewerReport;
        var dopt = this.getValue('dopt');
        if (x.master == 'false' && dopt.toLowerCase() == 'genbank'){ dopt = 'gbwithparts';}
        
        return "val=" + this.getValue('val')
          + "&db=" + this.getValue('db')
   	      + "&dopt=" + dopt   
          + "&extrafeat=" + x.extrafeat
          + "&fmt_mask=" + x.fmt_mask
          + "&maxplex=" + x.maxplex
          + (x.itemid > 0 ? "&itemID=" + x.itemid : "")
          + (x.from > 0 ? "&from=" + x.from : "")
          + (x.to > 0 ? "&to=" + x.to : "")
          + (x.strand == "on" ? "&strand=" + x.strand : "")
   	      + "&retmode=html"
	      + "&log$=seqview";
    },
	
	DecideToShowReport: function(){
	    var ShowReport = 'report'; //'report' | 'downloader';
	    var x = Portal.Portlet.Sequence_ViewerReport;
	    var Limit = 1 * this.getValue('maxdownloadsize');
	    var currentSize = 1 * this.getValue('SequenceSize');
	    var virtualseq = this.getValue('VirtualSequence');
	    
	    if (x.itemid || virtualseq == 'true'){
	        // do nothing, ShowReport is already set to 'report'
	    }
	    else if (x.from > 0 || x.to > 0){
	        //if either to or from or both have values, get the range size
	        if (x.from > 0 && x.to > 0){
	            currentSize = x.to - x.from;
	        } 
	        else if (x.from > 0){
	            currentSize -= x.from;
	        } 
	        else if (x.to > 0){
	            currentSize = x.to; 
	        }
	        
	        if (currentSize >= Limit){
	            ShowReport = 'downloader';
	        } 
	    }
	    else if (currentSize >= Limit){
            ShowReport = 'downloader';
        } 
	    
	    return ShowReport;
	},
	
	DownloaderFinished: function(){
        Portal.Portlet.Sequence_ViewerReport.downloader = 'off'; 
	},
	
	DownloaderCancelled: function(){
	    Portal.Portlet.Sequence_ViewerReport.downloader = 'off'; 
	}
	    
},

 
{
    oRemoteDataProvider:new RemoteDataProvider("/sviewer/viewer.fcgi?"),
    displayLongRecord: false,
    master: '',
    regionAvailable: false,
    bitmasksAvailable: false,
    extrafeat: 0,
    fmt_mask: 0,
    maxplex: 0,
    itemid: '',
    strand: '',
    to: '',
    from: '',
    downloader: 'off'
});
Portal.Portlet.Sequence_ViewerTitle = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created Sequence_ViewerTitle");
		this.base(path, name, notifier);
	},
	
	send: {
	    'RemoveUserMessage': null,
	    'AddUserMessage': null
	},
	
	listen: {
	   /* messages from message bus*/
		
		'SelectedRegion' : function(sMessage, oData, sSrc) {
            var from = oData.from;  
            var to = oData.to; 
            var itemid = oData.itemid;
            
            var unit = (this.getInput("Database") && this.getValue("Database").toLowerCase() == "protein")? "aa" : "base" ;
            var text = '';
            if (itemid != ''){
                text = 'Showing selected region.';
            }
            else if(from != '' && to != ''){
                var range = Math.abs(to - from); 
                
                if (range == 0){
                    text = 'Showing ' + unit + ' ' + from + '.';
                }
                else {
                    text = 'Showing ' + this.CustomizeUnit(range + 1, unit) + ' region from ' + unit + ' ' +
                       from + ' to ' + to + '.';
                }
            }
            else if(from != ''){
                var range = Math.abs(oData.length - from); 
                text = 'Showing ' + this.CustomizeUnit(range + 1, unit) + ' region from ' + unit + ' ' + from + '.';
            }
            else if(to != ''){ 
                text = 'Showing ' + this.CustomizeUnit(to, unit) + ' region up to ' + unit + ' ' + to + '.';
            }
            
            // update text on screen, it may be empty
            if (text == ''){
                 this.send.RemoveUserMessage({
                     name: 'SelectedRegion'
                 }); 
            }
            else{
                this.send.AddUserMessage({
                    type: 'info',
                    msg: text,
                    name: 'SelectedRegion'
               });
            }

	    },
	    
	    'AdvisoryMessage' : function(sMessage, oData, sSrc) {
	        if (oData.type == 'HiddenSequence'){
                if (oData.action == 'remove'){
                     this.send.RemoveUserMessage({
                         name: 'HiddenSequence'
                     }); 
                }
                else if (oData.action == 'add'){
                    this.send.AddUserMessage({
                        type: 'info',
                        msg: oData.text,
                        name: 'HiddenSequence'
                   });
                }
	        } // oData.type
	    }
	    
	}, //send
	
	'CustomizeUnit' : function(range, unit){
	    if (unit == "aa"){
	         return (range + " aa");
	    }
	    else if (range < 1000){
	        return (range + " bp");
	    }else if (range < 1000000) {
	        return ((range/1000).toFixed(2) + "kb");
	    }else{
	        return ((range/1000000).toFixed(2) + "Mb");
	    }
	}
	
});
Portal.Portlet.Entrez_Messages = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		this.base(path, name, notifier);
		
		this.setMsgAreaClassName();
	},
	
	listen: {
	   /* messages from message bus*/
		
		'AddUserMessage' : function(sMessage, oData, sSrc) {
		    // create new message node
		    var msgnode = document.createElement('li');
		    if (oData.type != ''){
		        msgnode.className = oData.type; 
		    }
		    if (oData.name != ''){
		        msgnode.id = oData.name; 
		    }
		    msgnode.innerHTML = oData.msg;
		    
		    // add new node as first message in message block (not ads that look like messages)
		    var parent = document.getElementById('msgportlet');
		    if (parent){
    		    var oldnode = document.getElementById(oData.name);
    		    if (oldnode){
    		        parent.removeChild(oldnode);
    		    }
    		    var firstchild = parent.firstChild;
    	        if (firstchild){
                    parent.insertBefore(msgnode, firstchild);
                }
                else{
                    parent.appendChild(msgnode);
                }
                this.setMsgAreaClassName('true');
            }
		},
		
		'RemoveUserMessage' : function(sMessage, oData, sSrc) {
		    var msgnode = document.getElementById(oData.name);
		    if (msgnode){
		        var parent = document.getElementById('msgportlet'); 
		        if (parent){
    		        parent.removeChild(msgnode);
    		        this.setMsgAreaClassName();
    		    }
		    }
		}
	}, // end listen
	
	'setMsgAreaClassName' : function(hasMsg){
        var msgarea = document.getElementById('messagearea');
	    if (msgarea){
	        var msgclass = "empty";
	        
    	    // if a message was added, hasMsg is set to true at call time to avoid checks. 
    	    // by default, hasMsg is false.
    	    if (hasMsg == 'true'){
    	        msgclass = "messagearea";
    	    }
    	    else if (msgarea.getElementsByTagName('li').length > 0){
                msgclass = "messagearea"; 
        	}
        	
            msgarea.className = msgclass;
        }
	} // end setMsgAreaClassName
});
		
		
// makes an ext js looking portlet w/o using extjs
if (typeof(noext) == 'undefined') {
    noext = {};
}
noext.parseConfig = function(n) {
    var configStr = n.getAttribute('config') || '';
    try {
        var jsonObj = eval('({' + configStr + '})');
    }
    catch(e) {
        if (window.console) {
            console.error(e.message + ' in local config object in noext.Portlet: file: ' + e.fileName + ', line ' + e.lineNumber + '.' );
        }
    }
    return jsonObj;
};
noext.getClassArray = function(n) {
    var classNameStr = n.className;
    return classNameStr.split(' ');
};
noext.getFirstHeader = function(n) {
    var fc = utils.getFirstChild(n);
    if(fc.tagName.search(/^h\d/i) != -1) {
        return fc;
    }
};
noext.Portlet = function(n){
    this.n = n;
    this.headerNode = noext.getFirstHeader(this.n);
    this.headerDiv = document.createElement('div');
    this.bodyDiv = document.createElement('div');
    utils.addClass(this.bodyDiv, 'x-panel-body');
    var configObj = noext.parseConfig(this.n);
    this.collapsible = configObj.collapsible == false ? false : true; 
    this.closeable = configObj.closeable == false ? false : true; 
    this.collapsed = configObj.collapsed == true ? true : false;
    if (this.collapsed == true) {
        utils.addClass(this.n, 'x-panel-collapsed');
        this.bodyDiv.style.display = 'none';
    }
    this.make();
};

noext.Portlet.instances = [];
noext.Portlet.ready = function() {
    var nodes = $C('port', 'class', document, 'div');
    for (var i = 0; i < nodes.length; i++) {
        var n = nodes[i];
        if (utils.hasClass(n, 'norender')) {
            continue;
        }
        portlet = new noext.Portlet(n);
    }
}
noext.Portlet.prototype = {
    make: function() {
        // put a parent aruond body content
        //var innerContainingDiv = document.createElement('div');
        utils.addClass(this.n, 'x-panel');
        //this.n.style.width = 'auto';
        utils.addClass(this.headerDiv, 'x-panel-header');
        utils.addClass(this.headerDiv, 'x-unselectable');
        this.n.insertBefore(this.headerDiv, this.headerNode);
        var header = this.n.removeChild(this.headerNode);
        var toolCloseA = document.createElement('a');
        toolCloseA.setAttribute('href', '#');
        utils.addClass(toolCloseA, 'x-tool');
        utils.addClass(toolCloseA, 'x-tool-close');
        var toggleA = document.createElement('a');
        toggleA.setAttribute('href', '#');
        utils.addClass(toggleA, 'x-tool');
        utils.addClass(toggleA, 'x-tool-toggle');
        if (this.closeable == true) {
            this.headerDiv.appendChild(toolCloseA);
        }
        if (this.collapsible) {
            this.headerDiv.appendChild(toggleA);
        }
        this.headerDiv.appendChild(header);
        utils.addClass(this.headerNode, 'x-panel-header-text');
        this.toolCloseA = toolCloseA;
        this.toggleA = toggleA;
        this.reParent();
        this.setEvents();
    }, 
    reParent: function() {
        var bodyElems = [];
        utils.insertAfter(this.n, this.bodyDiv, this.headerDiv);
        for (var i = this.n.childNodes.length -1; i >= 0; i--) {
            var fc = this.n.childNodes[i];
            if (fc != this.headerDiv && fc != this.bodyDiv) { 
                bodyElems.push(this.n.removeChild(fc));
            }
            for (var j = 0; j < bodyElems.length; j++) {
                this.bodyDiv.appendChild(bodyElems[j]);
            }
        }
    },
    setEvents: function(toggleA) {
        var that = this;
        utils.addEvent(this.toggleA, 'click', function(e) { that.toggle(e)});
        utils.addEvent(this.toolCloseA, 'click', function(e) { that.close(e)});
    }, 
    toggle: function(e) {
        utils.preventDefault(e);
        if (utils.hasClass(this.n, 'x-panel-collapsed')) {
            utils.removeClass(this.n, 'x-panel-collapsed')
            this.bodyDiv.style.display = 'block';
        } else {
            utils.addClass(this.n, 'x-panel-collapsed');
            this.bodyDiv.style.display = 'none';
        }
    },
    close: function(e) {
        utils.preventDefault(e); 
        this.n.parentNode.removeChild(this.n);
    }
};

utils.addEvent(window, 'load', noext.Portlet.ready);

Portal.Portlet.BriefLinkPageSection = Portal.Portlet.extend({

	init: function(path, name, notifier) {
	    console.info("Created BriefLinkPageSection");
		this.base(path, name, notifier);
        
        this.SetPortletName();
        if (Portal.Portlet.BriefLinkPageSection.portletname != ''){
            Portal.Portlet.BriefLinkPageSection.portletname = "." + Portal.Portlet.BriefLinkPageSection.portletname;
        }
        
        this.CreatePopNodes();
        
	},
	
	SetPortletName: function(){
	    // derived portlet should override and provide portlet name;
	},
	
	CreatePopNodes: function(){
	            var pop = cssQuery("div.brieflink div" + Portal.Portlet.BriefLinkPageSection.portletname + " ul");
        var poptype = pop[0].className;
        
        // when value is pop0
        if (poptype.match(/pop0/)){
            // no popnodes need to be created
        }
        // for all other cases try to create popnode
        else{
            // Create ElementPopper for each li.item
    		var elemid = "div.brieflink div" + Portal.Portlet.BriefLinkPageSection.portletname + " li.item";
    		var elements = cssQuery(elemid); 
    		
    		// go through the list of elements
    		for (var i = 0; i < elements.length; i++) {
                var elem = elements[i];
                var content = ""; 
                
                // when value is pop1
                if (poptype.match(/pop1/)){
                    // create pop with title + desc
                    var linktext = cssQuery("a", elem);
                    if (linktext && linktext.length > 0){
                        content += linktext[0].innerHTML + "<br/>";
                    }
                }
                // default case - when value is pop2 or anything else but pop3
                else if (!poptype || !poptype.match(/pop3/)){
                    // if visible content length is larger than 105, then show the entire text in popup,
                    // followed by any included link description.
                    var linktext = cssQuery("a", elem);
                    var note = cssQuery("p.note", elem);
                    var content = ""; 
                    var linklength = 0;
                    if (linktext && linktext.length > 0){
                       linklength += linktext[0].innerHTML.length;
                    }
                    if (note && note.length > 0){
                       linklength += note[0].innerHTML.length;
                    }
                    if (linklength > 100){
                       content += linktext[0].innerHTML + "<br/>";
                    }
                } // end default case
	           
	           
                // add Description to pop node when it exists for all popnode cases	           
	            var desc = cssQuery("p.desc", elem);
                if (desc && desc.length > 0){
                   content += desc[0].innerHTML;
                }
                
                // create popnode when content is present
                if (content && content.length > 0) {
                  var elemclass = elem.className;
                  if (!elemclass.match(/popnode/)){
                      elem.className = elemclass + " popnode";
                  }
                  new ElementPopper(elem, content);
                }
                
            } // end for 
        } // end else
	} // end CreatePopNodes function
},
{
portletname: '' 

});
//
// Java Module PopDiv_JS
//

//
// Shows and hides a div at a specific location, either (x,y), or relative to
// an element in a specific direction.
//
// Limitations:
//  - height or width must be set on the popper div, or it may not position correctly.
//
// You're probably better off using ElementPopper instead of PopDiv for hover pops. Popper handles mouse events
// correctly.
//
// Arguments:
//   divId: the id of the div to use for the popper. You can also pass a DOM node, and PopDiv will use that node.
//       The default is "popperDiv".
//   className: Add this className to the popper div. Optional--no default. This is useful for setting up default state.
//
// It's preferable to use a single PopDiv to render popups all over
// a single page. You only need as many PopDivs as the maximum number of popups you may
// ever have open at once. See ElementPopper for details.

var PopDiv = function(divId, className) {

   var innerdiv = null;
   
   // default is a string
   if (typeof(divId) == 'undefined') {
      divId = "popperDiv";
   }
   
   // Find or make popper div
   if ((this.div = document.getElementById(divId)) == null) {
   
      this.div = document.createElement("div");
      this.div.id = divId;
      this.div.className = "popper";
      if (className) {
         this.div.className += " " + className;
      }
      document.body.appendChild(this.div);
      this.div.innerHTML = "<div class='popperInnerDiv'/>";
   } else {
      utils.addClass(this.div, "popper"); // Be sure it has this
   }

   // this.innerdiv is first element of this.div
   for (this.innerdiv = this.div.firstChild;
      this.innerdiv && this.innerdiv.nodeType != 1;
      this.innerdiv = this.innerdiv.nextSibling) {
   }
   
   // If caller provided a div with no inner divs, add one.
   if (!this.innerdiv) {
       this.innerdiv = document.createElement("div");
       this.innerdiv.className = "popperInnerDiv";
       this.div.appendChild(this.innerdiv);
   }
}

PopDiv.POP_ABOVE = 0;
PopDiv.POP_RIGHT = 1;
PopDiv.POP_BELOW = 2;
PopDiv.POP_LEFT = 3;
PopDiv.POP_CLASSES = ['pop-above', 'pop-right', 'pop-below', 'pop-left'];

PopDiv.POPPED = 'popped';
PopDiv.POP_GETXY = 'pop-getxy';

PopDiv.prototype = {
   // Show it at x, y
   showAt: function(html, x, y) {

    //console.info("POP @ (" + x + ", " + y + ")");
    //console.info("----");
    
      // Set content if provided
      if (html) {
         this.innerdiv.innerHTML = html;
      }

      this.div.style.left = x + "px";
      this.div.style.top = y + "px";

      // Pop it
      utils.addClass(this.div, PopDiv.POPPED);

   },

   // Show it near element ("dir" indications direction)
   // Dimensions don't exist if element isn't shown.
   getXY: function() {
      var popped = utils.hasClass(this.div, PopDiv.POPPED);

      if (!popped) {
         utils.addClass(this.div, PopDiv.POPPED);
      }
      var dim = utils.getXY(this.div);
      if (!popped) {
         utils.removeClass(this.div, PopDiv.POPPED);
      }

      return dim;
   },

   hide: function() {
      utils.removeClass(this.div, PopDiv.POPPED);
      if (this.popClass) {
         utils.removeClass(this.div, this.popClass);
         this.popClass = null;
         this.popnode = null;
      }
   },

   // showBy shows a popper adjacent to another item.
   showBy: function(html, elem, dir) {
      var dx = 0;
      var dy = 0;

      // Already popped over a particular popnode
      if (this.popnode && (this.popnode == elem)) {
         return;
      }
      this.popnode = elem;

      // Default direction
      if (typeof(dir) != "number") {
         dir = PopDiv.POP_LEFT;
      }

      // Set class "pop-<direction>": this determines margin
      this.popClass = PopDiv.POP_CLASSES[dir];
      //console.info("dir = " + dir +", popclass = " + this.popClass);
      utils.addClass(this.div, this.popClass);
      
      // Set text so that sizing is correct
      this.innerdiv.innerHTML = html;

      // Get geometries of target and popper
      var thatdim = utils.getXY(elem);
      var popdim = this.getXY(this.div);
      //console.info("that =");
      //console.info(thatdim);
      //console.info("pop = ");
      //console.info(popdim);

      // Calculate position
      switch (dir) {
      case PopDiv.POP_ABOVE: dy = -1*popdim.h;  break;
      case PopDiv.POP_LEFT:  dx = -1*popdim.w;  break;
      case PopDiv.POP_BELOW: dy =    thatdim.h; break;
      case PopDiv.POP_RIGHT: dx =    thatdim.w; break;
      default:
        throw "PopDiv: Invalid direction: " + dir;
      }
      //console.info("Popping at: [x=" + (thatdim.x + dx) + ", y="+(thatdim.y + dy)+"]");

      this.showAt(null, thatdim.x + dx, thatdim.y + dy);
   }
};



// An ElementPopper shows a popup adjacent to a target element when the user mouses over the target.
// It handles browser events and manipulates a popper to show and hide detail data.
//
// When the mouse cursor enters the target, the content appears in the popper nearby.
//  When the mouse cursor leaves the target, the popper is hidden.
//
// This rather intense event handling code is necessary because mouseover and mouseout doesn't work
// as expected--mouseouts occur when moving over child nodes. The "relatedTarget" trick described
// by PPK (quirksmode.org) doesn't work either, because browsers sometimes drop events
// with related targets. So the only option is to capture document.onmousemove, and detect when
// the mouse either exits document.body, or moves over something that is neither the target nor
// one of its children.
//
// "popnode" is the target node
// "content" is either a string (the content to show), a DOM node (show its innerHTML), or a function.
// "config" is configuration; if it's just a number, then it's "direction" (PopDiv.POP_*); otherwise, it's a JS object with 
//     attribute/value pairs
// "popperId" is the ID to assign to the popper; default is to make one up.
//
// When used with Ext.Ncbi, the popper should initialize itself on the page a few hundred
// ms after the page loads. IE apparently creates a race condition where popper and Ext.Ncbi initialization
// throw away the event handlers on which ElementPopper depends.
//
// So in that case, instead of new ElementPopper(a,b), you want to do:
//     setTimeout(function() {new ElementPopper(a,b);}), 500);

//
var ElementPopper = function(popnode, content, config, popperId) {

    var that = this;

    // Inner functions
    // If target is popnode or one of its children, returns popnode; otherwise, null
    function getPopTarget(target) {
       while (target && !utils.hasClass(target, "popnode")) {
          target = target.parentNode;
       }
       return target;
    };

    // Use inner functions here because removeEvent requires a reference to the
    // function object passed to addEvent, and we need to use closure to maintain scope.

    // Show popper and start tracking mouse motions
    var handlePop = function(e) {
       var target = getPopTarget(utils.getTargetObj(e));
       
       if (target) {
	       // If Ajax URL is supplied, then fetch URL.
	       //   If AJAX URL is a function, the URL is the return value of the function
	       //   Otherwise AJAX URL must be a string
	       // When AJAX call succeeds, pop up the content. If there's a contentFunction, the content is
	       // the result of applying the contentFunction to the AJAX return data; otherwise, it's just
	       // the AJAX return data itself.
	       //
	       if (that.url) {
	          var url = (typeof(that.url) == 'function') ? that.url(target) : that.url;
	
	          jQuery.ajax({
	             cache: false,
	             'url': url,
	             success: function(data, textStatus) {
	                doPop(target, data, textStatus);
	             }
	          });
	       } else {
	          doPop(target);
	       }
	       
	       // Set up to listen for mouse out
	       // This is the node currently being handled; it's cleared when
	       // the user mouses out.
	       that.handlingNode = target;
	    
           utils.removeEvent(target, "mousemove", handlePop);
           utils.addEvent(document.body, "mousemove", trackMouse);
           utils.addEvent(document.body, "mouseout", trackMouse);
       }
    }
    
    var doPop = function(target, data, status) {
       
       // Pop, shift listener to document body
       if (target) {
          that.timer = setTimeout(function() {
             // If we're still waiting for a pop, do the pop.
             // We're only waiting for a pop when handlingNode is set.
             if (target == that.handlingNode) {
                that.thePopper.showBy(that.contentFunction(target, data, status), target, that.direction);
             } else {
               // Ignore the pop request, because we're no longer interested in this pop.
             }
             that.timer = null;             
          }, that.delay);

       }
    };

    // Unpop, shift listener back to popnode
    var trackMouse = function(e) {
       var target = getPopTarget(utils.getTargetObj(e));

       // If mouse left document.body, or the target's not over the popnode, hide.
       if (e.type == "mouseout" || target == null) {
          // Clear pop timer if it hasn't popped
          if (that.timer) {
             window.clearTimeout(that.timer);
             that.timer = null;
          }
          that.thePopper.hide();
          that.handlingNode = null;
          
          utils.removeEvent(document.body, "mousemove", trackMouse);
          utils.removeEvent(document.body, "mouseout", trackMouse);
          utils.addEvent(popnode, "mousemove", handlePop);
       }
    };

    // END inner functions

    //
    // ctor main
    //
     
    // Support new signature ElementPopper(element, {config...}); in that case, "content" is a config object,
    // all other arguments are ignored, and config.content must be set 
    if (typeof(content) == 'object') {
       config = content;
       content = config.content;
    }
    
    // If a content URL is defined (AJAX URL string or function that returns URL), remember it
    if (config && config.url) {
       this.url = config.url;
    }
    
    // If content is a string, make it a function that returns the string.
    // If it's a dom node, make it a function that returns the dom node's innerHTML
    // If content is undefined, then assume data is coming from AJAX callback
    if (typeof(content) == 'undefined') {
       this.contentFunction = function(target, data, status) { return data };
    } else if (typeof(content) == 'string') {
       this.contentFunction = function() { return content; }
    } else if (content.innerHTML) {
       this.contentFunction = function() { return content.innerHTML; }
    } else if (typeof(content) == 'function') {
       this.contentFunction = content; // Better be a function...
    }
    
    // Backward compatibility: "config" arg used to be "direction", so
    // interpret it as "direction" if config is a number.
    // Otherwise parse out config info
    if (typeof(config) === 'undefined') {
       config = {
          direction: PopDiv.POP_LEFT,
          delay: 0
       }
    }
    
    if (typeof(config) == 'number') {
       this.direction = config;
    } else {
       this.direction = (typeof(config.direction) != 'undefined') ? config.direction : PopDiv.POP_LEFT;
       this.delay = config.delay || 0;
    }
    this.popperId = config.popperId || popperId || ("popper" + (ElementPopper.popIndex++)); 

    // Create the popper
    this.thePopper = new PopDiv(this.popperId);
    var popdiv = $(this.popperId);
    
    // FIXME: Is this necessary? I think it's maybe wrong!
    if (popdiv) {
       utils.addClass(popdiv, "popnode");
    }

    // Get popnode if it's an id string
    if (typeof(popnode) == 'string') {
        popnode = $(popnode);
        if (!popnode) {
           throw "ElementPopper: id not found";
        }
    }
    this.popnode = popnode;
    this.timer = null; // Only exists when waiting for a pop
    
    // When the user mouses over target, pop it up
    utils.addEvent(popnode, "mousemove", handlePop);
}

ElementPopper.popIndex = 0;

ElementPopper.prototype = {
   setDelay: function(delay) {
      this.delay = delay;
   },
   getDelay: function() {
      return delay;
   }
};



Portal.Portlet.DiscoveryDbLinks = Portal.Portlet.BriefLinkPageSection.extend({
    
    init: function(path, name, notifier) {
		this.base(path, name, notifier);
	},
	
	SetPortletName: function(){
	    Portal.Portlet.BriefLinkPageSection.portletname = 'DiscoveryDbLinks';
	}
});
Portal.Portlet.Sequence_DiscoveryDbLinks = Portal.Portlet.DiscoveryDbLinks.extend({
    
    init: function(path, name, notifier) {
		this.base(path, name, notifier);
	}
});
 utils.addEvent(window, "load", function(){     
     theDEP = new DeferredElementPopper("rapopper", { 
          content: function(node) { 
               var c = cssQuery(".hidden", node);               
               if (c && c.length > 0) {
                  var label = (c[0].textContent || c[0].innerText); 
                  var cit = getCitation(node);
                  label = "<p id='htbpoptext'>" + label + "</p>";
                  if (cit && cit > ""){ // avoid "undefined" value
                      label = label + "<p id='htbpcit'>" + cit + "</p>";
                  } 
                  return label;
               }                
          }, 
          direction: PopDiv.POP_LEFT, 
          popperId: "ppPopper", 
          delay: 0 
     }); 
});

Portal.Portlet.HistoryDisplay = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created History Ad...");
		this.base(path, name, notifier);    
	},
	
	send: {
      'Cmd': null
      //'Recording': null
    },   
    
    receive: function(responseObject, userArgs) {  
         var cmd = userArgs.cmd;
         var rootNode = document.getElementById('HTDisplay'); 
         var ul = document.getElementById('activity');
         var resp = responseObject.responseText;
             
         if (cmd == 'HTOn') { 
            rootNode.className = '';    // hide all msg and the turnOn link
            try {
                // Handle timeouts
                if (responseObject.status == 408) { 
                    rootNode.className = 'HTOn'; // so that the following msg will show up
                    rootNode.innerHTML = "<p class='HTOn'>Your browsing activity is temporarily unavailable.</p>";
                    return;
                }
                   
                 // Looks like we got something...
                 resp = '(' + resp + ')';
                 var JSONobj = eval(resp);
                 
                 // Build new content (ul)
                 var newHTML = JSONobj.Activity;
                 var newContent = document.createElement('div');
                 newContent.innerHTML = newHTML;
                 var newUL = newContent.getElementsByTagName('ul')[0];
                 //alert(newHTML);
                 //alert(newContent.innerHTML);
                 //alert(newUL.innerHTML);
                 // Update content
                 rootNode.replaceChild(newUL, ul);
                 //XHR returns no activity (empty ul), e.g. activity cleared
                 if (newUL.className == 'hide')                     
                     rootNode.className = 'HTOn';  // show "Your browsing activity is empty." message
                 
            }         
            catch (e) {
                //alert('error');
                rootNode.className = 'HTOn'; // so that the following msg will show up
                rootNode.innerHTML = "<p class='HTOn'>Your browsing activity is temporarily unavailable.</p>";
           }
         }
         else if (cmd == 'HTOff') {                         
             if (ul != null) { 
                 ul.className='hide'; 
                 ul.innerHTML = ''; // clear activity
             }
             rootNode.className = 'HTOff';    // make "Activity recording is turned off." and the turnOn link show up             
         }
         else if (cmd == 'ClearHT') {     
             if ( rootNode.className == '') { //                 
                 rootNode.className = 'HTOn';  // show "Your browsing activity is empty." message                                  
                 if (ul != null) {
                     ul.className='hide'; 
                     ul.innerHTML = '';
                 }
             }            
         } 
         
    },
    
	listen: {
	  'Cmd' : function(sMessage, oData, sSrc){
			console.info("Inside Cmd in HistoryDisplay: " + oData.cmd);
			this.setValue("Cmd", oData.cmd);
	  },	  
		
      "HistoryToggle<click>" : function(e, target, name){
         //alert(target.getAttribute("cmd"));
         this.send.Cmd({'cmd': target.getAttribute("cmd")});         
         console.info("Inside HistoryToggle in HistoryDisplay: " + target.getAttribute("cmd"));
         
         var site = document.forms[0]['p$st'].value;
         var cmd =  target.getAttribute("cmd");     
               
         // Issue asynchronous call to XHR service, callback is to update the portlet output
	     var resp = xmlHttpCall(site, this.realname, cmd, {}, this.receive, {'cmd': target.getAttribute("cmd")}, this);
      }, 
      
      "HistoryOn<click>" : function(e, target, name){
         this.send.Cmd({'cmd': target.getAttribute("cmd")});
         //$PN('Pubmed_ResultsSearchController').getInput('RecordingHistory').value = 'yes';		 
         console.info("Inside HistoryOn in HistoryDisplay: " + target.getAttribute("cmd"));
                
         var site = document.forms[0]['p$st'].value;   
	     var resp = xmlHttpCall(site, this.realname, "HTOn", {}, this.receive, {'cmd': target.getAttribute("cmd")}, this);
	     //Portal.requestSubmit();
      },
      
      "ClearHistory<click>" : function(e, target, name){
         this.send.Cmd({'cmd': target.getAttribute("cmd")});	
         var site = document.forms[0]['p$st'].value;   
	     var resp = xmlHttpCall(site, this.realname, "ClearHT", {}, this.receive, {'cmd': target.getAttribute("cmd")}, this);	 
      }
    }
});

function getCitation(node) {
   // var withHistorys = cssQuery("*[title]", node); does not work!!!   
   var withTitles = $AN('title', node, '*');
   //alert(withTitles.length + " " + withTitles[0].tagName + " " + withTitles[withTitles.length-1].tagName);
   //alert(withTitles[1].getAttribute('title'));
   if (withTitles.length > 0) {      
      var p = withTitles[0];
      var s = p.getAttribute("title");
      if (s && s > "") { 
          //alert(s);
          p.setAttribute("title", "");
          node._title = s;
          for (var i = 0; i < withTitles.length; i++) {
              withTitles[i].removeAttribute("title");    
          }
      }
      /*else { //fixed in utils.js         
          s = node._title; // required for IE b/c of the bug in $AN
      }*/
   } else {
      s = node._title;
      //alert("old s "+ s);
   }
   return s;
}
// Requires: ElementPopper_JS, EventDispatcher_JS

//
// A DeferredElementPopper creates an ElementPopper on any node with a given className the first time the
// node receives a mouseover event. It collects all of the poppers it creates (in an array), and
// remembers the dispatcher rule that invokes the initialization.
//
// "content" is the same as "content" for ElementPopper (see which)
//
DeferredElementPopper = function(className, config) {
   var that = this;
   this.config = config;
   this.poppers = [];
   this.dispatcher = new EventDispatcher("mouseover", className, function(event, udata, dispatcher) {
         if (!this._popup_init) {
            this._popup_init = 1;
            that.poppers[that.poppers.length] = new ElementPopper(this, that.config.content, that.config);
         }
    });
    
}
DeferredElementPopper.prototype = {
   debug: function() {
     var s = "";
     for (var i = 0; i < this.poppers.length; i++) {
        var z = this.poppers[i];
        s += i + ": ";
        s += z.popnode.innerText + "(" + z.popnode.getAttribute("title") +"); ";
     }
     alert(s);
   }
};


//
// A EventDispatcher does a popup on demand based on a classname on an element.
// Every mouse motion on the page notifies EventDispatcher that an event occurred.
// Any time an event occurs that has one of EventDispatcher's classnames on it,
// the handlers associated with that className are executed.
//
// EventDispatcher is designed for popups, but can be used for anything; the handler
// is a generic function:
//    handler(event, udata, dispatchrule)
// <event> is the platform event object; use utils.getTargetObj() to get its target
// <udata> is the udata that was passed when that handler was set up.
// <dispatchrule> is the EventDispatcher object; it is the dictionary
// {eventType, className, handler, udata} describing the popper behavior.
//
// dispatchrule.remove() [TBD] removes the rule from the event dispatcher
//
// When the callback occurs, <this> is the target element. 

EventDispatcher = function(eventType, className, handler, udata) {

   // Start PP listening for this event
   if (!EventDispatcher.events) {
      EventDispatcher.events = {};
   }

    // If first definition for this event type, listen for it
   if (!EventDispatcher.events[eventType]) {
      EventDispatcher.events[eventType] = {};
      utils.addEvent(document.body, eventType, EventDispatcher.handleEvent);
   }
   
   // Index of all class names managed by PP
   if (!EventDispatcher.classNames[className]) {
      EventDispatcher.classNames[className] = 1;
   }

   // ev.classNames are the classes that listen for this event
   var ev = EventDispatcher.events[eventType];
   if (!ev.classNames) {
      ev.classNames = {};
   }
   
   // Add handler for this class
   if (!ev.classNames[className]) {
      ev.classNames[className] = [];
   }
   
   this.eventType = eventType;
   this.className = className;
   this.handler = handler;
   this.udata = udata || null;
   
   var c = ev.classNames[className];
   c[c.length] = this;
}

EventDispatcher.events = {}
EventDispatcher.classNames = {}

EventDispatcher.handleEvent = function(e) {

   if (!EventDispatcher.events[e.type]) { return; } // TODO: Remove event listener here
   
   var t = utils.getTargetObj(e);
   if (t.className) {
      var theClasses = t.className.split(/\s+/);
      for (var c in theClasses) {
         if (EventDispatcher.classNames[theClasses[c]]) {
            EventDispatcher.dispatch(e, t, theClasses[c]);
         }
      }
   }
}

EventDispatcher.dispatch = function(e, t, className) {
   if (!EventDispatcher.events[e.type]) return;
   var ev = EventDispatcher.events[e.type];
   if (!ev.classNames || !ev.classNames[className]) return;
   var poppers = ev.classNames[className];
   for (var i = 0; i < poppers.length; i++) {
      poppers[i].handleEvent(t, e);
   }
}

EventDispatcher.prototype = {
   handleEvent: function(target, event) {
      this.handler.call(target, event, this.udata, this);
   }
}



Portal.Portlet.Sequence_ToolsList = Portal.Portlet.BriefLinkPageSection.extend({
    
    init: function(path, name, notifier) {
        console.info("Created Sequence_ToolsList");
		this.base(path, name, notifier);
	},
	
	SetPortletName: function(){
	    Portal.Portlet.BriefLinkPageSection.portletname = 'Sequence_ToolsList';
	}
});
Portal.Portlet.Sequence_ViewerCustomizer = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created Sequence_ViewerCustomizer");
		this.base(path, name, notifier);
        
        if (document.getElementById('SCDshowmaster') && document.getElementById('SCDshowmaster').checked){
            document.getElementById('CustomizableOptions').style.display = 'none';
        }
        if (this.getInput("Customized") && this.getValue("Customized") == 'true'){
            this.send.Customized();
        }
        
        this.AdvisoryMessages('pageinit');
		this.SendData();
	},
	
    send: { 
   		'Bitmask': null,
   		'AdvisoryMessage': null,
   		'Customized': null
	},	
	
	listen: {
	        // events
	    "Customize<click>": function(e, target, name){
	        if (document.getElementById('SCDshowmaster').checked){
                document.getElementById('CustomizableOptions').style.display = 'none';
            }
            else{
                document.getElementById('CustomizableOptions').style.display = 'block';
            }
	    },
	    
	    "BasicFeatures<click>": function(e, target, name){
	        // people cannot choose selected Basic Features and any NCBI feature at the same time 
	        if (document.getElementById('SCDshowsel').checked){
                if (document.getElementById('SCDsnp')){
                    document.getElementById('SCDsnp').checked = false;
                    document.getElementById('SCDsnp').disabled = true;
                }
                if (document.getElementById('SCDcdd')){
                    document.getElementById('SCDcdd').checked = false;
                    document.getElementById('SCDcdd').disabled = true;
                }
                if (document.getElementById('SCDhprd')){
                    document.getElementById('SCDhprd').checked = false;
                    document.getElementById('SCDhprd').disabled = true;
                }
            }
            else{
                if (document.getElementById('SCDsnp')){
                    document.getElementById('SCDsnp').disabled = false;
                }
                if (document.getElementById('SCDcdd')){
                    document.getElementById('SCDcdd').disabled = false;
                }
                if (document.getElementById('SCDhprd')){
                    document.getElementById('SCDhprd').disabled = false;
                }
            }
	    },
	    
	    "ShowSeq<click>": function(e, target, name){
	        // When ShowSequence option is changed by user, unless they have previously changed it and clicked Do It,
	        // signalled by having UserChangedShowSequence set to true, set UserTouchedShowSequence to true
	        if (!Portal.Portlet.Sequence_ViewerCustomizer.UserChangedShowSequence){
	            Portal.Portlet.Sequence_ViewerCustomizer.UserTouchedShowSequence = true;
	        }
	    },
	        
	    "SetView<click>": function(e, target, name){
	    
	        // send message to indicate this portlet was used to customize features
	        this.send.Customized();
	    
	        // when DoIt is clicked, if ShowSequence was touched then change ShowSequenceChanged unless master is selected
	        if (Portal.Portlet.Sequence_ViewerCustomizer.UserTouchedShowSequence){
	            if (document.getElementById('SCDshowmaster') && document.getElementById('SCDshowmaster').checked){
                } 
                else{
                    // reset UserTouchedShowSequence so that this check is not run every time
                    Portal.Portlet.Sequence_ViewerCustomizer.UserChangedShowSequence = true;
                    Portal.Portlet.Sequence_ViewerCustomizer.UserTouchedShowSequence = false;
                }
            }
            
            
            // set values corresponding to checkboxes (so that the false values can also be received from unchecked checkboxes)
            if (document.getElementById('SCDsnp')){
                this.setValue("ShowSNPs", document.getElementById('SCDsnp').checked);
            }
            if (document.getElementById('SCDcdd')){
                this.setValue("ShowCDDs", document.getElementById('SCDcdd').checked);
            }
            if (document.getElementById('SCDhprd')){
                this.setValue("ShowHPRDs", document.getElementById('SCDhprd').checked);
            }
            if (document.getElementById('SCDshowseq')){
                this.setValue("ShowSeq", document.getElementById('SCDshowseq').checked);
            }
            if (document.getElementById('SCDstrand')){
                this.setValue("Strand", document.getElementById('SCDstrand').checked);
            }
            
            
            // Update messages
            this.AdvisoryMessages('pageupdate');
            
            // Send Customize related information
	        this.SendData();
	        
	        
	        // xml http call for logging current options. Print only if option is displayed  
	        var site = document.forms[0]['p$st'].value;
	        var basic = document.getElementById('SCDshowsel') ? 
	            (document.getElementById('SCDshowsel').checked ? document.getElementById('SCDshowsel').value : 'all') 
	            : "";
	        var snp = document.getElementById('SCDsnp') ? document.getElementById('SCDsnp').checked : "";
	        var cdd = document.getElementById('SCDcdd') ? document.getElementById('SCDcdd').checked : "";
	        var hprd = document.getElementById('SCDhprd') ? document.getElementById('SCDhprd').checked : "";
	        var seq = document.getElementById('SCDshowseq') ? document.getElementById('SCDshowseq').checked : "";
	        var strand = document.getElementById('SCDstrand') ? document.getElementById('SCDstrand').checked : "";
	        var master = document.getElementById('SCDshowmaster') ? document.getElementById('SCDstrand').checked : "";
            var args = {
                "BasicFeatures": basic,
                "ShowSNPs": snp,
                "ShowCDDs": cdd,
                "ShowHPRDs": hprd,
                "ShowSeq": seq,
                "Strand": strand,
                "MasterView": master
            };
            var resp = xmlHttpCall(site, this.path, "SetView", args, this.receive, {}, this);
	    },
	    
	        // messages
        'SelectedRegion' : function(sMessage, oData, sSrc) {
            var masterChanged = false;
            var ShowSeqChanged = false;
            
            // master
            if (oData.master && oData.master == 'false'){
                if (document.getElementById('SCDshowcustomize') && !document.getElementById('SCDshowcustomize').checked){
                    document.getElementById('SCDshowcustomize').checked = true;
                    document.getElementById('CustomizableOptions').style.display = 'block';
                    masterChanged = true;
                }
            } else if (oData.master){
                if (document.getElementById('SCDshowmaster') && !document.getElementById('SCDshowmaster').checked){
                    document.getElementById('SCDshowmaster').checked = true;
                    document.getElementById('CustomizableOptions').style.display = 'none';
                    masterChanged = true;
                }
            }
            
            // change ShowSeq only if user has not already changed it themselves
            if (document.getElementById('SCDshowseq') 
                && !Portal.Portlet.Sequence_ViewerCustomizer.UserChangedShowSequence){
    	        var max = this.getValue('MaxLenForShowSeq');
    	        
    	        if (oData.to > 0 && oData.from > 0){
    	            if ((oData.to - oData.from) < max){
    	                if (!document.getElementById('SCDshowseq').checked){
    	                    document.getElementById('SCDshowseq').checked = true;
    	                    ShowSeqChanged = true;
    	                }
    	            }
    	        } else if (oData.to > 0){
    	            if (oData.to < max && !document.getElementById('SCDshowseq').checked){
                        document.getElementById('SCDshowseq').checked = true;
                        ShowSeqChanged = true;
    	            }
    	        } else if (oData.from > 0){
    	            var seqlen = this.getValue('SeqLength');
    	            if ((seqlen - oData.from) < max && !document.getElementById('SCDshowseq').checked){
                        document.getElementById('SCDshowseq').checked = true;
                        ShowSeqChanged = true;
    	            }
    	        } 
    	        
    	        // xml http for logging to say that we changed ShowSequence if we did
    	        if (ShowSeqChanged){
    	            // ######### it's not a user change, so not logging it now
    	        }
    	        
            }// if showseq checkbox is present
            
            // send data in message
            if (masterChanged || ShowSeqChanged){
                this.AdvisoryMessages('pageupdate');
                this.SendData();
            }
	    }  
	    
        // 'ShowSequenceLinkClick' : function(sMessage, oData, sSrc) {
        // document.getElementById('SCDshowseq').checked = true;
	    //}
	},
	
	SendData: function(){
	    var url_fmt_mask = this.getInput("FmtMask") ? (this.getValue("FmtMask") *1) : 0;
	    var fmt_mask = 0;
        var extrafeat = this.getInput("extrafeat") ? (this.getValue("extrafeat") *1) : 0;
        var strand = '';
        var maxplex = 1;
        var master = '';
        
        if (document.getElementById('SCDshowmaster') && document.getElementById('SCDshowmaster').checked){
            master = 'true';
        } else{
        
            // basic features
            if (document.getElementById('SCDshowsel') && document.getElementById('SCDshowsel').checked){  
                fmt_mask += 128 + 32 + 8 + 256;
            }
            
            // ncbi features
            if (document.getElementById('SCDsnp')){
                if (document.getElementById('SCDsnp').checked){
                    extrafeat += (document.getElementById('SCDsnp').getAttribute('bitmask') * 1);
                }
            }
            if (document.getElementById('SCDcdd')){
                if (document.getElementById('SCDcdd').checked){
                    extrafeat += (document.getElementById('SCDcdd').getAttribute('bitmask') * 1);
                }
            }
            if (document.getElementById('SCDhprd')){
                if (document.getElementById('SCDhprd').checked){
                    extrafeat += (document.getElementById('SCDhprd').getAttribute('bitmask') * 1);
                }
            }
            if (this.getInput('Maxplex')){
                maxplex = this.getValue('Maxplex') * 1;
            }
            
            // sequence display
            if (document.getElementById('SCDshowseq') && !document.getElementById('SCDshowseq').checked){
                fmt_mask += 32768; // hide sequence 
                fmt_mask += 262144 // suppress CDS /translation notes 
            }
            if (document.getElementById('SCDstrand') && document.getElementById('SCDstrand').checked){
                 strand = 'on';
            }
            
            //master
            if (document.getElementById('SCDshowmaster')){
                master = 'false';
            }
            
            // add url input of fmt_mask
            fmt_mask |= url_fmt_mask ;
            
        } // if not master view selected
        
	    // send message
        this.send.Bitmask({
            'fmt_mask': fmt_mask,
            'extrafeat': extrafeat,
            'strand': strand,
            'maxplex': maxplex,
            'master': master,
            'url_fmt_mask': url_fmt_mask
        });
        
	}, // end SendData()
	
	AdvisoryMessages: function(pagestatus){
	    this.SequenceHiddenMsg(pagestatus);
	},
	
	SequenceHiddenMsg: function(pagestatus){
	    if (document.getElementById('SCDshowseq')){
	        if (pagestatus == 'pageinit' && !document.getElementById('SCDshowseq').checked){
    	        // send message
                this.send.AdvisoryMessage({
                    'type': 'HiddenSequence',
                    'action': 'add',
                    'text': "This record may be shown in an abbreviated form. Use 'Customize View' section for control."
                });
            }
            else if (pagestatus == 'pageupdate'){
                // send message
                this.send.AdvisoryMessage({
                    'type': 'HiddenSequence',
                    'action': 'remove',
                    'text': ''
                });
            }
	    }
	},
	
    receive: function(responseObject, userArgs) {
    }
    
},
{
    UserTouchedShowSequence: false,
    UserChangedShowSequence: false    
});
Portal.Portlet.Sequence_ViewerChangeRegion = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created Sequence_ViewerChangeRegion");
		this.base(path, name, notifier);
		Portal.Portlet.Sequence_ViewerChangeRegion.region = this.GetRegionOption();
		this.RegionData(Portal.Portlet.Sequence_ViewerChangeRegion.region);
	},
	
    send: { 
   		'SelectedRegion': null
	},	
	
	listen: {
	
	    "From<click>": function(e, target, name){
	        document.getElementById("crselregion").checked = true;
	        if (target.value == 'begin'){
	            target.value = '';
	        }
	    },
	    
	    "To<click>": function(e, target, name){
	        document.getElementById("crselregion").checked = true;
	        if (target.value == 'end'){
	            target.value = '';
	        }
	    },
	    
	    "From<keypress>": function(e, target, name){
	        document.getElementById("crselregion").checked = true;
	        if (target.value == 'begin'){
	            target.value = '';
	        }
	        
	        this.CheckRegionKeyPress(e, target);
	    },
	    
	    "To<keypress>": function(e, target, name){
	        document.getElementById("crselregion").checked = true;
	        if (target.value == 'end'){
	            target.value = '';
	        }
	        
	        this.CheckRegionKeyPress(e, target);
	    },
	    
	    "From<blur>": function(e, target, name){
	        if (target.value < 2){
	            target.value = 'begin';
	        }
	    },
	    
	    "To<blur>": function(e, target, name){
	        if (target.value == ''){
	            target.value = 'end';
	        }
	    },
	    
	    "SetSelRegion<click>": function(e, target, name){
	        this.UpdateRegion(target);
	    },
	    
	        // messages 
	    'Bitmask' : function(sMessage, oData, sSrc) {
	        if (oData.master && oData.master == 'true'){
	            if (!document.getElementById("crwholeregion").checked){
	                document.getElementById("crwholeregion").checked = true;
	                this.UpdateRegion();
	            }
	        }
	    }
	},
	
	CheckRegionKeyPress: function(e, target){
        if (!e){
            e = utils.fixEvent(window.event);
        }
	    if ((e.keyCode || e.which) == 13) {
	        this.UpdateRegion(target);
    	    e.returnValue = false;
        	if (e.stopPropagation != undefined)
                e.stopPropagation();   
        	if (e.preventDefault != undefined)
                e.preventDefault();   
    	    return false;
        }
	},
	
	UpdateRegion: function(target){
	    var val = this.GetRegionOption();
        if (val != Portal.Portlet.Sequence_ViewerChangeRegion.region){
            this.RegionData(val);
            // update region
            Portal.Portlet.Sequence_ViewerChangeRegion.region = val;
        }else if (val == 'sel'){
             this.RegionData(val);
        }else{
            if (this.getValue("From") != 'begin'){
                this.setValue("From", 'begin');
            }
            if (this.getValue("To") != 'end'){
                this.setValue("To", 'end');
            }
        }
        
        // xml-http call to log this click 
        var site = document.forms[0]['p$st'].value;
        var args = {
            "Region": val,
            "From": this.getValue("From"),
            "To": this.getValue("To"),
            "ItemId": this.getValue("ItemId")
        };
        var resp = xmlHttpCall(site, this.path, "SetSelRegion", args, this.receive, {}, this);
	},
	
	GetRegionOption: function(){
	    var value = '';
	    var options = this.getInputs("ChangeRegion");
	    for (var i=0; i<options.length; i++){
	        if (options[i].checked) {
				value = options[i].value;
				break;
			}
	    }
	    return value;
	},
	
	RegionData:function(region){
	    var to = '';
	    var from = '';
	    var itemid = '';
	    var max = parseInt(this.getValue('Max'));
	    var master = '';
	    
	    if (region == 'multi'){
	        itemid = this.getValue("ItemId");
	    }
	    else if (region == 'sel'){
	        // get value of From
            from = this.CheckValue(this.getValue('From'));
  
            // get the value of To
            to = this.CheckValue(this.getValue('To'));
            
            // swap if to is less than from
            if (to != '' && from != '' && to < from){
                var temp = to;
	            to = from;
	            from = temp;
            }
	    } // end else if (region == 'sel')
	    
	    if (region != 'multi' && Portal.Portlet.Sequence_ViewerChangeRegion.region == 'multi'){
	        this.setValue("ItemId", '');   
	        // hide 
            document.getElementById('crmultiregionsection').style.display = 'none';
	    }
	    
	    // update to and from values on screen 
        if (from == '' || from <= 1){
            from = '';
            this.setValue("From", 'begin');
        }
        else{
            this.setValue("From", from);
        }
        
        if (to == '' || to == max){
            to = '';
            this.setValue("To", 'end');
        }
        else{
            this.setValue("To", to);
        }
        
        // master
        if (this.getInput('Contig') && this.getValue('Contig') == 'true'){ 
            if (from != '' || to != '' || itemid != ''){
                master = 'false'; 
            } else {
                master = 'true';
            }
        }
        
        //alert("message- from:" + from + " to:" + to + " itemid:" + itemid + " master" + master);
	    // send message
        this.send.SelectedRegion({
            'from': from,
            'to': to,
            'itemid': itemid,
            'length': max,
            'master': master
        });
	    
	}, // end RegionData()
	
	CheckValue: function(val){
        var max = parseInt(this.getValue('Max'));
	    
	    // allow comma in number
        val = val.replace(/,/,'');
        val = parseInt(val);
	    // if non numeric, set to default
        if (isNaN(val)){
            val = '';
        }
        else {
            // convert to positive integer
            val = Math.round(Math.abs(val));
            // zero is same as default
            if (val == 0){
                val = '';
            }// if the value is greater than the maximum, set at max
            else if (val > max){
                val = max;
            }
        }
        
        return val;
	}, // CheckValue
	
    receive: function(responseObject, userArgs) {
    }
	
},
{
    region: 'whole'
});
Portal.Portlet.Entrez_SingleReportDisplayBar = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		console.info("Created Entrez_SingleReportDisplayBar");
		this.base(path, name, notifier);
	},
	
	send: {  
		'Cmd': null, 
		'ItemSelectionChanged': null,
		'TabCmd': null
	},
	
	listen: {
	    
	    SingleReportSaveMenu: function(sMessage, oData, sSrc){
	        this.ProcessSave(oData, sSrc);
        },
        
        SingleReportFormat: function(sMessage, oData, sSrc){	
            this.ProcessFormat(oData, sSrc);
        },
        
        SingleReportDownloadMenu: function(sMessage, oData, sSrc){	
            this.ProcessDownload(oData, sSrc);
        }
        
	},
	
	ProcessFormat: function(oData, sSrc){
	    window.location = oData.url;
	},
	
	ProcessDownload: function(oData, sSrc){
	    window.location = oData.url;
	},
	
	ProcessSave: function(oData, sSrc){
        this.setValue('Cmd', oData.cmd);
        this.send.Cmd({'cmd': 'retrieve'});
        this.send.ItemSelectionChanged({ 'id': oData.id,
                                          'selected': true});
        this.send.TabCmd({'tab': ''});
        Portal.requestSubmit();
	}
});
if (typeof(noext) == 'undefined') {
   noext = {}; 
}

noext.Menu = function(n, subMenuStyle) {
    this.n = n;
    this.subMenuStyle = (subMenuStyle == 'ext') ? 'opened-ext' : 'opened';
    this.firstLi = utils.getFirstChild(this.n);
    var children = this.firstLi.childNodes;
    for (var i = 0; i < children.length; i++) {
        var child = children[i];
        if (child.nodeType == 1 && child.tagName.toLowerCase() == 'ul') {
            this.subMenu = child;
        }
    }
    this.init();
};
/*noext.Menu.getSubmenus = function(n) {
    var submenus = [];
    for (var j = 0; j < n.childNodes.length; j++) { // loop thru all lis
        var li = n.childNodes[j];
        for (var k = 0; k < li.childNodes.length; k++) {
            var child = li.childNodes[k];
            if (child.nodeType == 1 && child.tagName && child.tagName.toLowerCase() == 'ul') {
                submenus.push(child);
            }
        }
    }
    return submenus;
}; */

noext.Menu.ready = function(subMenuStyle) {
    // default style is ext js like, simple is more like google
    var menus =  $C('mb', 'class');
    noext.Menu.menuInstances = [];
    noext.Menu.menus = menus; // remember all the menus on the page
    for (var i = 0; i < menus.length; i++) {
        var ul = menus[i];
        var firstLi = utils.getFirstChild(ul);
        var anchor = utils.getFirstChild(firstLi); 
        var anchorContent = anchor.innerHTML;
        utils.removeChildren(anchor);
        var smallElem = document.createElement('u');
        smallElem.appendChild(document.createTextNode(anchorContent));
        anchor.appendChild(smallElem);
        utils.addClass(anchor, 'first-link');
        //insert delta char after anchor
        var delta = document.createTextNode('\u25BC');
        var deltaSpan = document.createElement('span');
        deltaSpan.setAttribute('class', 'delta');
        deltaSpan.appendChild(delta);
        anchor.appendChild(deltaSpan);
        //utils.insertAfter(anchor.parentNode, deltaSpan, anchor);
        // insert button after
        //var btn = document.createElement('button');
        //btn.appendChild(document.createTextNode(firstChildElem.innerHTML));
        //firstChildElem.parentNode.insertBefore(btn, firstChildElem);
        //firstLi.removeChild(firstChildElem);
        var menu = new noext.Menu(ul, subMenuStyle);
        noext.Menu.menuInstances.push(menu); // remember all instances
    }
};

noext.Menu.prototype = {
    init: function() {
        var that = this;
        utils.addClass(this.subMenu, 'submenu');
        // add classes to all submenus items
        for (var i = 0; i < this.subMenu.childNodes.length; i++) {
            var child = this.subMenu.childNodes[i];
            if (child.nodeType == 1 && child.tagName.toLowerCase() == 'li') {
                utils.addClass(child, 'submenu-item');
                // add rollover classes to all submenu items
                utils.addEvent(child, 'mouseover', this.hover);
                utils.addEvent(child, 'mouseout', this.unHover);
            }
        }
        
        utils.addEvent(this.n, 'click', function(event) { that.update(event)});
        utils.addEvent(document, 'click', function(event) { that.closeAllSubMenus(event)});
    },
    update: function(e) {
        // set up events needed
            var t = utils.getTargetObj(e);
            // if target is sub menu item, return out of function, otherwise clicking on item will trigger update on submenu
            /* if (utils.hasClass(t.parentNode, 'submenu-item') || utils.hasClass(t, 'delta')) {
                return;
            }*/
            var sm = this.subMenu;
            // close other menus
            this.closeOtherSubMenus(sm);
       
            var subMenuStyle = this.subMenuStyle;
            if (utils.hasClass(sm, subMenuStyle) == false) {
                utils.addClass(sm, subMenuStyle);
            } else {
                utils.removeClass(sm, subMenuStyle);
            }
            e.stopPropagation(); // don't let default event occur (removeMenus from document.click)

    },
    hover: function() {
        utils.addClass(this, 'active-item');

    },
    unHover: function() {
        utils.removeClass(this, 'active-item');

    },
    closeOtherSubMenus: function(currentSubMenu) {
        // close all menu's submenus, that are different than currently clicked menu's submenu
        var subMenuStyle = this.subMenuStyle;
        var currentSubMenu = this.subMenu;
        var menuInstances = noext.Menu.menuInstances;
        for (var i = 0; i < menuInstances.length; i++) {
            var menuInstance = menuInstances[i];
            if (currentSubMenu != menuInstance.subMenu && utils.hasClass(menuInstance.subMenu, subMenuStyle)) {
                utils.removeClass(menuInstance.subMenu, subMenuStyle);
            } 
        }
    },
    closeAllSubMenus: function(event) {
        // for when user clicks on document
        var menuInstances = noext.Menu.menuInstances;
        for (var i = 0; i < menuInstances.length; i++) {
            var subMenu = this.subMenu;
            utils.removeClass(subMenu, this.subMenuStyle);
        }
    }
};
utils.addEvent(window, 'load', noext.Menu.ready);

Portal.Portlet.Sequence_SingleReportDisplayBar = Portal.Portlet.Entrez_SingleReportDisplayBar.extend({

	init: function(path, name, notifier) {
		console.info("Created Sequence_SingleReportDisplayBar");
		this.base(path, name, notifier);
	},
	
	listen: {
	    
	    SingleReportSaveMenu: function(sMessage, oData, sSrc){
	        this.ProcessSave(oData, sSrc);
        },
        
        SingleReportFormat: function(sMessage, oData, sSrc){	
            this.ProcessFormat(oData, sSrc);
        },
        
        SingleReportDownloadMenu: function(sMessage, oData, sSrc){	
            this.ProcessDownload(oData, sSrc);
        },
        
        'SelectedRegion' : function(sMessage, oData, sSrc) {
            Portal.Portlet.Sequence_SingleReportDisplayBar.regionUrlSeg = 
                (oData.itemid > 0 ? "&itemID=" + oData.itemid : "")
                + (oData.from > 0 ? "&from=" + oData.from : "")
                + (oData.to > 0 ? "&to=" + oData.to : "");
        },
        
        'Bitmask' : function(sMessage, oData, sSrc) {
            Portal.Portlet.Sequence_SingleReportDisplayBar.optionsUrlSeg =
            "&extrafeat=" + oData.extrafeat
            + (oData.fmt_mask > 0 ? "&fmt_mask=" + oData.fmt_mask: "")
            + (oData.maxplex.toString() != '' ? "&maxplex=" + oData.maxplex : "")
            + (oData.strand == "on" ? "&strand=" + oData.strand : "");
            
            // fmt_msk already contains fmt_mask summed from customize portlet + any value from URL.
            // Still the fmt_mask from URL is passed along separately so it could be sent when there is no
            // need to send other options because Customize portlet was not changed from default state.
            if (oData.url_fmt_mask > 0){
                Portal.Portlet.Sequence_SingleReportDisplayBar.urlFmtMaskUrlSeg = "&fmt_mask=" + oData.url_fmt_mask;               
            }
            
            // to be used for sending to graph only
            if (oData.strand == "on" ){
                Portal.Portlet.Sequence_SingleReportDisplayBar.optionsUrlSegForGraph = "&strand=true";
            }
        },
        
        'Customized' : function(sMessage, oData, sSrc) {
            Portal.Portlet.Sequence_SingleReportDisplayBar.customized = true;
        }
        
	},
	
	ProcessFormat: function(oData, sSrc){
	    // if Customize portlet was used and the new report will have a customize portlet, then 
	    //submit the form to get all parameters from POST, otherwise go to the URL
	    if (Portal.Portlet.Sequence_SingleReportDisplayBar.customized && 
	        oData.report.match(/fasta|genbank|genpept|asn1|gbwithparts|gpwithparts/)){ 
	        this.setValue("Report", oData.report);
	        this.send.ItemSelectionChanged( { 'id': oData.id,
			                                  'selected': true });
			this.send.Cmd({'cmd': 'retrieve'});
	        Portal.requestSubmit();
	    }
	    // If it is the graphical report and user has customized the current report, send strand only.
	    else if (Portal.Portlet.Sequence_SingleReportDisplayBar.customized && oData.report.match(/graph/)){
            window.location = oData.url + Portal.Portlet.Sequence_SingleReportDisplayBar.regionUrlSeg +
	            Portal.Portlet.Sequence_SingleReportDisplayBar.optionsUrlSegForGraph;
	    }
	    /* for later .... 
	    // if current report is graph, send all options in URL
	    else if (this.getValue("Report") == 'graph'){
	          window.location = oData.url + Portal.Portlet.Sequence_SingleReportDisplayBar.regionUrlSeg + 
	             Portal.Portlet.Sequence_SingleReportDisplayBar.optionsUrlSeg;
	    }
	    // customize portlet cannot accept extra_feat or maxplex currently. Maxplex can be easily added but extrafeat 
	    // will be difficult because we do not know how to resolve it with current selections. Maybe choose 'master view'
	    // when options are from URL?. 
	    */
	    // For reports that do not have a customized portlet -it means it cannot understand the customizations, 
	    // so, no need to send them the bitmask related data. On the other hand, for reports that have a 
	    // customize portlet, but there were no customizations, they can use the default settings. Do pass
	    // along any fmt_mask parameter from the URL though.
	    else
	        window.location = oData.url + Portal.Portlet.Sequence_SingleReportDisplayBar.regionUrlSeg +
	            Portal.Portlet.Sequence_SingleReportDisplayBar.urlFmtMaskUrlSeg;
	},
	
	ProcessDownload: function(oData, sSrc){
	    var dlurl = oData.url + Portal.Portlet.Sequence_SingleReportDisplayBar.regionUrlSeg
	        + Portal.Portlet.Sequence_SingleReportDisplayBar.optionsUrlSeg;
	    
	    // for asn1 & xml only, set maxplex=0
	    if (dlurl.match(/asn1|xml/) && !(dlurl.match(/gbc_xml|gpc_xml|fasta_xml/))){
	        dlurl = dlurl.replace(/maxplex=\d/,'maxplex=0');
	    }
	    else if (dlurl.match(/maxplex=0/)){
	         dlurl = dlurl.replace(/maxplex=0/,'maxplex=1');
	    }
	    
	    window.location = dlurl; 
	}
	
},

{
    regionUrlSeg: '',
    optionsUrlSeg: '',
    urlFmtMaskUrlSeg: '',
    optionsUrlSegForGraph: '',
    customized: false
});
Portal.Portlet.MessageBar = Portal.Portlet.extend ({
	init: function (path, name, notifier)
	{
		this.base (path, name, notifier);
	},
	
	send: {
		"TabCmd": null,
		"Cmd": null,
		"Term": null
	},
	
	listen: {
		//upon clicking 'see details', sends name of details tab.
		"Details<click>":  function(e, target, name) {
			this.send.TabCmd({'tab': this.getValue("Details:tab")});
		},
		
		"CorrectedQuery<click>": function(e, target, name) {
		    this.send.Term ({'term': target.getAttribute('term')})
		    this.send.Cmd ({'cmd': 'CorrectSpelling'})
		}
	}
});


Portal.Portlet.SearchController = Portal.Portlet.extend({
   
   init: function(path, name, notifier) {
      console.info("Created SearchController");
      this.base(path, name, notifier);
   },
   
   listen: {
   
		'QueryKey' : function(sMessage, oData, sSrc) {
			this.ReceivedQueryKey(sMessage, oData, sSrc);
		},
        
        'scTerm' : function(sMessage, oData, sSrc) {
			this.ReceivedTerm(sMessage, oData, sSrc);
        }
    },
   
    ReceivedQueryKey : function (sMessage, oData, sSrc){
       this.setValue('QueryKey', oData.qk);
    },
   
    ReceivedTerm : function (sMessage, oData, sSrc){
       this.setValue('Term', oData.term);
    }
});

Portal.Portlet.CommandTab = Portal.Portlet.extend ({
	init: function (path, name, notifier)
	{
		 console.info ("Created CommandTab");
		this.base (path, name, notifier);
	},
	
	send: {
		"Cmd": null,
		"TabCmd": null
	},
	
	listen: {
		//upon click on tab, sends name of tab that was clicked,
		// and the command that indicates that a tab was clicked.
		"Tab<click>":  function(e, target, name) {
			this.send.TabCmd({'tab': target.getAttribute("tab")});
			this.send.Cmd({'cmd': 'CommandTabClicked'});
		},
		
	
		"LimitCheckBox<click>":  function(e, target, name) {
			console.info ("LimitsActive: " + this.getValue("LimitsActive"));
			if (target.checked){
				this.setValue("LimitsActive", 'true');
				console.info ("LimitsActive: " + this.getValue("LimitsActive"));
			}
			else{
				this.setValue("LimitsActive", 'false');
				console.info ("LimitsActive: " + this.getValue("LimitsActive"));
			}
				
		},
		
		'PreserveTabCmd' : function(e, target, name) {
			this.send.TabCmd({'tab': this.getValue("LastTabCmd")});	
		}
	}
});

Portal.Portlet.DbConnector = Portal.Portlet.extend({

	init: function(path, name, notifier) {
		var oThis = this;
		console.info("Created DbConnector");
		this.base(path, name, notifier);
		
		// reset Db value to original value on page load. Since LastDb is the same value as Db on page load and LastDb is not changed on
		// the client, this value can be used to reset Db. This is a fix for back button use.
		if (this.getValue("Db") != this.getValue("LastDb")){
		    this.setValue("Db", this.getValue("LastDb"));
		}
     
		// the SelectedIdList and id count from previous iteration (use a different attribute from IdsFromResult to prevent back button issues)
		Portal.Portlet.DbConnector.originalIdList = this.getValue("LastIdsFromResult");
		console.info("originalIdList " + Portal.Portlet.DbConnector.originalIdList);
		// if there is an IdList from last iteration set the count
		if (Portal.Portlet.DbConnector.originalIdList != ''){
			Portal.Portlet.DbConnector.originalCount = Portal.Portlet.DbConnector.originalIdList.split(/,/).length;
		}

		notifier.setListener(this, 'HistoryCmd', 
        	function(oListener, custom_data, sMessage, oNotifierObj) {
           		var sbTabCmd = $N(oThis.path + '.TabCmd');
           		sbTabCmd[0].value = custom_data.tab;
        	}
    		, null);
    
	},

	send: {
   		'SelectedItemCountChanged': null,
   		'newUidSelectionList': null,
   		'SavedSelectedItemCount': null
	},

	listen: {
	
		//message from Display bar on Presentation change 
		'PresentationChange' : function(sMessage, oData, sSrc){
			
			// set link information only if it exists
			if (oData.dbfrom){
				console.info("Inside PresentationChange in DbConnector: " + oData.readablename);
				this.setValue("Db", oData.dbto);
				this.setValue("LinkSrcDb", oData.dbfrom);
				this.setValue("LinkName", oData.linkname);
				this.setValue("LinkReadableName", oData.readablename);
			}
			//document.forms[0].submit();
		},
		
		// various commands associated with clicking different form control elements
		'Cmd' : function(sMessage, oData, sSrc){
			console.info("Inside Cmd in DbConnector: " + oData.cmd);
			this.setValue("Cmd", oData.cmd);
			
			// back button fix, clear TabCmd
			if (oData.cmd == 'Go' || oData.cmd == 'PageChanged' || oData.cmd == 'FilterChanged' || 
			oData.cmd == 'DisplayChanged' || oData.cmd == 'HistorySearch' || oData.cmd == 'Text' || 
			oData.cmd == 'File' || oData.cmd == 'Printer' || oData.cmd == 'Order' || 
			oData.cmd == 'Add to Clipboard' || oData.cmd == 'Remove from Clipboard' || 
			oData.cmd.toLowerCase().match('details')){
				this.setValue("TabCmd", '');
				console.info("Inside Cmd in DbConnector, reset TabCmd: " + this.getValue('TabCmd'));
			}

		},
		
		
		// the term to be shown in the search bar, and used from searching
		'Term' : function(sMessage, oData, sSrc){
			console.info("Inside Term in DbConnector: " + oData.term);
			this.setValue("Term", oData.term);
		},
		
		
		// to indicate the Command Tab to be in
		'TabCmd' : function(sMessage, oData, sSrc){
			console.info("Inside TABCMD in DbConnector: " + oData.tab);
			this.setValue("TabCmd", oData.tab);
			console.info("DbConnector TabCmd: " + this.getValue("TabCmd"));
		},
		
		
		// message sent from SearchBar when db is changed while in a Command Tab
		'DbChanged' : function(sMessage, oData, sSrc){
			console.info("Inside DbChanged in DbConnector");
			this.setValue("Db", oData.db);
		},
		
		// Handles item select/deselect events
		// Argument is { 'id': item-id, 'selected': true or false }
		'ItemSelectionChanged' : function(sMessage, oData, oSrc) {
			var sSelection = this.getValue("IdsFromResult");
			var bAlreadySelected = (new RegExp("\\b" + oData.id + "\\b").exec(sSelection) != null);
	       	var count =0;
	       	
			if (oData.selected && !bAlreadySelected) {
				sSelection += ((sSelection > "") ? "," : "") + oData.id;
			   	this.setValue("IdsFromResult", sSelection);
			   	if (sSelection.length > 0){
			   		count = sSelection.split(',').length;
			   	}
			   	this.send.SelectedItemCountChanged({'count': count});
			   	this.send.newUidSelectionList({'list': sSelection});
		   	} else if (!oData.selected && bAlreadySelected) {
				sSelection = sSelection.replace(new RegExp("^"+oData.id+"\\b,?|,?\\b"+oData.id+"\\b"), '');
		   	   	this.setValue("IdsFromResult", sSelection);
				console.info("Message ItemSelectionChanged - IdsFromResult after change:  " + this.getValue("IdsFromResult"));
			   	if (sSelection.length > 0){
			   		count = sSelection.split(',').length;
			   	}
				console.info("Message ItemSelectionChanged - IdsFromResult length:  " + count);   
				this.send.SelectedItemCountChanged({'count': count});
			   	this.send.newUidSelectionList({'list': sSelection});
		   	}
		},
				
		// FIXME: This is the "old message" that is being phased out.
		// when result citations are selected, the list of selected ids are intercepted here,
		// and notification sent that selected item count has changed.
		'newSelection' : function(sMessage, oData, sSrc){
		
			// Check if we already have such IDs in the list
			var newList = new Array();
			var haveNow = new Array();
			if(Portal.Portlet.DbConnector.originalIdList){
				haveNow = Portal.Portlet.DbConnector.originalIdList.split(',');
				newList = haveNow;
			}
			
			var cameNew = new Array();
			if (oData.selectionList.length > 0) {
				cameNew = oData.selectionList;
			}
			
			if (cameNew.length > 0) {
				for(var ind=0;ind<cameNew.length;ind++) {
					var found = 0;
					for(var i=0;i<haveNow.length;i++) {
						if (cameNew[ind] == haveNow[i]) {
							found = 1;
							break;
						}
					}
						//Add this ID if it is not in the list
					if (found == 0) {
						newList.push(cameNew[ind]);
					}
				}
			}
			else {
				newList = haveNow;
			}

				// if there was an IdList from last iteration add new values to old
			var count = 0;
			if ((newList.length > 0) && (newList[0].length > 0)){
				count = newList.length;
			}
			
			console.info("id count = " + count);
			this.setValue("IdsFromResult", newList.join(","));
			
			this.send.SelectedItemCountChanged({'count': count});
			this.send.newUidSelectionList({'list': newList.join(",")});
		},


		// empty local idlist when list was being collected for other purposes.
		//used by Mesh and Journals (empty UidList should not be distributed, otherwise Journals breaks)
		// now used by all reports for remove from clipboard function.
		'ClearIdList' : function(sMessage, oData, sSrc){
			this.setValue("IdsFromResult", '');
			this.send.SelectedItemCountChanged({'count': '0'});
			this.send.newUidSelectionList({'list': ''});
		}, 


		// back button fix: when search backend click go or hot enter on term field,
		//it also sends db. this db should be same as dbconnector's db
		'SearchBarSearch' : function(sMessage, oData, sSrc){
			if (this.getValue("Db") != oData.db){
				this.setValue("Db", oData.db);
			}
		},
		
		// back button fix: whrn links is selected from DisplayBar,
		//ResultsSearchController sends the LastQueryKey from the results on the page
		// (should not be needed by Entrez 3 code)
		'LastQueryKey' : function(sMessage, oData, sSrc){
			if (this.getInput("LastQueryKey")){
				this.setValue("LastQueryKey", oData.qk);
			}
		},
		
		'QueryKey' : function(sMessage, oData, sSrc){
			if (this.getInput("QueryKey")){
				this.setValue("QueryKey", oData.qk);
			}
		},
		
		
		//ResultsSearchController asks for the initial item count in case of send to file 
		'needSavedSelectedItemCount' : function(sMessage, oData, sSrc){
			var count = 0;
			if(this.getInput("IdsFromResult")){
				if (this.getValue("IdsFromResult").length > 0){
					count = this.getValue("IdsFromResult").split(',').length;
				}
				console.info("sending SavedSelectedItemCount from IdsFromResult: " + count);
			}
			else{
				count = Portal.Portlet.DbConnector.originalCount;
				console.info("sending SavedSelectedItemCount from OriginalCount: " + count);
			}
			this.send.SavedSelectedItemCount({'count': count});
		},
		
		// Force form submit, optionally passing db, term and cmd parameters
		'ForceSubmit': function (sMessage, oData, sSrc)
		{
		    if (oData.db)
    			this.setValue("Db", oData.db);
		    if (oData.cmd)
    			this.setValue("Cmd", oData.cmd);
		    if (oData.term)
    			this.setValue("Term", oData.term);
    		Portal.requestSubmit ();
		},
		
		'LinkName': function (sMessage, oData, sSrc){
		    this.setValue("LinkName", oData.linkname);
		}
		
	}, //listen
	
	/* other portlet functions */
	
	// DisplayBar in new design wants selected item count
	'SelectedItemCount': function(){
	    var count = 0;
		if(this.getInput("IdsFromResult")){
			if (this.getValue("IdsFromResult") != ''){
				count = this.getValue("IdsFromResult").split(',').length;
			}
		}
		else{
			count = Portal.Portlet.DbConnector.originalCount;
		}
		return count;
	}
		
},
{
	originalIdList: '',
	originalCount: 0
});

function getEntrezSelectedItemCount() {
    return $PN('DbConnector').SelectedItemCount();
}

Portal.Portlet.SearchBar = Portal.Portlet.extend ({
  
	init: function (path, name, notifier) {
		console.info ("Created SearchBar"); 
		this.base (path, name, notifier);

        Portal.Portlet.SearchBar.originalTerm = this.getValue("Term");
        Portal.Portlet.SearchBar.originalDb = this.getValue("Db");
        
        // listen for autocomplete selection event 
        var AutoCompSelectFnc = function(){ 
            Portal.$send('AutoCompSelect'); 
        } 
        jQuery("#search_term").bind("ncbiautocompleteenter", AutoCompSelectFnc ).bind("ncbiautocompleteoptionclick", AutoCompSelectFnc ); 

	},

	// Define message sender methods here.
	// If you just want to send a message the standard way, simply
	// supply "null" as the implementation. 
	// 
	send: {
		"Cmd": null,
		"Term": null,
		"TabCmd": null,
		"DbChanged": null, 
		"SearchBarSearch": null,
		"AutoCompSelect": null
	},

	// Define messages and events
	listen: {

		// Global message bus
		'IndexLogicalOp': function(sMessage, oData, sSrc) {
		    this.ReceivedIndexLogicalOp(sMessage, oData, sSrc);
		},

		'TermLogicalOp': function(sMessage, oData, sSrc) {
		    this.ReceivedTermLogicalOp(sMessage, oData, sSrc);
		},

		'LimitsGoClicked': function(sMessage, oData, sSrc) {
            this.ReceivedLimitsGoClicked(sMessage, oData, sSrc);
		},

		'Cmd': function(sMessage, oData, sSrc) {
		    this.ReceivedCmd(sMessage, oData, sSrc);
		},
		
		'AppendTerm': function(sMessage, oData, sSrc) {
		    this.ReceivedAppendTerm(sMessage, oData, sSrc);
		},
		
		'ClearSearchBarTerm': function(sMessage, oData, sSrc) {
			this.setValue("Term", '');
		}, 
		
		'AutoCompleteControl': function(sMessage, oData, sSrc) {
		    this.ChangeAutoCompleteState(sMessage, oData, sSrc);
        },
        
        'AutoCompSelect': function(sMessage, oData, sSrc) {
		    this.AutoCompleteOptionSelected();
        },
		
		// Browser events
		"Term<keypress>": function(event, target, name) {
		    this.TermKeyPress(event, target, name);
		},
      
		// Cmd is set to Go, so ResultsView of other database can choose component based 
		// on value of Cmd. The existing search term is also passed down.
		"Go<click>": function(e, target, name) {
            this.GoClick(e, target, name);
		},
		
		"Preview<click>": function(e, target, name) {
		 	this.PreviewClick(e, target, name);
		},
	  
		// to indicate that the db field was the submitter, this will also set future 
		// TabCmd to current TabCmd and keep the existing search term.
		"Db<change>": function(e, target, name) {
		    this.DbChange(e, target, name);
		},
		
		// On Clear button click, set focus to search box and clear the term
		"Clear<click>": function (e, target, name) {
		    this.ClearClick(e, target, name);
		}
    
		
	}, //listen
	
	/* other functions */
	
	'ReceivedIndexLogicalOp': function(sMessage, oData, sSrc){
		var sbTerm = this.getValue("Term");
		var oTerm = this.getInput("Term");
		var newValue = oData.key;

		console.info ("In Indexlogocalop in SearchBar");

		if (sbTerm != "") {
			newValue = sbTerm + ' ' + oData.op + ' ' + oData.key;
		}
		this.setValue("Term", newValue);
		if (oTerm) {
		   oTerm.focus();
		}
	},
	
	'ReceivedTermLogicalOp': function(sMessage, oData, sSrc){
	    var sbTerm = this.getValue("Term");
		var bNotBlank = sbTerm != "";
		
		if (bNotBlank)
		   sbTerm = '(' + sbTerm + ') ' + oData.op + ' (';
		
		sbTerm += oData.key;
		
		if (bNotBlank)
		   sbTerm += ')';
 
		this.setValue("Term", sbTerm);  
		this.send.Term({'term' : sbTerm});
		this.getInput("Term").focus();
	},
	
	'ReceivedLimitsGoClicked': function(sMessage, oData, sSrc){
		this.send.Cmd({ 'cmd' : this.getValue("Go:cmd") });
		this.send.Term({ 'term' : this.getValue("Term") });
		// for back button fix, when go is clicked, also send db in searchbar, for checking against dbconnector
		this.send.SearchBarSearch({ 'db' : this.getValue("Db") });
		Portal.requestSubmit();
	},
	
	'ReceivedCmd': function(sMessage, oData, sSrc){
	    if (oData.cmd == 'CommandTabClicked'){
	        this.send.Term({'term': this.getValue("Term") });
			// for back button fix, when Tab is clicked, also send db in searchbar
			if (this.getValue("Db")){
				this.send.DbChanged({ 'db' : this.getValue("Db") });
			}
		}
	},
	
	'ReceivedAppendTerm': function(sMessage, oData, sSrc){
	    var newTerm = Portal.Portlet.SearchBar.originalTerm;
	    if (Portal.Portlet.SearchBar.originalTerm != '' && oData.op != ''){
	        newTerm += ' ' + oData.op + ' ';
	    }
	    newTerm += oData.term;
	    //this.setValue("Term", newTerm); 
		this.send.Term({'term': newTerm });
		// for back button fix, send original db
		this.send.SearchBarSearch({ 'db' : Portal.Portlet.SearchBar.originalDb });
	},
	
	'TermKeyPress': function(event, target, name){
	    event = event || utils.fixEvent (window.event);
		if ((event.keyCode || event.which) == 13) 
		{
			// Emulate Go command.
    
			console.info ("In term keypress: CMD  in SearchBar");
			this.send.Cmd({ 'cmd' : this.getValue("Term:cmd") });

			// In History Tab, Term field should send command Preview
			if (this.getValue("Term:cmd") == 'Preview')
			{
				this.send.TabCmd({ 'tab' : this.getValue("Db:tab") });
			}
    
			console.info ("In term keypress : TERM in SearchBar");
			this.send.Term({ 'term': this.getValue("Term") });

		    // for back button fix, when go is clicked, also send db in searchbar, for checking against dbconnector
		    this.send.SearchBarSearch({ 'db' : this.getValue("Db") });
        
			event.returnValue = false;
			if (event.stopPropagation != undefined)
                  event.stopPropagation ();   
			if (event.preventDefault != undefined)
                  event.preventDefault ();   
			Portal.requestSubmit (); 
			return false;
		}
	},
	
	'GoClick': function(e, target, name){
	 	this.send.Cmd({ 'cmd' : this.getValue("Go:cmd") });
	   	this.send.Term({ 'term' : this.getValue("Term") });
		// for back button fix, when go is clicked, also send db in searchbar, for checking against dbconnector
	 	this.send.SearchBarSearch({ 'db' : this.getValue("Db") });
	},
	
	'PreviewClick': function(e, target, name){
        this.send.Cmd({ 'cmd' : 'Preview' });
	   	this.send.Term({ 'term' : this.getValue("Term") });
	   	this.send.TabCmd({ 'tab' : this.getValue("Preview:tab") });
		// for back button fix, when go is clicked, also send db in searchbar, for checking against dbconnector
	 	this.send.SearchBarSearch({ 'db' : this.getValue("Db") });
	},
	
	'DbChange': function(e, target, name){
	 	this.send.Cmd({ 'cmd' : this.getValue("Db:cmd") });
		console.info ("In DB SearchBar: " + this.getValue("Db:cmd"));
		this.send.TabCmd({ 'tab' : this.getValue("Db:tab") });
		this.send.DbChanged({ 'db' : this.getValue("Db") });
		this.send.Term({ 'term' : this.getValue("Term") });
		
		if (this.getValue("Db:cmd") == ''){
    		if (this.getValue("Term:suggest") == 'true'){
    		    // change to if the current database has a dictionary 
    		    this.EnableDisableAutocomplete();
            }
        }
	},
	
	'ClearClick': function(e, target, name){
	    this.setValue ("Term", "");
        var term = this.getInput ("Term");
        if (term) 
            term.focus ();
    },
    
    'ChangeAutoCompleteState': function(sMessage, oData, sSrc){
        this.setValue("Term:suggest", 'false');
        var site = document.forms[0]['p$st'].value;
        var resp = xmlHttpCall(site, this.realname, "AutoCompleteControl", {"ShowAutoComplete": 'false'}, this.receive, {}, this);
    },        
        
    'receive': function(responseObject, userArgs) {
    },
    
    'AutoCompleteOptionSelected': function(){
        /*if (this.getInput("AutoSuggestUsed")){
            this.setValue("AutoSuggestUsed", 'true');
        }*/
        
	    this.send.Cmd({ 'cmd' : this.getValue("Go:cmd") });
	   	this.send.Term({ 'term' : this.getValue("Term") });
		// for back button fix, when go is clicked, also send db in searchbar, for checking against dbconnector
	 	this.send.SearchBarSearch({ 'db' : this.getValue("Db") });
	 	Portal.requestSubmit(); 
	},
	
	'EnableDisableAutocomplete': function(){
	    var site = document.forms[0]['p$st'].value;
        var resp2 = xmlHttpCall(site, this.realname, "SetAutoCompleteDictionary", {"Db": this.getValue("Db")}, this.receiveDictionary, {}, this);
	},
	
	'receiveDictionary': function(responseObject, userArgs){ 
        try {
            // deserialize the string with the JSON object
            var response = '(' + responseObject.responseText + ')';
            var JSONobject = eval(response);
            
            var dict = JSONobject.Dictionary || "";
            
            // turn autocomplete off or on if database is changed in selector.
            if(dict != ''){
               jQuery("#search_term").ncbiautocomplete("option","isEnabled",true).ncbiautocomplete("option","dictionary",dict);
            }
            else{
               jQuery("#search_term").ncbiautocomplete("turnOff",true);    
            }
        }
        catch (e){
        
        }
    }
	
}, // portlet instance

{
	originalTerm: '',
	originalDb: ''
});

function EntrezSearchBarAutoComplCtrl(){
    Portal.$send('AutoCompleteControl');
}

