//////////////////////////////////////////////////////////////////////
////
////  MEDIE - Semantic retrieval engine for MEDLINE
////
//////////////////////////////////////////////////////////////////////

var NUM_PREFETCH_RESULTS = 20;
var main_cgi_name   = "search.cgi";
var query_cgi_name  = "query.cgi";
var rungcl_cgi_name = "rungcl.cgi";   // old
    rungcl_cgi_name = "simplify.cgi";
var cgi1 = "completion.cgi"; // object, subject completion cgi
var cgi2 = "verb.cgi";       // verb completion cgi
var cgi3 = "title.cgi";      // journal title completion cgi

var color = {
  backgroundSelected: "rgb(  5%,  5%,  5% )", // for completion
  foregroundSelected: "rgb( 99%, 99%, 99% )", // for completion
  background:         "rgb( 99%, 99%, 99% )", // for completion
  foreground:         "rgb( 10%, 10%, 10% )", // for completion
  type:               "rgb( 40%, 40%, 40% )", // for completion
  id:                 "rgb( 45%, 45%, 70% )", // for completion
  border:             "rgb( 30%, 30%, 30% )", // 
  error:              "rgb( 70%,  0%,  0% )",
  collation:          "rgb( 30%, 30%, 30% )"
}

var months = {
  jan: 1,
  feb: 2,
  mar: 3,
  apr: 4,
  may: 5,
  jun: 6,
  jul: 7,
  aug: 8,
  sep: 9,
  oct: 10,
  nov: 11,
  dec: 12 };

var lowrank = 201;
var publication_ranks = {
  "0370647": 1,
  "8309206": 2,
  "0255562": 3,
  "2985150R": 4,
  "101124168": 5,
  "0404511": 6,
  "101124169": 7,
  "100962782": 8,
  "0413066": 9,
  "0410462": 10,
  "9502015": 11,
  "0231714": 12,
  "100941354": 13,
  "9216904": 14,
  "7804039": 15,
  "9600627": 16,
  "2985213R": 17,
  "7501160": 18,
  "9604648": 19,
  "8006258": 20,
  "2985134R": 21,
  "7607088": 22,
  "100890575": 23,
  "100962779": 24,
  "101124171": 25,
  "101130617": 26,
  "101137067": 27,
  "9883513": 28,
  "0434624": 29,
  "9211097": 30,
  "0361046": 31,
  "101155473": 32,
  "0421737": 33,
  "8711660": 34,
  "9706653": 35,
  "9809671": 36,
  "8913428": 37,
  "7503089": 38,
  "9432918": 39,
  "7802877": 40,
  "9802571": 41,
  "8805125": 42,
  "101183755": 43,
  "101120028": 44,
  "100955717": 45,
  "7808616": 46,
  "8809320": 47,
  "101190261": 48,
  "2985109R": 49,
  "0117605": 50,
  "0335405": 51,
  "0372370": 52,
  "15040080R": 53,
  "0370600": 54,
  "7610674": 55,
  "8219702": 56,
  "0372351": 57,
  "0157313": 58,
  "0370475": 59,
  "0372435": 60,
  "0374630": 61,
  "101139309": 62,
  "8507085": 63,
  "8309333": 64,
  "9200566": 65,
  "0370121": 66,
  "9107782": 67,
  "0147763": 68,
  "7900832": 69,
  "9208688": 70,
  "0375356": 71,
  "100883395": 72,
  "101130150": 73,
  "101322263": 74,
  "9876465": 75,
  "8807282": 76,
  "7513292": 77,
  "2985151R": 78,
  "7906158": 79,
  "9302532": 80,
  "7505876": 81,
  "100966032": 82,
  "9518021": 83,
  "7603509": 84,
  "100911346": 85,
  "8208664": 86,
  "8902526": 87,
  "7808666": 88,
  "101125647": 89,
  "101088070": 90,
  "8302946": 91,
  "0372374": 92,
  "0271103": 93,
  "0144032": 94,
  "0376473": 95,
  "9890299": 96,
  "0370543": 97,
  "9107784": 98,
  "0047103": 99,
  "9111375": 100,
  "9607835": 101,
  "0372367": 102,
  "0117147": 103,
  "8301365": 104,
  "9708669": 105,
  "9885358": 106,
  "8900118": 107,
  "9612306": 108,
  "100973461": 109,
  "9009458": 110,
  "9421642": 111,
  "8209988": 112,
  "101213381": 113,
  "9111376": 114,
  "101165244": 115,
  "9811312": 116,
  "7702118": 117,
  "101231360": 118,
  "7905840": 119,
  "0370512": 120,
  "0372763": 121,
  "8605731": 122,
  "0372440": 123,
  "9815056": 124,
  "9010218": 125,
  "0376476": 126,
  "8103150": 127,
  "8310903": 128,
  "9215429": 129,
  "0370416": 130,
  "100957246": 131,
  "7805975": 132,
  "9437445": 133,
  "9208958": 134,
  "9604391": 135,
  "101137089": 136,
  "9421549": 137,
  "2985108R": 138,
  "1275002": 139,
  "100963049": 140,
  "2984705R": 141,
  "0372373": 142,
  "8701744": 143,
  "9431859": 144,
  "7707449": 145,
  "0411011": 146,
  "0372537": 147,
  "0372741": 148,
  "8102140": 149,
  "0401141": 150,
  "7806090": 151,
  "8903774": 152,
  "0370605": 153,
  "7503056": 154,
  "9711805": 155,
  "8006263": 156,
  "0376515": 157,
  "8502408": 158,
  "9013836": 159,
  "9001516": 160,
  "101300458": 161,
  "8710523": 162,
  "8109087": 163,
  "8804484": 164,
  "9505803": 165,
  "9207397": 166,
  "0372355": 167,
  "101128775": 168,
  "9100492": 169,
  "8711562": 170,
  "8510851": 171,
  "0213264": 172,
  "101190390": 173,
  "101215604": 174,
  "9310916": 175,
  "8704895": 176,
  "0404471": 177,
  "0052457": 178,
  "8610640": 179,
  "9201390": 180,
  "9203213": 181,
  "9607332": 182,
  "100883581": 183,
  "101300366": 184,
  "100939340": 185,
  "2985117R": 186,
  "100883691": 187,
  "7906255": 188,
  "0372354": 189,
  "9890633": 190,
  "8501455": 191,
  "0204634": 192,
  "8712028": 193,
  "9110718": 194,
  "9815369": 195,
  "0417353": 196,
  "9509184": 197,
  "9500160": 198,
  "0401224": 199,
  "9304532": 200
};

//////////////////////////////////////////////////////////////////////
////
////  Library
////
//////////////////////////////////////////////////////////////////////

// manipulating HTML elements

function showLayer( id ) {
  var elem = $( id );
  elem.style.position = 'static';
  elem.style.visibility = 'inherit';
  elem.style.display = '';
  elem.removeClassName('hidden');
}

function hideLayer( id ) {
  var elem = $( id );
  elem.style.position = 'absolute';
  // elem.style.visibility = 'hidden';
  elem.style.display = 'none';
}

function showByClass( id ) {
  var elem = $( id );
  elem.removeClassName('hidden');
  elem.addClassName('visible');
}

function hideByClass( id ) {
  var elem = $( id );
  elem.removeClassName('visible');
  elem.addClassName('hidden');
}

function deleteChildren( doc ) {
  if ( ! doc ) { return; }
  if ( ! doc.childNodes ) { return; }
  while ( doc.childNodes.length > 0 ) {
    doc.removeChild( doc.firstChild );
  }
}

function elementPosition( elem ) {
  var left = 0;
  var top = 0;
  var e = elem;
  while ( e ) {
    left += e.offsetLeft;
    top += e.offsetTop;
    e = e.offsetParent;
  }
  return { left: left, top: top };
}

function windowPosition() {
  var position = { left: 0, right: 0, top: 0, bottom: 0 };
  if ( document.all ) {
    position.left   = document.body.scrollLeft;
    position.right  = position.left + document.body.clientWidth;
    position.top    = document.body.scrollTop;
    position.bottom = position.top + document.body.clientHeight;
  } else {
    position.left   = pageXOffset;
    position.right  = position.left + innerWidth;
    position.top    = pageYOffset;
    position.bottom = position.top + innerHeight;
  }
  position.right    = position.right  - 20;
  position.bottom   = position.bottom - 20;
  return position;
}

// HTTP client
function createXMLHttpRequest() {
  if ( window.XMLHttpRequest ) {
    return new XMLHttpRequest();
  } else {
    return new ActiveXObject( "Microsoft.XMLHTTP" );
  }
}

function getTextValue(el) {
  return el.textContent ? el.textContent : el.text;
}

//////////////////////////////////////////////////////////////////////
////
////  table sorting
////
//////////////////////////////////////////////////////////////////////

var target_texts;

function sortTable( id, col, is_entity ) {
  var target_column = col;
  var tbody = $( id ).getElementsByTagName( "tbody" )[ 0 ];
  var target_rows = tbody.getElementsByTagName( "tr" );
  target_texts = new Array();

  var row_ids = new Array();
  var cells = new Array();
  for ( var i = 0; i < target_rows.length; ++i ) {
    row_ids[ i ] = i;
    cells[ i ] = target_rows[ i ];
    target_texts[ i ] = extractText( target_rows[ i ].getElementsByTagName( "td" )[ target_column ], is_entity );
    if ( target_texts[ i ] == "" ) {
      target_texts[ i ] = "\xff";  // empty string should be put lower
    }
  }
  row_ids.sort( compareRows );
  for ( var i = 0; i < target_rows.length; ++i ) {
    tbody.appendChild( cells[ row_ids[ i ] ] );
  }
}

function extractText( elem, is_entity ) {
  var text = "";
  var nodes = elem.childNodes;
  for ( var i = 0; i < nodes.length; ++i ) {
    if ( nodes[ i ].nodeType == 3 ) {  // TEXT_NODE
      if ( ! is_entity ) { text = text + nodes[ i ].nodeValue.toLowerCase(); }
    } else if ( nodes[ i ].nodeType == 1 ) {  // ELEMENT_NODE
      text = text + extractText( nodes[ i ], false );
    }
  }
  return text;
}

function compareRows( a, b ) {
  var val_a = target_texts[ a ];
  var val_b = target_texts[ b ];
  if ( val_a < val_b ) { return -1; }
  if ( val_a > val_b ) { return 1; }
  return 0;
}

//////////////////////////////////////////////////////////////////////
////
////  coloring subject, verb, and object 
////
//////////////////////////////////////////////////////////////////////

var coloring = {
  subject: true,
  verb:    true,
  object:  true,
  gene:    true,
  disease: true
}

function switchColoring( type ) {
  var name1, name2, isColoringOn;
  switch ( type ) {
  case "subject": coloring.subject = ! ( isColoringOn = coloring.subject ); break;
  case "verb"   : coloring.verb    = ! ( isColoringOn = coloring.verb    ); break;
  case "object" : coloring.object  = ! ( isColoringOn = coloring.object  ); break;
  case "gene"   : coloring.gene    = ! ( isColoringOn = coloring.gene    ); break;
  case "disease": coloring.disease = ! ( isColoringOn = coloring.disease ); break;
  }
  if ( isColoringOn ) {
    name1 = "." + type;
    name2 = "x" + type;
  } else {
    name1 = ".x" + type;
    name2 = type;
  }
  var elements = $$( name1 );
  for ( var i=0; i<elements.length; ++i ) { elements[i].className = name2; }
}

//////////////////////////////////////////////////////////////////////
////
////  Popup window 
////
//////////////////////////////////////////////////////////////////////

var popupWindow = null;

function showPopupWindow( event, target, content ) {
  if (!event) event = window.event;
  if ( ! popupWindow ) {
    popupWindow = document.createElement( "div" );
    popupWindow.style.position   = 'absolute';
    popupWindow.style.visibility = 'hidden';
    document.body.appendChild( popupWindow );
  }
  deleteChildren( popupWindow );
  new Insertion.Bottom( popupWindow, content );

  var width = 300; // instead of using popupWindow.offsetWidth
  popupWindow.className = 'popup';
  popupWindow.onclick  = function () { hidePopupWindow(); };
  popupWindow.style.fontSize = "100%" ;
  var position = elementPosition( target );
  position.top += target.offsetHeight;
  var window_position = windowPosition();
  if ( position.left + width > window_position.right ) {
    position.left = window_position.right - width;
  }
  if ( position.left < window_position.left ) {
    position.left = window_position.left;
  }
  if ( position.top + popupWindow.offsetHeight > window_position.bottom ) {
    position.top = window_position.bottom - popupWindow.offsetHeight;
  }
  if ( position.top < window_position.top ) {
    position.top = window_position.top;
  }
  popupWindow.style.left       = position.left + 'px';
  popupWindow.style.top        = position.top + 'px';
  popupWindow.style.width      = width + "px";
  popupWindow.style.padding    = "5px";
  popupWindow.style.visibility = 'visible';
}

function hidePopupWindow() {
  if ( popupWindow ) {
    popupWindow.style.visibility = 'hidden';
  }
}

//////////////////////////////////////////////////////////////////////
////
////  Popup window for database links
////
//////////////////////////////////////////////////////////////////////
// reform deleteChildren(), windowPosition()

var PopupWindow = function( popupID ) {
  this.window     = null;
  this.isActive   = false;
  this.initialize = function() {
                      if ( this.window ) { return; }
                      var self = this;
                      this.window = document.createElement( "div" );
                      this.window.id = popupID;
                      this.window.className = "popup";
                      this.window.style.position = "absolute";
                      this.window.style.visibility = "hidden";
                      this.window.style.padding = "5px";
                      this.window.onmouseover = function () { self.isActive = true; };
                      this.window.onmouseout  = function () { self.hide(); };
                      document.body.appendChild( this.window );
                    };
  this.calcPos    = function( target ) {
                      var position = elementPosition( target );
                      position.top += target.offsetHeight;
                      var wpos = windowPosition();
                      if ( position.left + this.window.offsetWidth > wpos.right ) {
                        position.left = wpos.right - this.window.offsetWidth;
                      }
                      if ( position.left < wpos.left ) {
                        position.left = wpos.left;
                      }
                      if ( position.top + this.window.offsetHeight > wpos.bottom ) {
                        position.top = wpos.bottom - this.window.offsetHeight;
                      }
                      if ( position.top < wpos.top ) {
                        position.top = wpos.top;
                      }
                      return position;
                    };
  this.append     = function( arg ) {
                    };
  this.show       = function( target, arg ) {
                      this.isActive = true;
                      $( this.window.id ).innerHTML = "";
                      this.append( arg );
                      var position = this.calcPos( target );
                      this.window.style.left = position.left + "px";
                      this.window.style.top  = position.top  + "px";
                      this.window.style.visibility = "visible";
                    };
  this.hide       = function() {
                      if ( ! this.isActive ) { return; }
                      this.isActive = false;
                      var self = this;
                      setTimeout( function() { self.hideNow(); }, 1000 );
                    };
  this.hideNow    = function() {
                      if ( ! this.window || this.isActive ) { return; }
                      this.window.style.visibility = "hidden";
                      deleteChildren( this.window );
                    };
  this.initialize();
}

var popupHelp;
var dblinksPopup;
function initPopups() {
  popupHelp = new PopupWindow( "popuphelp" );
  popupHelp.append = function( text ) {
    this.window.style.width = "300px";
    this.window.style.fontSize = "100%" ;
    $( this.window.id ).innerHTML = text;
  }

  dblinksPopup = new PopupWindow( "dblinks" );
  dblinksPopup.speciesClassMap = {
    "Caenorhabditis elegans"    : "org0",
    "Drosophila melanogaster"   : "org1",
    "Homo sapiens"              : "org2",
    "Mus musculus"              : "org3",
    "Rattus norvegicus"         : "org4",
    "Saccharomyces cereviciae"  : "org5",
    "Schizosaccharomyces pombe" : "org6"
  };

  dblinksPopup.append = function( node ) {

    var type         = node.getAttribute( "type" );
    var gene_id      = node.getAttribute( "uniprot_id" );
    var gene_symbol  = node.getAttribute( "gene_symbol" );
    var gene_name    = node.getAttribute( "gene_name" );
    var species      = node.getAttribute( "species" );
    var db_site      = node.getAttribute( "db_site" );
    var disease_id   = node.getAttribute( "facta_id" );

    type        = ( type )?        type        : "";
    gene_id     = ( gene_id )?     gene_id     : "";
    gene_symbol = ( gene_symbol )? gene_symbol : "";
    gene_name   = ( gene_name )?   gene_name   : "";
    species     = ( species )?     species     : "";
    db_site     = ( db_site )?     db_site     : "";
    disease_id  = ( disease_id )?  disease_id  : "";

    var types        = type.split(";");
    var gene_ids     = gene_id.split(";");
    var gene_symbols = gene_symbol.split(";");
    var gene_names   = gene_name.split(";");
    var speciess     = species.split(";");
    var db_sites     = db_site.split(";");
    var disease_ids  = disease_id.split(";");

    for ( var i = 0; i < types.length; ++i ) {
      if (types[i] == "gene" || types[i] == "gene_prod" || types[i] == "disease") {
          this.appendTitleNode( types[ i ], gene_symbols[ i ], speciess[ i ], disease_ids[ i ] );
          this.appendLinkNode( db_sites[ i ] );
          this.appendDiseaseIDNode( disease_ids[ i ] );
      }
    }
  };

  dblinksPopup.appendDiseaseIDNode = function( disease_id ) {
    var node = document.createElement( "div" );
    node.appendChild( document.createTextNode( disease_id ) );
    node.style.marginBottom = "2px";
    this.window.appendChild( node );
  };

  dblinksPopup.appendTitleNode = function( type, gene_symbol, species, disease_id ) {
    // make gene/gene_prod/disease title 
    var titleString;
    var titleNode = document.createElement( "div" );
    titleNode.className = "db_title";
    switch ( type ) {
      case "gene"      :
      case "gene_prod" : titleString = gene_symbol + " (" + species + ")";
                         break;
      case "disease"   : titleString = "disease";
                         break;
    }
    var speciesClass = this.speciesClassMap[ species ];
    if ( speciesClass ) { titleNode.className = "db_title " + speciesClass; }
    else { titleNode.style.backgroundColor = "rgb( 70%, 70%, 70% )"; }
    titleNode.appendChild( document.createTextNode( titleString ) );
    this.window.appendChild( titleNode );
  };

  dblinksPopup.appendLinkNode = function( db_site ) {

    var links = db_site.split( '|' );
    for ( var i=0; i<links.length; ++i ) {
      var link    = links[ i ].split( ':' );
      var label   = link[ 0 ];
      var href    = null;
      var onclick = null;
      switch ( label ) {
      // CHANGE: 2008-01-29:
      //  * GDB is not operated now
      //  * GenAtlas: the CGI program name changed: fiche1.php -> fiche.php
      //  * HUGO: URL and CGI program name changed
      //  * PIR: no longer serviced
      //  * S.pombe: interface changed
      // case "GDB"        : href = "http://www.gdb.org/gdb-bin/genera/genera/hgd/DBObject?!key=GDB%3A" + link[ 1 ] + "&!sub=0"; break;
      // case "GenAtlas"   : href = "http://www.dsi.univ-paris5.fr/genatlas/fiche1.php?symbol=" + link[ 1 ] + "#carte"; break;
      // case "HUGO"       : href = "http://www.gene.ucl.ac.uk/nomenclature/data/get_data.php?hgnc_id=" + link[ 1 ]; break;
      // case "PIR"        : href = "http://pir.georgetown.edu/cgi-bin/pirwww/nbrfget?xref=1&id=" + link[ 1 ]; break;
      // case "S.pombe"    : href = "http://www.genedb.org/genedb/Search?name=" + link[ 1 ] + "&organism=pombe"; break;
      case "Info-PubMed": href = INFO_PUBMED_URL; onclick = new Function( "", "addToInfoPubMed( \"" + link[ 1 ] + "\", 1 ); return false;" ); break;
      case "EntrezGene" : href = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=full_report&list_uids=" + link[ 1 ]; break;
      case "FlyBase"    : href = "http://www.flybase.org/.bin/fbidq.html?" + link[ 1 ]; break;
      case "GenAtlas"   : href = "http://www.dsi.univ-paris5.fr/genatlas/fiche.php?symbol=" + link[ 1 ] + "#carte"; break;
      case "HUGO"       : href = "http://www.genenames.org/data/hgnc_data.php?hgnc_id=" + link[ 1 ]; break;
      case "MGI"        : href = "http://www.informatics.jax.org/searches/accession_report.cgi?id=MGI:" + link[ 1 ]; break;
      case "MIPS"       : href = "http://mips.gsf.de/genre/proj/yeast/searchEntryAction.do?text=" + link[ 1 ]; break;
      case "OMIM"       : href = "http://www.ncbi.nlm.nih.gov/entrez/dispomim.cgi?id=" + link[ 1 ]; break;
      case "RGD"        : href = "http://rgd.mcw.edu/tools/genes/genes_view.cgi?id=" + link[ 1 ]; break;
      case "S.pombe"    : href = "http://www.genedb.org/genedb/Dispatcher?formType=navBar&organism=All%3A*&desc=yes&submit=Search&name=" + link[ 1 ]; break;
      case "SGD"        : href = "http://db.yeastgenome.org/cgi-bin/locus.pl?dbid=" + link[ 1 ]; break;
      case "SWISS-PROT" : href = "http://www.expasy.ch/cgi-bin/niceprot.pl?" + link[ 1 ]; break;
      case "TrEMBL"     : href = "http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-e+[SPTREMBL-acc:" + link[ 1 ] + "]+-vn+2"; break;
      case "WormBase"   : href = "http://www.wormbase.org/db/gene/gene?name=" + link[ 1 ] + ";class=CDS"; break;
      case "UMLS"       : href = "http://www.nlm.nih.gov/research/umls/";  /* temporary */ break;
      }
      // XXX: just skip unknown lable
      // if ( ! href ) { return; }
      if ( ! href ) { continue; }
      var a = document.createElement( "a" );
      a.href = href;
      a.appendChild( document.createTextNode( label ) );
      a.target = "_blank";
      if ( onclick ) { a.onclick = onclick; }
      var linkNode = document.createElement( "div" );
      linkNode.style.marginBottom = "2px";
      linkNode.appendChild( a );
      this.window.appendChild( linkNode );
    }
  };
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////
var dblink_popup = null;
var dblink_popup_active = false;
var dblink_class_map = {
  "Caenorhabditis elegans"    : "org0",
  "Drosophila melanogaster"   : "org1",
  "Homo sapiens"              : "org2",
  "Mus musculus"              : "org3",
  "Rattus norvegicus"         : "org4",
  "Saccharomyces cereviciae"  : "org5",
  "Schizosaccharomyces pombe" : "org6"
};

var sendto_popup = null;
var sendto_popup_active = false;

function showSendTo( event, target, symbol, id ) {
  if ( ! sendto_popup ) {
    sendto_popup = document.createElement( "div" );
    sendto_popup.style.position = "absolute";
    sendto_popup.style.visibility = "hidden";
    document.body.appendChild( sendto_popup );
  }
  deleteChildren( sendto_popup );

  var sendToObject = document.createElement( "div" );
  sendToObject.appendChild( document.createTextNode( "send to object field" ) );
  var funcObject = 
    "var field = $( 'object' );" +
    "var newText = field.value + ' [" + symbol + "]';" +
    "field.value = newText;" +
    "oc.last = newText;" +
    "oc.dict.push( { symbol: '[" + symbol + "]', id: '" + id + "' } );";
  sendToObject.onclick = new Function( "", funcObject );
  sendto_popup.appendChild( sendToObject );

  var sendToSubject = document.createElement( "div" );
  sendToSubject.appendChild( document.createTextNode( "send to subject field" ) );
  var funcSubject = 
    "var field = $( 'subject' );" +
    "var newText = field.value + ' [" + symbol + "]';" +
    "field.value = newText;" +
    "sc.last = newText;" +
    "sc.dict.push( { symbol: '[" + symbol + "]', id: '" + id + "' } );";
  sendToSubject.onclick = new Function( "", funcSubject );
  sendto_popup.appendChild( sendToSubject );

  sendto_popup.className = 'popup';
  sendto_popup.onmouseover = function () { sendto_popup_active = true; };
  sendto_popup.onmouseout = function () { removeSendTo(); };
  sendto_popup_active = true;
  dblink_popup_active = true;
  var position = elementPosition( target );
  position.top += target.offsetHeight;
  var window_position = windowPosition();
  if ( position.left + sendto_popup.offsetWidth > window_position.right ) {
    position.left = window_position.right - sendto_popup.offsetWidth;
  }
  if ( position.left < window_position.left ) {
    position.left = window_position.left;
  }
  if ( position.top + sendto_popup.offsetHeight > window_position.bottom ) {
    position.top = window_position.bottom - sendto_popup.offsetHeight;
  }
  if ( position.top < window_position.top ) {
    position.top = window_position.top;
  }
  sendto_popup.style.left = position.left + 'px';
  sendto_popup.style.top  = position.top  + 'px';
  sendto_popup.style.visibility = 'visible';

}


function removeSendToNow() {
  if ( sendto_popup && ! sendto_popup_active ) {
    sendto_popup.style.visibility = 'hidden';
  }
}

function removeSendTo() {
  if ( sendto_popup_active ) {
    sendto_popup_active = false;
    setTimeout( 'removeSendToNow()', 1000 );
  }
}

function removeDbLinksNow() {
  if ( dblink_popup && ! dblink_popup_active ) {
    dblink_popup.style.visibility = 'hidden';
  }
}

function removeDbLinks() {
  if ( dblink_popup_active ) {
    dblink_popup_active = false;
    setTimeout( 'removeDbLinksNow()', 1000 );
  }
}

function createInfoPubmedLink( id ) {
  var a = document.createElement( "a" );
  a.href = INFO_PUBMED_URL + "?symbolquery=" + id;
  a.target = "_blank";
  a.appendChild( document.createTextNode( "Info-PubMed" ) );
  var div = document.createElement( "div" );
  div.className = "item";
  div.appendChild( document.createTextNode( "See" ) );
  div.appendChild( a );
  return div;
}

function addToInfoPubMed( str, tox ) {
  var info_pubmed_request = createXMLHttpRequest();
  if ( info_pubmed_request ) {
    info_pubmed_request.onreadystatechange = function () { openInfoPubMed( info_pubmed_request ); };
    var postdata;
    if ( tox == 1 ) {
      postdata = "targetfor=1&addable=1&ent1=" + escape( str ) + "&mems=ent1";
    } else {
      postdata = "targetfor=1&addable=1&ent2=" + escape( str ) + "&mems=ent2";
    }
    info_pubmed_request.open( "POST", INFO_PUBMED_URL + "SessionMem", true );
    info_pubmed_request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
    info_pubmed_request.send( postdata );
  }
}

var info_pubmed_window = null;
function openInfoPubMed( info_pubmed_request ) {
  if ( info_pubmed_request && info_pubmed_request.readyState == 4 ) {
    if ( info_pubmed_request.status != 200 ) {
      alert( "Info-PubMed is not working now." );
    } else {
      if ( ! info_pubmed_window || info_pubmed_window.closed ) {
        if ( window.opener && ! window.opener.closed && window.opener.name == "info_pubmed" ) {
          info_pubmed_window = opener;
        } else {
          info_pubmed_window = window.open( INFO_PUBMED_URL, "info_pubmed" );
        }
      } else {
        info_pubmed_window.location.reload();
      }
    }
  }
}

//////////////////////////////////////////////////////////////////////
////
////  Result Formatting
////
//////////////////////////////////////////////////////////////////////


function isCommaEtc( doc ) {
  if ( !doc || !doc.childNodes ) { return; }
  var child = doc.childNodes[ 0 ];
  if ( child.nodeType == 3 ) { // TEXT_NODE
    switch ( child.nodeValue ) {
    case "," :
    case "." :
    case ":" :
    case ";" :
      return true;
    default :
      return false;
    }
  }
  return false;
}

function setPopupEventHandlers(span, entityNode) {
    var origTitle = span.title;
    span.onclick    = function() { 
        span.title = "";
        dblinksPopup.show( this, entityNode ); 
        return false; 
    };
    span.onmouseout = function() { 
        span.title = origTitle;
        dblinksPopup.hide();
        return false; 
    };
}

function findMarkupClass(elem, variable) {
  var id = elem.getAttribute( "id" );

  // if coloring, class="subject", else, class="xsubject"
  switch ( id ) {
  case variable[ "$subject" ]:  return coloring.subject ? "subject" : "xsubject";
  case variable[ "$verb" ]:     return coloring.verb ?    "verb"    : "xverb";
  case variable[ "$object" ]:   return coloring.object ?  "object"  : "xobject";
  case variable[ "$keyword1" ]: return "keyword1";
  case variable[ "$keyword2" ]: return "keyword2";
  case variable[ "$keyword3" ]: return "keyword3";
  case variable[ "$keyword4" ]: return "keyword4";
  case variable[ "$keyword5" ]: return "keyword5";
  case variable[ "$keyword6" ]: return "keyword6";
  case variable[ "$keyword7" ]: return "keyword7";
  case variable[ "$keyword8" ]: return "keyword8";
  case variable[ "$keyword9" ]: return "keyword9";
  case variable[ "$keyword10" ]: return "keyword10";
  }

  if (variable[ "$ev_trigger" ] && variable[ "$ev_trigger" ][ id ]) {
    return "event-trigger";
  }
  else if (variable[ "$ev_arg" ] && variable[ "$ev_arg" ][ id ]) {
    return "event-argument";
  }
  else if (variable[ "$ev_part" ] && variable[ "$ev_part" ][ id ]) {
    return "event-participant";
  }

  return null;
}

function convertResultText( doc, ret, variable ) {
  if ( !doc || !doc.childNodes ) { return; }
  for ( var i = 0; i < doc.childNodes.length; ++i ) {
    var child = doc.childNodes[ i ];
    if ( child.nodeType == 3 ) { // TEXT_NODE
      ret.appendChild( document.createTextNode( child.nodeValue ) );
    } else if ( child.nodeType == 1 ) { // ELEMENT_NODE
      if ( child.tagName == "entity_name" ) { // always markup
        var node  = child;
        var span  = document.createElement( "span" );
        var type = node.getAttribute( "type" );

        if ( type.search(/(^|;)gene(_prod)?(;|$)/) != -1 ) {
          span.className = coloring.gene ? "gene" : "xgene";
          span.title = "click to popup database links";
          setPopupEventHandlers(span, node);
        }
        else if ( type.search(/(^|;)disease(;|$)/) != -1 ) {
          span.className = coloring.disease ? "disease" : "xdisease";
          span.title = "click to popup database links";
          setPopupEventHandlers(span, node);
        }
        else {
          // do nothing for other named entity types
        }

        var markupClass = findMarkupClass( node, variable );
        if ( markupClass ) {
          span.className += " " + markupClass;
        }

        if ( child.getAttribute( "class" ) ) {
          span.className += " " + child.getAttribute( "class" );
        }

        convertResultText( child, span, variable );
        ret.appendChild( span );
      }
      else { // other tags
        var markupClass = findMarkupClass( child, variable );
        if ( markupClass ) { // need a span for highlighting
          var span = document.createElement( "span" );
          if ( child.getAttribute( "class" ) ) {
            span.className = child.getAttribute( "class" ) + " " + markupClass;
          }
          else {
            span.className = markupClass;
          }

          // NOTE: matuzaki: for what?
          if ( child.getAttribute( "title" ) ) {
            span.title = child.getAttribute( "title" );
          }

          convertResultText( child, span, variable );
          ret.appendChild( span );
        }
        else { // just go down
          convertResultText( child, ret, variable );
        }
      }
    }
  }
}

function appendXMLLink( parent, position, sentence_id, title, text ) {
  if ( !text ) text = sentence_id ? "\xbbsXML" : "\xbbXML";
  if ( !title ) title = sentence_id ?
      "See linguistic annotations for the sentence" :
      "See linguistic annotations for the article";
  parent.appendChild( document.createTextNode( "   " ) );
  var a = document.createElement( "a" );
  var url = "showxml.cgi?position=" + position;
  if ( sentence_id ) url += "&sentence_id=" + sentence_id;
  a.href = url;
  a.target = "_blank";
  a.title = title;
  a.className = "tiny_option";
  a.appendChild( document.createTextNode( text ) );
  parent.appendChild( a );
}

function createTitleNode( result, id, variable ) {
  var title = result.getElementsByTagName( "ArticleTitle" )[ 0 ];
  var title_text = document.createElement( "span" );
  if ( title ) {
    convertResultText( title, title_text, variable );
  } else {
    title_text.appendChild( document.createTextNode( "[No Title]" ) );
  }
  var title_node = document.createElement( "span" );
  title_node.className = "result_title";
  var pmid = result.getElementsByTagName( "PMID" )[ 0 ].firstChild.nodeValue;
  if ( pmid ) {
    var a = document.createElement( "a" );
    a.href = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&amp;db=pubmed&amp;dopt=Abstract&amp;list_uids=" + pmid;
    a.target = "_blank";
    a.title = "Go to PubMed";
    a.appendChild( title_text );
    title_node.appendChild( a );
  } else {
    title_node.appendChild( title_text );
  }
  var position = result.getElementsByTagName( "position" )[ 0 ].firstChild.nodeValue;
  if ( position ) {
    appendXMLLink( title_node, position );
  }
  return title_node;
}

function createCollationNode( result ) {
  var year = null;
  var journalTitle = "[journal title not available]";
  var volume = null;
  var issue = null;
  var pmid = null;
  var pp = null;
  var authors = [];

  var collationNode = document.createElement( "div" );

  // get year
  var pubDate = result.getElementsByTagName( "PubDate" );
  if ( pubDate && 0 < pubDate.length ) {
    pubDate = pubDate[ 0 ];
    var y = pubDate.getElementsByTagName( "Year" );
    if ( y && 0 < y.length ) {
      year = y[ 0 ].firstChild.nodeValue;
    }
  }

  // get journal title
  var jt = result.getElementsByTagName( "Title" );
  if ( jt && 0 < jt.length ) {
    journalTitle = jt[ 0 ].firstChild.nodeValue;
  }
   
  // get volume and issue 
  var journal = result.getElementsByTagName( "Journal" );
  if ( journal && journal.length ) {
    journal = journal[ 0 ];
    var volumeNodes = journal.getElementsByTagName( "Volume" );
    var issueNodes  = journal.getElementsByTagName( "Issue" );
    volume = ( volumeNodes && 0 < volumeNodes.length )? ( volumeNodes[ 0 ].firstChild.nodeValue ) : ( "" );
    issue  = ( issueNodes  && 0 < issueNodes.length ) ? (  issueNodes[ 0 ].firstChild.nodeValue ) : ( "" );
  }

  // get PubMedID
  var pmidNodes = result.getElementsByTagName( "PMID" );
  if ( pmidNodes && 0 < pmidNodes.length ) { pmid = pmidNodes[0].firstChild.nodeValue; }

  // get pages
  var ppNodes = result.getElementsByTagName( "MedlinePgn" );
  pp = ( ppNodes && 0 < ppNodes.length )? ( ppNodes[ 0 ].firstChild.nodeValue ) : "";

  // get authors
  var authorListNodes = result.getElementsByTagName( "AuthorList" );
  if ( authorListNodes && 0 < authorListNodes.length ) {
    var authorNodes = authorListNodes[0].getElementsByTagName( "Author" );
    var firstName = "";
    var lastName  = "";
    for ( var i=0; i<authorNodes.length; ++i ) {
      var firstNameNodes = authorNodes[ i ].getElementsByTagName( "FirstName" );
      if ( firstNameNodes && 0 < firstNameNodes.length ) {
        firstName = firstNameNodes[ 0 ].firstChild.nodeValue;
      } else {
        var foreNameNodes = authorNodes[ i ].getElementsByTagName ( "ForeName" );
        if ( foreNameNodes && 0 < foreNameNodes.length ) {
          firstName = foreNameNodes[ 0 ].firstChild.nodeValue;
        }
      }
      var lastNameNodes = authorNodes[ i ].getElementsByTagName( "LastName" );
      if ( lastNameNodes && 0 < lastNameNodes.length ) {
        lastName = lastNameNodes[ 0 ].firstChild.nodeValue;
      }
      var author = firstName + " " + lastName;
      authors.push( author );
    }
  }

  for ( var i=0; i<authors.length; ++i ) {
    if ( i != 0 ) { collationNode.appendChild( document.createTextNode( ", " ) ); }
    collationNode.appendChild( document.createTextNode( authors[ i ] ) );
  }
  if ( pp )     { collationNode.appendChild( document.createTextNode( ", pp. " + pp ) ); }
  if ( volume ) { collationNode.appendChild( document.createTextNode( ", Volume " + volume ) ); }
  if ( issue )  { collationNode.appendChild( document.createTextNode( ", Issue " + issue ) ); }
  collationNode.appendChild( document.createTextNode( ", " + journalTitle ) );
  if ( year ) { collationNode.appendChild( document.createTextNode( ", " + year ) ); }
  collationNode.appendChild( document.createTextNode( " [PMID:" + pmid + "]" ) );
  collationNode.style.fontSize = "70%";
  collationNode.style.color = color.collation;
  return collationNode;
}


var debug = function ( message ) { 
// 2010-03-11: slowest medie.js function (13.85%!)
//  var n = $( "debug" ).childNodes.length;
//  for ( var i=0; i<n-60; i++ ) {
//    $( "debug" ).removeChild( $( "debug" ).firstChild );
//  }
//  $( "debug" ).innerHTML += message + "<br />"; 

// if we need debugging, uncomment this, then use FireBug
  try {
    console.log(message);
  } catch(x) {
    // not debugging
  }
}


//////////////////////////////////////////////////////////////////////
////
////   Search
////
//////////////////////////////////////////////////////////////////////

var Search = function() {
  this.runGCLRequest    = null;
  this.gclOptions       = null;
  this.viewOptions      = { outputFormat: "sentence", numberOfResults: 10 };
  this.displayPosition  = { start: 0, end: 0 };
  this.resultsWindow    = { sentence: null, article: null, table: null };
  this.formData         = null;
  this.results          = null;
  this.summary          = null;
  this.isSearchFinished = null;
  this.targetVariables  = null;
  this.searchPosition   = null;
  this.summaryStrings   = null;
  this.submittedQuery   = "";
  this.time             = 0;

  this.displayError     = function( text ) {
                            var summary_window = $( "results_summary" );
                            if ( summary_window ) {
                              summary_window.className = "result_summary";
                            }
                            var summary_num = $( "results_summary_num" );
                            if ( summary_num ) {
                              deleteChildren( summary_num );
                              var span = document.createElement( "span" ); 
                              span.className = "item";
                              span.appendChild( document.createTextNode( "Error: " ) );
                              summary_num.appendChild( span );
                              summary_num.appendChild( document.createTextNode( text ) );
                            }
                            showLayer( "results_field" );
                          },

  this.editGCLQuery     = function() {
                            var element = $( "results_submitted_query_value" );
                            var field = '<textarea class="queryarea" id="textarea" rows="10" cols="80">' + element.innerHTML + 
                                        "</textarea>" + "<br /><button onclick=\"searchObject.gclRun()\">GCL search</button>";
                            Element.update( element, field );
                            element.title = "edit GCL query";
                            element.onclick = "";
                          },
  this.gclRun           = function() {
debug( "GCL search" );
                            this.initSearch();
                            $( "textarea" ).value = $( "textarea" ).value.replace( /\n/g, "" );
                            $( "textarea" ).value = $( "textarea" ).value.replace( /\s+/g, " " );
                            this.submittedQuery = $( "textarea" ).value; // and targets and summary ... pa
                            this.targetVariables = [ "subject", "verb", "object" ];
                            this.displayResults( true );
                          },
  this.getEndPosition   = function() { // get end_position
                            if ( this.viewOptions.numberOfResults < 0 ) {
                              // negative values means all results
                              return this.results.length;
                            } 
                            return Math.min( this.displayPosition.start + this.viewOptions.numberOfResults , this.results.length );
                          },

  this.getTbodyNode     = function() { // wrapping of this.getPrevNextNode()
                            var tbody = document.createElement( "tbody" ); 
                            var doc   = document.createElement( "tr" );         tbody.appendChild( doc  );
                            var td    = document.createElement( "td" );           doc.appendChild( td   );
                            td.setAttribute( "colSpan", 3 );
                            var node  = this.underline( this.getPrevNextNode() );  td.appendChild( node );
                            var docx  = document.createElement( "tr" );         tbody.appendChild( docx );
                            var tdx   = document.createElement( "td" );          docx.appendChild( tdx  );
                            tdx.setAttribute( "colSpan", 3 );
                            tdx.style.padding = "10px";
                            return tbody;
                          };

  this.overline         = function( node ) {
                            node.style.borderTop = "double 3px " + color.border;
                            node.style.paddingTop = "3px";
                            return node;
                          };

  this.underline        = function( node ) {
                            node.style.borderBottom = "double 3px " + color.border;
                            node.style.paddingBottom = "3px";
                            return node;
                          };

  this.displayResults   = function( init ) { // display search results
                            if ( ! this.gclOptions ) return; // when called without search
                            var end_position = this.getEndPosition();

                            // display summary
                            this.displaySummary( end_position, init );

                            if ( ! this.resultsWindow.sentence ) {
                              this.initDisplay();
                            }

                            // I'm not sure this is the right place to do this.
                            // TODO: rename the ID of the <div id="resultsField"> tag later
                            showLayer("resultsField");

                            // add new results
                            if ( this.displayPosition.end < end_position ) {
                              for ( var i = this.displayPosition.end; i < end_position; ++i ) {
                                var index = ( i >= this.sortperm.length ) ? i : this.sortperm[ i ][ 0 ];
                                var result = this.results[ index ];
                                var result_id = "result_" + index;

                                // variable such as $vert = t1596 
                                var variable = $H();
                                $A( result.getElementsByTagName( "variable" ) ).each(
                                  function( e, i ) {
                                    var varElement = e.getElementsByTagName( "var"   )[ 0 ];
                                    var valElement = e.getElementsByTagName( "value" )[ 0 ];
                                    if ( varElement.firstChild && valElement.firstChild ) {
                                      var ev_var = varElement.firstChild.nodeValue.match( /\$ev_part|\$ev_arg|\$ev_trigger/ );
                                      if ( ev_var ) {
                                        if ( ! variable[ ev_var ] ) variable[ ev_var ] = {};
                                        variable[ ev_var ][ valElement.firstChild.nodeValue ] = varElement.firstChild.nodeValue;
                                      }
                                      else {
                                        variable[ varElement.firstChild.nodeValue ] = valElement.firstChild.nodeValue;
                                      }
                                    }
                                  } 
                                );
                                var position = result.getElementsByTagName( "position" )[ 0 ].firstChild.nodeValue;
                                var sentence_id = variable[ "$sentence" ];

                                { // table
                                  if ( this.resultsWindow.table.childNodes.length == 0 ) {
                                    var prevNext = this.underline( this.getPrevNextNode() );
                                    var node = this.resultsWindow.table.parentNode.parentNode.childNodes[ 0 ];
                                    this.resultsWindow.table.parentNode.parentNode.insertBefore( prevNext, node );
                                  }

                                  var doc = document.createElement( "tr" );
                                  doc.id = result_id;
                                  var title = createTitleNode( result, result_id, variable );
                                  var td1 = document.createElement( "td" );
                                  td1.style.padding = "5px";
                                  if ( title ) { td1.appendChild( title ); }
                                  appendXMLLink( td1, position, sentence_id );
                                  doc.appendChild( td1 );

                                  var tags = [].concat(
                                         $A( result.getElementsByTagName( "cons" ) ),
                                         $A( result.getElementsByTagName( "tok" ) ),
                                         $A( result.getElementsByTagName( "event_expression" ) ),
                                         $A( result.getElementsByTagName( "entity_name" ) ),
                                         $A( result.getElementsByTagName( "Event" ) ),
                                         $A( result.getElementsByTagName( "Trigger" ) ) );

                                  if ( tags.length > 0 ) {
                                    var phrase_table = new Array();
                                    for ( var j = 0; j < tags.length; ++j ) {
                                      var phrase = tags[ j ];
                                      var td = document.createElement( "td" );
                                      td.style.padding = "5px";
                                      if ( variable[ "$ev_trigger" ] && variable[ "$ev_trigger" ][ phrase.getAttribute( "id" ) ] ) {
                                        var varName = variable[ "$ev_trigger" ][ phrase.getAttribute( "id" ) ];
                                        td.className = "event-trigger";
                                        phrase_table[ varName ] = td;
                                      }
                                      else if ( variable[ "$ev_arg" ] && variable[ "$ev_arg" ][ phrase.getAttribute( "id" ) ] ) {
                                        var varName = variable[ "$ev_arg" ][ phrase.getAttribute( "id" ) ];
                                        td.className = "event-argument";
                                        phrase_table[ varName ] = td;
                                      }
                                      else if ( variable[ "$ev_part" ] && variable[ "$ev_part" ][ phrase.getAttribute( "id" ) ] ) {
                                        var varName = variable[ "$ev_part" ][ phrase.getAttribute( "id" ) ];
                                        td.className = "event-participant";
                                        phrase_table[ varName ] = td;
                                      }
                                      else {
                                        switch ( phrase.getAttribute( "id" ) ) {
                                        case variable[ "$verb" ]     : td.className = "verb"     ; break;
                                        case variable[ "$object" ]   : td.className = "object"   ; break;
                                        case variable[ "$subject" ]  : td.className = "subject"  ; break;
                                        case variable[ "$keyword1" ] : td.className = "keyword1" ; break;
                                        case variable[ "$keyword2" ] : td.className = "keyword2" ; break;
                                        case variable[ "$keyword3" ] : td.className = "keyword3" ; break;
                                        case variable[ "$keyword4" ] : td.className = "keyword4" ; break;
                                        case variable[ "$keyword5" ] : td.className = "keyword5" ; break;
                                        case variable[ "$keyword6" ] : td.className = "keyword6" ; break;
                                        case variable[ "$keyword7" ] : td.className = "keyword7" ; break;
                                        case variable[ "$keyword8" ] : td.className = "keyword8" ; break;
                                        case variable[ "$keyword9" ] : td.className = "keyword9" ; break;
                                        case variable[ "$keyword10" ]: td.className = "keyword10"; break;
                                        }
                                        phrase_table[ td.className ] = td;
                                      }
                                      convertResultText( phrase, td, variable );
                                    }

                                    for ( var j = 0; j < this.targetVariables.length; ++j ) {
                                      var target = phrase_table[ this.targetVariables[ j ] ];
                                      if ( target ) {
                                        doc.appendChild( target );
                                      } else {
                                        var td = document.createElement( "td" );
                                        td.className = this.targetVariables[ j ];
                                        td.appendChild( document.createTextNode( "" ) );
                                        doc.appendChild( td );
                                      }
                                    }
                                  }
                                  this.resultsWindow.table.appendChild( doc );
                                } 
                                { // article
                                  if ( this.resultsWindow.article.childNodes.length == 0 ) {
                                    this.resultsWindow.article.appendChild( this.getTbodyNode() );
                                  }
                                  var tr1 = document.createElement( "tr" );
                                  var tr2 = document.createElement( "tr" );
                                  var td1 = document.createElement( "td" );
                                  var td2 = document.createElement( "td" );
                                  var td3 = document.createElement( "td" );
                                  var tdx = document.createElement( "td" ); // for abstract
                                  tdx.setAttribute( "colSpan", 2 );
                                  td3.id = result_id;
                                  td1.style.verticalAlign = "top";
                                  td1.style.padding       = "3px";
                                  td2.style.verticalAlign = "top";
                                  td2.style.padding       = "3px";
                                  td3.style.padding = "3px 3px 10px 3px";
                                  td1.appendChild( document.createTextNode( i + 1 + "." ) );  // number
                                  var title = createTitleNode( result, result_id, variable );
                                  if ( title ) {
                                    td3.appendChild( title );
                                  }
      
                                  var collation = createCollationNode( result );
                                  if ( collation ) { td3.appendChild( collation ); }
      
                                  var content = result.getElementsByTagName( "AbstractText" )[ 0 ];
                                  var div = document.createElement( "div" );
                                  div.className = "result_content";
                                  if ( content ) {
                                    convertResultText( content, div, variable );
                                  } else {
                                    div.innerHTML = '<div style="color:' + color.error + '">No abstract available</div>';
                                  }
                                  td3.appendChild( div );
                                  appendXMLLink( div, position, sentence_id );
      
                                  tr1.appendChild( td1 );
                                  tr1.appendChild( td3 );
                                  tr1.appendChild( td2 );
                                  tr2.appendChild( document.createElement( "td" ) );
                                  tr2.appendChild( tdx );
                                  var tbody = document.createElement( "tbody" );
                                  tbody.appendChild( tr1 )
                                  //tbody.appendChild( tr2 )
                                  this.resultsWindow.article.appendChild( tbody );
                                } 
                                { // sentence
                                  if ( this.resultsWindow.sentence.childNodes.length == 0 ) {
                                    this.resultsWindow.sentence.appendChild( this.getTbodyNode() );
                                  }
                                  var doc = document.createElement( "tr" );
                                  var td1 = document.createElement( "td" );
                                  var td2 = document.createElement( "td" );
                                  var td3 = document.createElement( "td" );
                                  doc.appendChild( td1 );
                                  doc.appendChild( td3 );
                                  doc.appendChild( td2 );
                                  td3.id = result_id;
                                  td1.style.verticalAlign = "top";
                                  td1.style.padding       = "3px";
                                  td2.style.verticalAlign = "top";
                                  td2.style.padding       = "3px";
                                  td3.style.padding       = "3px 3px 15px 3px";
                                  td1.appendChild( document.createTextNode( i + 1 + "." ) );  // number
                                  var title = createTitleNode( result, result_id, variable );
                                  if ( title ) { td3.appendChild( title ); }
                                  var collation = createCollationNode( result );
                                  if ( collation ) { td3.appendChild( collation ); }
                                  
                                  var sentence = $A( result.getElementsByTagName( "sentence" ) ).find(
                                    function( e, i ) {
                                      if ( sentence_id == e.getAttribute( "id" ) ) {
                                        return true;
                                      }
                                      return false;
                                    }
                                  );
                                  if ( sentence ) {
                                    // if the sentence is equal to the title, do not display it
                                    if ( sentence.parentNode.tagName != "ArticleTitle" ) 
                                    {
                                      var content_node = document.createElement( "div" );
                                      content_node.className = "result_content";
                                      convertResultText( sentence, content_node, variable );
                                      appendXMLLink( content_node, position, sentence_id );
                                      td3.appendChild( content_node );
                                    } else {
                                      appendXMLLink( td3, position, sentence_id );
                                    }
                                  } else { // no phrase tag, in keyword search
                                    var ne = result.getElementsByTagName( "named_entity" )[ 0 ];
                                    if ( ne ) { // instead of phrase
                                      var ne_parent = ne.parentNode;
                                      if ( sentence = ne_parent ) { // not a bug
                                        if ( ne_parent.nodeName == "sentence" ||
                                             ( ( sentence = ne_parent.parentNode ) && ne_parent.parentNode.nodeName == "sentence" ) ) {
                                          var content_node = document.createElement( "div" );
                                          content_node.className = "result_content";
                                          convertResultText( sentence, content_node, variable );
                                          doc.appendChild( content_node );
                                        }
                                      }
                                    }
                                  }
                                  var tbody = document.createElement( "tbody" );
                                  tbody.appendChild( doc )
                                  this.resultsWindow.sentence.appendChild( tbody );
                                }
                              }
                              this.displayPosition.end = end_position;
                            }
                            if ( init ) {
                              this.runAgain();
                            } else if ( this.searchPosition ) {
                              //this.showNext( Math.max( this.displayPosition.start - this.viewOptions.numberOfResults , 0 ) ); 
                              var limit = this.displayPosition.start + 
                                          this.viewOptions.numberOfResults  + NUM_PREFETCH_RESULTS;
                              if ( this.viewOptions.numberOfResults < 0 || this.results.length < limit ) {
                                this.runAgain( 'position', this.searchPosition );
                              }
                            }
                          };

  this.getPrevNextNode  = function() {
                            var container = document.createElement( "div" );
                            if ( this.displayPosition.start > 0 ) { // show prev
                              var proxy = this;
                              var a = document.createElement( "a" );
                              a.style.textDecoration = "none";
                              a.href = "";
                              a.title = "show previous " + String( this.viewOptions.numberOfResults ) + " results";
                              a.onclick = function () { proxy.showNext( Math.max( proxy.displayPosition.start - proxy.viewOptions.numberOfResults , 0 ) ); return false; };
                              a.appendChild( document.createTextNode( "\xab show prev" ) );
                              container.appendChild( a );
                              container.appendChild( document.createTextNode( " | " ) );
                            }
debug( "displayPosition.end = " + this.displayPosition.end );
debug( "displayPosition.start = " + this.displayPosition.start );
debug( "viewOptions.numberOfResults = " + this.viewOptions.numberOfResults );
debug( "results.length = " + this.results.length );
                            //if ( this.results.length < this.viewOptions.numberOfResults ) { // show next
                              var proxy = this;
                              var a = document.createElement( "a" );
                              a.style.textDecoration = "none";
                              a.href = "";
                              a.title = "show next " + String( this.viewOptions.numberOfResults ) + " results";
                              a.onclick = function () { proxy.showNext( proxy.getEndPosition() ); return false; };
                              a.appendChild( document.createTextNode( "show next \xbb" ) );
                              container.appendChild( a );
                            //}
                            return container;
                          };

  this.getSummarySub    = function( endPosition ) {
                            var summary = null;
                            if ( endPosition == this.results.length && this.isSearchFinished ) {
                              summary = this.isSearchFinished;
                            } else if ( this.viewOptions.numberOfResults < 0 ||
                              this.displayPosition.end < this.displayPosition.start + this.viewOptions.numberOfResults ) {
                              summary = this.summary[ endPosition - 1 ];
                            } 
                            return summary;
                          };

  this.stopSearch       = function() {
                            var searching = $('searching');
                            if (searching) {
                              searching.innerHTML = '<span style="color: ' + color.error + '">Search stopped.</span>';
                            }
                            if (this.runGCLRequest) {
                              this.runGCLRequest.abort();
                            }
                          };

  this.displaySummary   = function( end_position, init ) {
                            var summary;
                            if ( init ) {
                              var summary_window = $( "results_summary" );
                              if ( summary_window ) {
                                summary_window.className = "result_summary " 
                              }

                              // show results field
                              var field_name =  "results_field" ;
                              var field = $( field_name );
                              if ( field ) showLayer( field_name );
                            
                              summary = null;
                            } else if ( end_position == this.results.length && this.isSearchFinished ) {
                              summary = this.isSearchFinished;
                            } else if ( this.viewOptions.numberOfResults < 0 ) {
                              summary = this.summary[ end_position - 1 ];
                            } else if ( this.displayPosition.end < this.displayPosition.start + this.viewOptions.numberOfResults ) {
                              summary = this.summary[ end_position - 1 ];
                            } else {
                              return;  // no update
                            }
                            var summary_num       = $( "results_summary_num" );
                            if ( summary_num ) {
                              deleteChildren( summary_num );
                              if ( init || ( ( this.viewOptions.numberOfResults < 0 || end_position < this.displayPosition.start + this.viewOptions.numberOfResults ) && summary.next ) ) {
                                var searching = document.createElement( "div" );
                                searching.id = "searching";
                                searching.style.whiteSpace = "nowrap";
                                searching.className = "searching";
                                summary_num.appendChild( searching );
                                searching.innerHTML = '<img src="Ajax-loader.gif"/> Searching';
                              }
                              if ( summary ) {
                                if ( summary.error ) { // Error
                                  this.displayError( summary.error.firstChild.nodeValue );
                                } else { // Keyword summary
                                  if ( this.isSearchFinished || this.displayPosition.start < end_position ) {
                                    var span = document.createElement( "span" );
                                    span.className = "item";
                                    if ( this.isSearchFinished && this.displayPosition.start == end_position ) {
                                      hideLayer( "resultsField" ); 
                                      var span = document.createElement( "span" );
                                      span.appendChild( document.createTextNode( "No results " ) );
                                      span.style.fontWeight = "bold";
                                      span.style.fontSize   = "20px";
                                      span.style.color      = color.error;
                                      summary_num.appendChild( span );
                                    } else if ( this.displayPosition.start + 1 == end_position ) {
                                      summary_num.appendChild( document.createTextNode( "Result " ) );
                                      span.appendChild( document.createTextNode( end_position ) );
                                    } else if ( this.displayPosition.start < end_position ) {
                                      summary_num.appendChild( document.createTextNode( "Results " ) );
                                      span.appendChild( document.createTextNode( ( this.displayPosition.start + 1 ) + "-" + end_position ) );
                                    }
                                    summary_num.appendChild( span );
                                    if ( this.summaryStrings.length > 0 ) {
                                      var span = document.createElement( "span" );
                                      span.className = "item";
                                      span.appendChild( document.createTextNode( this.summaryStrings.join( ' ' ) ) );
                                      //var string = $( "subject" ).value + " " + 
                                      //             $( "verb" ).value + " " + 
                                      //             $( "object" ).value;
                                      //span.appendChild( document.createTextNode( string ) );
                                      summary_num.appendChild( document.createTextNode( " for " ) );
                                      summary_num.appendChild( span );
                                    }
                                  }
                                }
                          
                                // timeout
                                var timeout = summary.timeout;
                                if ( timeout ) {
                                  summary_num.appendChild( document.createTextNode( " (Timeout in " + timeout.firstChild.nodeValue + " seconds)" ) );
                                }
                              }
                              // when summary exists
                              // show "<< previous" and "next >>"
                              if ( this.viewOptions.numberOfResults >= 0 ) {
                                deleteChildren( $( "bottom_summary" ) );
                                $( "bottom_summary" ).appendChild( this.overline( this.getPrevNextNode() ) );
                              }
                            }
                          
                            // Search time
                            var summary_time = $( "results_summary_time" );
                            if ( summary_time ) {
                              deleteChildren( summary_time );
                              if ( summary && summary.time ) {
                                var time = summary.time;
                                if ( time ) {
                                  summary_time.appendChild( document.createTextNode( time.toFixed( 2 ) + " seconds" ) );
                                }
                                // Search progress
                                var search_progress = summary.progress;
                                if ( search_progress ) {
                                  summary_time.appendChild( document.createTextNode( " (searched " + (parseFloat( search_progress.firstChild.nodeValue )*100.0).toFixed( 2 ) + "% of Medline)" ) );
                                }
                              }
                            }
                          
                            // query
                            if ( init ) {
                              var summary_query = $( "results_submitted_query" );
                              if ( summary_query ) {
                                var query = this.beautifyQuery( this.submittedQuery );
                                var summary_query_value = $( "results_submitted_query_value" );
                                if ( summary_query_value ) {
                                  deleteChildren( summary_query_value );
                                  var proxy = this;
                                  summary_query_value.onclick = function() { proxy.editGCLQuery(); };
                                  summary_query_value.appendChild( document.createTextNode( query ));
                                  //summary_query_value.innerHTML = query;
                                }
                              }
                            }
                          },
  this.beautifyQuery    = function( query ) {
debug( "<strong>beautifyQuery:</strong>" );
                            var ind = "";
                            var inds = [];
                            var res;
                            // indent parentheses
                            query = query.replace( /([^()]*?)\s*([()])(\2*)/gm,
                                function ( whole, nonbraces, brace, morebraces ) {
                                    if ( brace == "(" ) {
                                        if ( nonbraces.length == 1 ) {
                                            // operator
                                            res = nonbraces + " " + brace;
                                            inds.push( false );
                                        } else {
                                            // subexpression
                                            res = nonbraces + "\n" + ind + brace;
                                            inds.push( true );
                                        }
                                        ind += "   ";
                                    } else {
                                        res = nonbraces + brace + morebraces;
                                        for ( var k = morebraces.length; k >= 0; k-- ) {
                                            ind = ind.substring( 0, ind.length - 3 );
                                            if ( inds.pop() ) {
                                                res += "\n" + ind;
                                            }
                                        }
                                    }
                                    return res;
                                } );
                            // break up two [...] arguments
                            query = query.replace( /\n(.*)(\[[^\]]*)\]\s\[/g,
                                function ( whole, leader, content ) {
                                    ind = leader.replace(/./g, ' ');
                                    return "\n" + leader + content + "]\n" + ind + "[";
                                } );
                            // break up (...) [...] arguments
                            query = query.replace( /\n(.*)(\([^\)]*)\)\s\[/g,
                                function ( whole, leader, content ) {
                                    ind = leader.replace(/./g, ' ');
                                    return "\n" + leader + content + ")\n" + ind + "[";
                                } );
                            // remove empty lines
                            query = query.replace( /\n\s*(\n|$)/gm, "\n" );
                            return query; 
                          },

  this.initDisplay      = function() {
                            var ssroot = $( "sentence_results" );
                            var saroot = $( "article_results" );
                            var stroot = $( "table_results" );

                            hideLayer( "sentence_results" );
                            hideLayer( "article_results" );
                            hideLayer( "table_results" );
                            deleteChildren( ssroot );
                            deleteChildren( saroot );
                            deleteChildren( stroot );
                            if (this.viewOptions.outputFormat == "sentence" ) showLayer( "sentence_results" );
                            if (this.viewOptions.outputFormat == "article"  ) showLayer( "article_results" );
                            if (this.viewOptions.outputFormat == "table"    ) showLayer( "table_results" );

                            if ( this.displayPosition.start >= this.results.length ) return;
                            var results_table = document.createElement( "table" );
                            results_table.border = 1;
                            results_table.className = "result_table";
                            results_table.id = "result_table";
                            var tr = document.createElement( "tr" );
                            var th = document.createElement( "th" );
                            th.appendChild( document.createTextNode( "title" ) );
                            tr.appendChild( th );
                            for ( var i = 0; i < this.targetVariables.length; ++i ) {
                              var target = this.targetVariables[ i ];
                              var td = document.createElement( "th" );
                              td.className = target;
                              var button1 = document.createElement( "input" );
                              button1.type = "button";
                              button1.title = "Sort by " + this.targetVariables[ i ];
                              var sorttable1 = "sortTable('result_table'," + ( i + 1 ) + ",false)";
                              button1.onclick = new Function( "", sorttable1 );
                              button1.value = this.targetVariables[ i ];
                              var button2 = document.createElement( "input" );
                              button2.type = "button";
                              button2.title = "Sort by entities in " + this.targetVariables[ i ];
                              var sorttable2 = "sortTable('result_table'," + ( i + 1 ) + ",true)";
                              button2.onclick = new Function( "", sorttable2 );
                              button2.value = "entities";
                              td.appendChild( button1 );
                              td.appendChild( button2 );
                              tr.appendChild( td );
                            }
                            var thead = document.createElement( "thead" );
                            thead.appendChild( tr );
                            results_table.appendChild( thead );
                            this.resultsWindow.table = document.createElement( "tbody" );
                            results_table.appendChild( this.resultsWindow.table );
                            stroot.appendChild( results_table );
                            this.resultsWindow.sentence = document.createElement( "table" );
                            this.resultsWindow.article  = document.createElement( "table" );
                            ssroot.appendChild( this.resultsWindow.sentence );
                            saroot.appendChild( this.resultsWindow.article );
                          },
  this.showNext         = function( start ) {
                            this.displayPosition.start = start;
                            this.displayPosition.end   = this.displayPosition.start;
                            this.resultsWindow.sentence = null;
                            this.resultsWindow.article  = null;
                            this.resultsWindow.table    = null;
                            this.displayResults( false );
                          },
  this.initRequest      = function() { 
                            if ( this.runGCLRequest == null ) {
                              this.runGCLRequest = createXMLHttpRequest();
                            } else {
                              this.runGCLRequest.onreadystatechange = function () {};
                              this.runGCLRequest.abort();
                              this.runGCLRequest = createXMLHttpRequest();  // hack for mozilla's bug
                            }
                          },
  this.initSearch       = function() { 
                            this.initRequest();
                            this.gclOptions = new Array();
                            this.formData   = new Array();
                            var form = document.forms[ "search" ];
                            for ( var i = 0; i < form.elements.length; ++i ) {
                              var elem = form.elements[ i ];
                              var valid = false;
                              switch ( elem.type ) {
                              case "checkbox"  :
                              case "radio"     : valid = elem.checked; break;
                              case "option"    : valid = elem.selected; break;
                              case "select-one":
                              case "text"      :
                              case "textarea"  :
                              case "hidden"    : valid = true;
                              }
                              if ( valid ) {
                                this.gclOptions[ elem.name ] = elem.value;
                                this.formData.push( { name: elem.name, value: elem.value } );
                              }
                            }
                            this.displayPosition        = { start: 0, end: 0 };
                            this.isSearchFinished       = null;
                            this.resultsWindow.sentence = null;
                            this.resultsWindow.article  = null;
                            this.resultsWindow.table    = null;
                            this.targetVariables        = null;
                            this.searchPosition         = null;
                            this.time                   = 0;
                            this.submittedQuery         = "";
                            this.results                = new Array();
                            this.summary                = new Array();
                            this.summaryStrings         = new Array();
                            this.sortperm               = new Array();
                            this.current_sort           = false;
                            this.setSortEnabled( false );
                          },
  this.runAgain         = function() { 
                            this.initRequest();

                            var postData = new String();
                            postData = "num_results=" + encodeURIComponent( String( this.viewOptions.numberOfResults ) );
                            var query = this.submittedQuery;
                            postData += "&decross=1";
                            postData += "&query=" + encodeURIComponent( query );
                            for ( var i = 0; i < this.summaryStrings.length; ++i ) {
                              postData += "&summary=" + encodeURIComponent( this.summaryStrings[ i ] );
                            }
                            for ( var i = 0; i < arguments.length; i += 2 ) {
                              postData += "&" + arguments[ i ] + "=" + encodeURIComponent( arguments[ i + 1 ] );
                            }

                            var proxy = this;
                            this.runGCLRequest.onreadystatechange = function () { proxy.putResults(); };
                            this.runGCLRequest.open( "POST", rungcl_cgi_name, true );
                            this.runGCLRequest.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
                            this.runGCLRequest.send( postData );
                          },
  this.putQuery         = function() { // call-back function
                            if ( this.runGCLRequest && this.runGCLRequest.readyState == 4 ) {
                              var addr = '' + document.location.pathname;
                              var re = new RegExp('[^/]+$');
                              addr = addr.replace(re, query_cgi_name);
                              pageTracker._trackPageview(addr);
                              if ( ! ( this.runGCLRequest.responseText && this.runGCLRequest.status == 200 ) ) {
                                this.displayError( "Disconnected from query server." );
                              } else if ( ! ( this.runGCLRequest.responseXML && this.runGCLRequest.responseXML.documentElement ) ) {
                                this.displayError( "Query server returned non-XML text." );
                              } else {
                                var xml = this.runGCLRequest.responseXML.documentElement;
                                this.runGCLRequest = null;
                                var error = xml.getElementsByTagName( "error" )[ 0 ];
                                if ( error ) {
                                  this.displayError( error.firstChild.nodeValue );
                                  return;
                                }
                                var query = xml.getElementsByTagName( "gcl_query" )[ 0 ];
                                if ( ( ! query ) || ( ! query.firstChild ) || query.firstChild.nodeValue == "" ) {
                                  this.displayError( "Empty query." );
                                  return;
                                }

                                this.submittedQuery = query.firstChild.nodeValue;

                                var summary = xml.getElementsByTagName( "summary" );
                                for ( var i = 0; i < summary.length; ++i ) {
                                  if ( summary[ i ].firstChild && summary[ i ].firstChild.nodeValue ) {
                                    this.summaryStrings.push( summary[ i ].firstChild.nodeValue );
                                  }
                                }

                                var targets = xml.getElementsByTagName( "target" );
                                this.targetVariables = new Array( targets.length );
                                for ( var i = 0; i < targets.length; ++i ) {
                                  this.targetVariables[ i ] = targets[ i ].firstChild.nodeValue;
                                }

                                this.displayResults( true );
                              }
                            }
                          },
  this.runGCL           = function() { 
                            this.initSearch();
                            // make post data
                            var postData = new String();
                            postData = "num_results=" + encodeURIComponent( String( NUM_PREFETCH_RESULTS ) );
                            for ( var i = 0; i < this.formData.length; ++i ) {
                              postData += "&" + this.formData[ i ].name + "=" + encodeURIComponent( this.formData[ i ].value );
                            }
                            for ( var i = 0; i < arguments.length; i += 2 ) {
                              postData += "&" + arguments[ i ] + "=" + encodeURIComponent( arguments[ i + 1 ] );
                            }
                            if ( oc.lastResult2 != "" ) { postData += "&cobject="  + encodeURIComponent( oc.lastResult2 ); }
                            if ( sc.lastResult2 != "" ) { postData += "&csubject=" + encodeURIComponent( sc.lastResult2 ); } 
                            if ( vc.lastResult2 != "" ) { postData += "&cverb=" + encodeURIComponent( vc.lastResult2 ); }

                            // make a query
                            var proxy = this;
                            this.runGCLRequest.onreadystatechange = function() { proxy.putQuery(); }
                            this.runGCLRequest.open( "POST", query_cgi_name, true );
                            this.runGCLRequest.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
                            this.runGCLRequest.send( postData );
                          },
  this.putResults       = function() { // call-back function
                            if ( this.runGCLRequest && this.runGCLRequest.readyState == 4 ) {
                              if ( this.runGCLRequest.status == 0) {
                                // Dummy value on Gecko browsers - ignore
                              } else if ( ! ( this.runGCLRequest.responseText && this.runGCLRequest.status == 200 ) ) {
                                this.displayError( "Disconnected from data server." );
                              } else if ( ! ( this.runGCLRequest.responseXML && this.runGCLRequest.responseXML.documentElement ) ) {
                                this.displayError( "Data server returned Non-XML text." );
                              } else {
                                var xml = this.runGCLRequest.responseXML.documentElement;
                                this.runGCLRequest = null;
                                var summary = this.createSummary( xml );
                                var results = xml.getElementsByTagName( "result" );
                                if ( results.length == 0 ) {
                                  this.isSearchFinished = summary;
                                } else {
                                  for ( var i = 0; i < results.length; ++i ) {
                                    this.results.push( results[ i ] );
                                    this.summary.push( summary );
                                  }
                                }
                                this.current_sort = false;
                                this.setSortEnabled( true );
                                this.displayResults( false );
                              }
                            }
                          },
  this.createSummary    = function( xml ) {
                            var nextPosition = xml.getElementsByTagName( "next" )[ 0 ];
                            if ( nextPosition ) {
                              this.searchPosition = nextPosition.firstChild.nodeValue;
                            } else {
                              this.searchPosition = "";
                            }
                            var error   = xml.getElementsByTagName( "error"   )[ 0 ];
                            var timeout = xml.getElementsByTagName( "timeout" )[ 0 ];
                            var time    = xml.getElementsByTagName( "time"    )[ 0 ];
                            if ( time ) {
                              this.time += parseFloat( time.firstChild.nodeValue );
                            }
                            var searchProgress = xml.getElementsByTagName( "progress" )[ 0 ];
                            return { next    : nextPosition,
                                     error   : error,
                                     timeout : timeout,
                                     time    : this.time,
                                     progress: searchProgress };
                          },
  this.getDateForSort   = function( element ) {
                            var date = element.getElementsByTagName( "PubDate" );
                            var year, month, day;
                            if (date.length == 0) {
                              date = element.getElementsByTagName( "MedlineDate" );
                              if (date.length == 0) return 0;
                              date = getTextValue(date[ 0 ]);
                              // MedlineDate
                              var matches = date.match(/(\d{4})(?:.*?(Jan|Feb|Mar|Apr|May|Jun|Jul|Sep|Oct|Nov|Dec))?/);
                              if (!matches) {
                                console.error("PubDate unrecognised format: ", date); // DEBUG
                                return 0;
                              }
                              year = matches[ 1 ];
                              month = matches[ 2 ];
                            } else {
                              date = date[ 0 ];
                              // PubDate
                              year = date.getElementsByTagName( "Year" );
                              month = date.getElementsByTagName( "Month" );
                              day = date.getElementsByTagName( "Day" );
                              year = year.length ? getTextValue(year[ 0 ]) : 0;
                              month = month.length ? getTextValue(month[ 0 ]) : 0;
                              day = day.length ? getTextValue(day[ 0 ]) : 0;
                            }
                            year = year ? parseInt(year) : 0;
                            if ( month ) {
                              month = month.toLowerCase();
                              if ( months[ month ] ) month = months[ month ];
                            } else month = 0;
                            day = day ? parseInt(day) : 0;
                            return year * 371 + month * 31 + day;
                          },
  this.setSortEnabled   = function( val ) {
                            document.getElementById('sort').disabled = !val;
                          }
  this.checkSortEnabled = function( el ) {
                            if (!this.results || this.results.length <= 1) return;
                            this.setSortEnabled(el.value != this.current_sort);
                          }
  this.sort             = function() {
                            var criterion;
                            var proxy = this;
                            var what;
                            var radio = $('sortby').childNodes;
                            for (var i in radio) {
                              if (radio[i].name == 'sortby')
                                if (radio[i].checked) what = radio[i].value;
                            }
                            if ( what == undefined ) return;

                            var size = this.results.length;
                            var permsize = this.sortperm.length;
                            this.current_sort = what;
                            this.setSortEnabled( false );

                            // memoise the newly arrived data
                            var el;
                            var rank;
                            for ( var i = permsize; i < size; i++ ) {
                              el = this.results[ i ];
                              rank = publication_ranks[getTextValue(el.getElementsByTagName( "NlmUniqueID" )[ 0 ])];
                              if ( !rank ) rank = lowrank;
                              this.sortperm[ i ] = [ i, this.getDateForSort(el), rank,
                                    el.getElementsByTagName( "Title" )[ 0 ].textContent ];
                            }

                            // sort
                            if ( what == 'rank' ) {
                              criterion = function( a, b ) {
                                    if ( a[ 2 ] == lowrank && b[ 2 ] == lowrank ) {
                                      // ascending title
                                      if ( a[ 3 ] > b[ 3 ] ) return 1;
                                      if ( a[ 3 ] < b[ 3 ] ) return -1;
                                      return 0;
                                    }
                                    return a[ 2 ] - b[ 2 ]; // ascending rank
                                  };
                            } else if ( what == 'date' ) {
                              criterion = function( a, b ) { return b[ 1 ] - a[ 1 ]; } // descending date
                            }
                            this.sortperm.sort( criterion );
                            this.showNext( 0 );
                          }
}

var searchObject= new Search(); 


var Multivalue = function( div, attrs, prog, cand ) { // eg. ("object_div")
  var div = $(div);
  var children = div.childNodes;

  this.field_add = function() {
    var proxy = this;
    var size = children.length;
    if (size > 0) {
      children[size - 1].childNodes[1].value = 'Remove';
    }
    var row = document.createElement('div');

    var field = document.createElement('input');
    for (i in attrs) {
      field.setAttribute(i, attrs[i]);
    }
    field.onkeydown = function(event) {
      if (!event) event = window.event;
      var key = event.keyCode;
      if (key == 13 && this.value != "") {
        proxy.field_add();
        return false;
      } else if ((key == 8 || key == 46) && this.value == "") {
        return !proxy.field_try_delete(this, key);
      }
      return true;
    }
    row.appendChild(field);
    if (prog) {
      new Completion(field, prog, cand, this);
    }

    var button = document.createElement('input');
    button.type = 'button';
    button.value = 'Add';
    button.onclick = function() {
        if (this.value == 'Add') {
          proxy.field_add();
        } else {
          div.removeChild(row);
          children[children.length - 1].value = 'Add';
        }
      }
    button.tabIndex = -1;
    row.appendChild(button);

    div.appendChild(row);
    if ($('advanced_search').hasClassName('visible')) {
      field.focus();
    }
  }

  this.field_try_delete = function(field, key) {
    if (children.length > 1) {
      var row = field.parentNode;
      var nextFocus;
      if (!key) {
      } else if ((key == 8 && children[0] !== row)
          || (key == 46 && children[children.length - 1] === row)) {
        nextFocus = row.previousSibling;
      } else {
        nextFocus = row.nextSibling;
      }
      div.removeChild(field.parentNode);
      children[children.length - 1].childNodes[1].value = 'Add';
      if (key) {
        nextFocus = nextFocus.childNodes[0];
        nextFocus.focus();
        nextFocus.select();
      }
      return true; // success
    }
    return false; // failure
  }

  this.reset = function() {
    while (children.length > 0) {
      div.removeChild(children[0]);
    }
    this.field_add();
  }

  this.field_add();
}


//////////////////////////////////////////////////////////////////////
////
////   Completion
////
//////////////////////////////////////////////////////////////////////

// XXX: not used anywhere; delete later (2008.09.27)
//var Candidate = function ( type, id, symbol, name, species, db_sites ) {
//  this.type     = type;
//  this.id       = id;
//  this.symbol   = symbol;
//  this.name     = name;
//  this.species  = species;
//  this.db_sites = db_sites;
//}

var Completion = function( fid, prog, cand, multi ) { // eg. ("object", "completion.cgi", func)
  this.program     = prog; // server program name such as completion.cgi
  this.window      = null; // completion window, a table element to show candidates
  this.field       = $(fid);  // input field such as "object"
  this.toCandidate = cand; // function to return <tr>...</tr> object for candidate
  this.index       = null; // fucused candidate index, null means no focus
  this.last        = "  "; // last-time string in the field
  this.passEvent   = null; // whether or not to process event after onkeypress
  this.lastResult2 = "";   // last-time completion result(hidden) such as #GDM01709 for query.cgi use
  this.focused     = false;// whether this.field has the focus
  this.multi       = multi; // a Multivalue object, if used

  var isJournalTitle = this.field.name == "journal_title";

  // methods
  this.clear       = function() { // clear the field
                       this.last = "  ";
                       this.lastResult2 = ""
                       this.field.value = "";
                     };
  this.cleanup     = function() { // clean up candidates
                       this.index = null;
                       deleteChildren( this.window ); 
                     }; 
  this.isShown     = function() { // return true if the completion window is shown with candidates
                       if ( this.window && this.window.style.display == "block" && 0 < this.getNumCand() ) { 
                         return true; 
                       } else { 
                         return false; 
                       }
                     };
  this.hide        = function( message ) { // hide the completion window, and cleanup its contents
                       this.window.style.display = "none"; 
                       this.cleanup();
                     };  
  this.show        = function() { // show the completion window
                       var pos = elementPosition( this.field );
                       this.window.style.top        = pos.top + this.field.offsetHeight + "px";
                       this.window.style.left       = pos.left + "px";
                       this.window.style.visibility = "visible";
                       this.window.style.display    = "block";
                     };
  this.getNumCand  = function() { // return number of candidates stored in the window
                       if ( this.window == null ) { return 0; }
                       if ( this.window.childNodes.length == 0 ) { return 0; }
                       return this.window.firstChild.childNodes.length;
                     };
  this.setResult   = function() { // set result in the field and hide the window
                       if ( this.index == null ) { this.hide( "from setResult 1" ); return; }
                       this.field.value = this.getResult( this.index );
                       this.lastResult2 = this.getResult2( this.index );
                       this.index = null; // now, no focus 
                       this.hide( "from setResult 2" );
                     }
  this.getResult   = function( n ) { // return n-th candidate text 
                       return this.window.firstChild.childNodes[ n ].firstChild.innerHTML; // table.tbody.tr[n].td[0].innerHTML
                     };
  this.getResult2  = function( n ) { // return n-th candidate text, but second element if exists
                       if ( this.window.firstChild.childNodes[n].childNodes.length < 2 ) {
                         return this.getResult( n ); // return first elemtn, table.tbody.tr[n].td[0].innerHTML
                       } else {
                         return this.window.firstChild.childNodes[ n ].childNodes[ 1 ].innerHTML; // table.tbody.tr[n].td[1].innerHTML
                       }
                     };
  this.getDecIndex = function() { // get decremented index
                       var number = this.getNumCand(); 
                       var n = ( this.index == null )? ( number - 1 ) : ( this.index - 1 );
                       // TMP 
                       // return ( n < 0 )? ( number - 1 ) : n; 
                       return ( n < 0 )? null : n; 
                     }; 
  this.getIncIndex = function() { // get incremented index
                       var number = this.getNumCand(); 
                       var n = ( this.index == null )? 0 : ( this.index + 1 );
                       // TMP
                       // return ( n >= number )? 0 : n; 
                       return ( n >= number )? null : n; 
                     }; 
  this.onfocus     = function() { this.focused = true; }
  this.onblur      = function() { // when the field lost its focus
                       // onblur deprives event of candidate.onclick .. so, be careful
                       if (multi && this.field.value == "" && multi.field_try_delete(this.field)) {
                         this.hide( "from handleKey/blur" );
                       } else {
                         this.setResult();
                         this.focused = false;
                       }
                     }; 
  this.highlight   = function ( n ) { // function to highlight the n-th candidate and dehighlight the others
                       var number = this.getNumCand(); 
                       for ( var i = 0; i < number; ++i ) {
                         if ( i == n ) { // highlight
                           this.window.firstChild.childNodes[i].style.backgroundColor = color.backgroundSelected;
                           this.window.firstChild.childNodes[i].style.color           = color.foregroundSelected;
                         } else { // de-highlight
                           this.window.firstChild.childNodes[i].style.backgroundColor = color.background;
                           this.window.firstChild.childNodes[i].style.color           = color.foreground;
                         }
                       }
                     };
  this.handleKey   = function( event ) { // handle key input if candidates have already been displayed, 
                                         // this function handles only special inputs such as space or enter
                                         // return true/false for further event processing
                       if (!event) event = window.event;
                       var keyCode = ( event.charCode == 32 ) ? 32 : event.keyCode; // for space key handling
                       if (isJournalTitle && keyCode == 32) keyCode = 1032;

                       this.passEvent = false;
                       if (multi && (keyCode == 8 || keyCode == 46)
                           && this.field.value == "" && multi.field_try_delete(this.field, keyCode)) {
                         if (this.request) this.request.canceled = true;
                         this.hide( "from handleKey/delete" );
                         return false;
                       }
                       if ( ! this.isShown() ) {
                         if ( keyCode == 13 && multi && this.field.value != "" ) {
                           multi.field_add();
                           return false;
                         } else {
                           // window not shown, quit
                           this.passEvent = true;  return true;
                         }
                       }
                       switch ( keyCode ) {
                       case 37: // left  key
                       case 38: // up key
                         this.index = this.getDecIndex();
                         this.highlight( this.index ); 
                         return false; // stop further event processing by the browser
                       // XXX: pass tab to the browser and don't execute ajax
                       case  9: // tab key 
                         this.passEvent = false;
                         return true;
                       //case 32: // space key
                       case 39: // right key
                       case 40: // down key
                         // TMP
                         //if ( this.isShown() && this.getNumCand() == 1 ) { // special case, only one candidate
                         //  this.index = 0;
                         //  this.setResult(); 
                         //  return false; // stop further event processing by the browser
                         // } // else go through
                         this.index = this.getIncIndex();
                         this.highlight( this.index ); 
                         return false; // stop further event processing by the browser
                       case 13: // enter key
                         this.setResult(); 
                         if ( multi ) {
                           multi.field_add();
                         }
                         return false; // stop further event processing by the browser
                       case 27: // escape key
                         this.hide( "from handleKey" );
                         return false; // stop further event processing by the browser
                       }
                       this.passEvent = true;
                       return true; // keep event processing 
                     };

  this.onComplete  = function( request, key, self ) { // call-back function to display completion 
                       var xml = request.responseXML.documentElement;
                       self.cleanup();
                       // if there is no candidate or the input field has already lost the focus,
                       // hide the completion window and quit
                       if ( key == 13 || xml.childNodes.length == 0 || ! self.focused ) { 
                         self.hide( "from onComplete" );
                         return;  
                       } else { 
                         self.show(); 
                       }

                       // add all candidates to the window
                       var tbody = document.createElement( "tbody" );
                       var n = xml.childNodes.length;
                       for ( var i = 0; i < n; ++i )
                       {  
                         var candidate = self.toCandidate( xml.childNodes[ i ] ); 
                         candidate.className = "" + i; // numbering
                         candidate.onmouseover = function() { // highlight by mouse
                          var number = parseInt( this.className );
                           self.highlight( number ); 
                           self.index = number;
                         };
                         tbody.appendChild( candidate );
                       }
                       self.window.appendChild( tbody );
                     };

  this.execute     = function( event, text ) { // main function, call this from html(onkeyup) 
                       if (!event) event = window.event;
                       if ( ! this.passEvent )  { return; } // handleKey(onkeypress) says no need to execute
                       if ( text == this.last ) { return; } // quit if there is no change in the field
// GT 2010-01-21: Try to handle space. Why was it disallowed?
//                       if ( !isJournalTitle && text.indexOf( ' ' ) != -1 ) {
//                         return; // if text contains space, quit, it's vain
//                       }
                       this.last        = text; // save the field value 
                       this.lastResult2 = "";   // last-time completion cleared
                       var parameter = "query=" + encodeURIComponent( text );
                       var key = event.keyCode;
                       var self = this; // to use below

                       if (this.request) this.request.canceled = true;

                       new Ajax.Request(
                         this.program,
                         {
                           "method"     : "post",
                           "parameters" : parameter,
                           onCreate     : function( request ) { self.request = request; },
                           onComplete   : function( request ) { if ( !request.canceled ) self.onComplete( request, key, self ); },
                           onFailure    : function( request ) { alert( "failure" ); },
                           onException  : function( request, exception ) { alert( "Exception: " + exception.message ); }
                         }
                       );
                     };

  // initialization methods
  this.initWindow  = function() { // initial settings for this.window
                       if ( this.window ) { return; }
                       this.window = document.createElement( "table" ); 
                       document.body.appendChild( this.window );
                       this.window.style.position        = "absolute";
                       this.window.cellSpacing           = "0px";
                       this.window.cellPadding           = "2px";
                       this.window.style.borderStyle     = "solid";
                       this.window.style.borderWidth     = "1px";
                       this.window.style.borderColor     = color.border;
                       this.window.style.backgroundColor = color.background;
                       this.window.style.padding         = "0px";
                       this.window.style.display         = "none"; // or block
                       this.window.style.visibility      = "hidden"; // or visible
                       this.window.style.fontSize        = "14px";
                     }

  var proxy = this;
  this.field.onkeydown = function(event) { return proxy.handleKey(event); };
  this.field.onkeyup = function(event) { return proxy.execute(event, this.value); };
  this.field.onfocus = function() { return proxy.onfocus(); };
  this.field.onblur = function() { return proxy.onblur(); };

  // initialization
  this.initWindow();
}

var titleCandidate = function( c ) {
  var candidate  = document.createElement( "tr" );
  var titleString = c.childNodes[ 0 ].firstChild.nodeValue; // <candidate>.<title>.textContent.nodeValue 
  var title       = document.createElement( "td" );
  candidate.style.padding = "1px 3px 1px 3px";
  title.appendChild( document.createTextNode( titleString ) ); 
  candidate.appendChild( title ) ;
  return candidate;
}

var verbCandidate = function( c ) {  // c is a <candidate> node
  var candidate  = document.createElement( "tr" );
  var verbString = c.childNodes[ 0 ].firstChild.nodeValue; // <candidate>.<verb>.textContent.nodeValue 
  var regulation = c.childNodes[ 1 ].firstChild.nodeValue; // <candidate>.<regulation>.textContent.nodeValue
  var verb       = document.createElement( "td" );
  var eventClass = document.createElement( "td" );
  candidate.style.padding = "1px 3px 1px 3px";
  verb.appendChild( document.createTextNode( verbString ) ); 
  eventClass.appendChild( document.createTextNode( regulation ) );
  eventClass.style.display = "none";
  candidate.appendChild( verb ) ;
  candidate.appendChild( eventClass );
  return candidate;
}

// this program is to separate content-dependent parts of the completion system
// takes c (<gene>...</gene> xml) 
// returns a object of <tr> which will be appended to the candidate list in the window
var geneCandidate = function( c ) { 
  var candidate = document.createElement( "tr" );
  var typeString     = c.tagName;  
  var idString       = c.childNodes[ 0 ].firstChild.nodeValue;
  var symbolString   = ( c.childNodes[ 1 ].firstChild )? c.childNodes[ 1 ].firstChild.nodeValue : "";
  var nameString     = ( c.childNodes[ 2 ].firstChild )? c.childNodes[ 2 ].firstChild.nodeValue : "";
  var speciesString  = ( c.childNodes[ 3 ].firstChild )? c.childNodes[ 3 ].firstChild.nodeValue : "";
  var db_sitesString = ( c.childNodes[ 4 ].firstChild )? c.childNodes[ 4 ].firstChild.nodeValue : "";
  // 2008-01-29: Disease completion bug fix
  // var isComplete = ( symbolString == "" )? false : true ;
  var isComplete = ( speciesString == "" )? false : true ;
  var result    = document.createElement( "td" ); // resultant text for completion such as nrp-7
  var result2   = document.createElement( "td" ); // resultant text for completion (hidden) such as GDM150967
  var type      = document.createElement( "td" );
  var symbol    = document.createElement( "td" );
  var name      = document.createElement( "td" );
  var species   = document.createElement( "td" );
  var id        = document.createElement( "td" );
  candidate.style.padding = "1px 3px 1px 3px";

  // hidden field, this content is used for final substitution
  result.appendChild   ( document.createTextNode( symbolString ) );  
  result2.appendChild   ( document.createTextNode( idString ) );  
  result.style.display = "none";
  result2.style.display = "none";
  candidate.appendChild( result ) ;
  candidate.appendChild( result2 ) ;

  if ( isComplete ) {
    type.appendChild   ( document.createTextNode( typeString    + "  " ) ); 
    symbol.appendChild ( document.createTextNode( symbolString  + "  " ) );
    name.appendChild   ( document.createTextNode( nameString    + "  " ) );
    species.appendChild( document.createTextNode( speciesString + "  " ) );
    id.appendChild     ( document.createTextNode( idString      + "  " ) );

    species.className = dblink_class_map[ speciesString ] + "x" ;
    type.style.color  = color.type;
    id.style.color    = color.id;

    candidate.appendChild( type ) ;
    candidate.appendChild( symbol ) ;
    candidate.appendChild( species ) ;
    candidate.appendChild( name ) ;
    
  } else {
    type.appendChild   ( document.createTextNode( typeString    + "  " ) ); 
    symbol.appendChild ( document.createTextNode( symbolString  + "  " ) );
    id.appendChild     ( document.createTextNode( idString      + "  " ) );

    type.style.color = color.type;
    id.style.color   = color.id;

    candidate.appendChild( type );
    candidate.appendChild( symbol );
    candidate.appendChild( id );
  } 
  return candidate;
}


//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
////
////  "clear" button
////     - for clearing all the field even when a page was generated
////       by search.cgi with some initial settings (like subject=p53)
////
//////////////////////////////////////////////////////////////////////
function initializeInputFields() {
    // subject, verb, and object
    $( "subject" ).value = "";
    $( "verb" ).value = "";
    $( "object" ).value = "";

    // ontology checkboxes
    $( "subject_ontology" ).checked = true;
    $( "verb_ontology" ).checked = true;
    $( "object_ontology" ).checked = true;

    // base-form checkboxes
    $( "subject_base_form" ).checked = false;
    $( "verb_base_form" ).checked = true;
    $( "object_base_form" ).checked = false;

    // subject category checkboxes
    $( "subject_category_gene" ).checked = true;
    $( "subject_category_product" ).checked = true;
    $( "subject_category_disease" ).checked = true;

    // object category checkboxes
    $( "object_category_gene" ).checked = true;
    $( "object_category_product" ).checked = true;
    $( "object_category_disease" ).checked = true;

    // verb modifier -> disabled because the default value of verb_ontology is true
    $( "verb_modifier" ).value = "";

    // meta-document info fields
    $( "additional_keyword" ).value = "";
    $( "journal_author" ).value = "";
    titlemv.reset();
    meshmv.reset();

    $$("#sentence_type_checkboxes input").each(function(el) {
        el.checked = true;
      });
    
    $( "pmid" ).value = "";
}

function verbModifierChanged(tfMod) {
  if (tfMod.value != "") {
    $('verb_ontology').checked = false;
  }
  return true;
}

function verbOntologyRedisplay() {
  $('verb_ontology_warning').style.display = "none";
  $('verb_ontology_checkbox').style.display = "block";
}

function verbOntologyChanged(cbOnt) {
  var tfMod = $('verb_modifier');
  if (cbOnt.checked && tfMod.value != "") {
    $('verb_ontology_checkbox').style.display = "none";
    $('verb_ontology_warning').style.display = "block";
    tfMod.select();
    tfMod.focus();
    cbOnt.checked = false;
    setTimeout('verbOntologyRedisplay()', 2000);
  }
  return true;
}

var oc, sc, vc;
var meshmv, titlemv;
function onLoadHandler(topPage) {
  sc = new Completion( "subject",       cgi1, geneCandidate  );  // completion for subject
  vc = new Completion( "verb",          cgi2, verbCandidate  );  // completion for verb
  oc = new Completion( "object",        cgi1, geneCandidate  );  // completion for object
  //new Completion( "keyword",       cgi1, geneCandidate  );  // completion for keyword
  //new Completion( "journal_title", cgi3, titleCandidate );  // completion for journal title

  meshmv = new Multivalue( "mesh_heading_div", { name: "mesh_heading_list", size: 40 } );
  titlemv = new Multivalue( "journal_title_div", { name: "journal_title", size: 40 }, cgi3, titleCandidate);

  initPopups();

  if (!topPage) searchObject.runGCL();
}

