// QuickSearch script for JabRef HTML export 
// Version: 2.0
//
// Copyright (c) 2006-2008, Mark Schenk, Magnus Knuth
//
// This software is distributed under a Creative Commons Attribution 3.0 License
// http://creativecommons.org/licenses/by/3.0/

// Some features:
// + optionally searches Abstracts and Reviews
// + allows RegExp searches
//   e.g. to search for entries between 1980 and 1989, type:  198[0-9]
//   e.g. for any entry ending with 'symmetry', type:  symmetry$
//   e.g. for all reftypes that are books: ^book$, or ^article$
//   e.g. for entries by either John or Doe, type john|doe
// + easy toggling of Abstract/Review/BibTeX
// + ul/li modification by Magnus Knuth

// Speed optimisation introduced some esoteric problems with certain RegExp searches
// e.g. if the previous search is 200[-7] and the next search is 200[4-7] then the search doesn't work properly until the next 'keyup'
// hence the searchOpt can be turned off for RegExp adepts
var searchOpt = true;

if (window.addEventListener) {
  window.addEventListener('load', initSearch, false); }
else if (window.attachEvent) {
  window.attachEvent('onload', initSearch); }

function initSearch() {
  // basic object detection
  if (!document.getElementById || !document.getElementsByTagName) { return; }
  if (!document.getElementById('qs')) { return; }

  // find bibentries
  searchLists = getElementsByClass('biblist', document, 'ul');

  allEntries = new Array();
  for (var i = 0; i < searchLists.length; i++) {
    allEntries = allEntries.concat(getElementsByClass('bibentry', searchLists[i], 'li'));
  }

  // find infoEntries (e.g. abstract, review, bibtex)
  absEntries = new Array(); revEntries = new Array(); bibEntries = new Array();
  for (var i = 0; i < allEntries.length; i++) {
    absEntries = absEntries.concat(getElementsByClass('abstract', allEntries[i]));
    revEntries = revEntries.concat(getElementsByClass('review', allEntries[i]));
    bibEntries = bibEntries.concat(getElementsByClass('bibtex', allEntries[i]));
  }

  // number of entries
  numEntries = allEntries.length;
  numAbs = absEntries.length;
  numRev = revEntries.length;
  numBib = bibEntries.length;

  // find the query field
  qsfield = document.getElementById('qsfield');

  // previous search term; used for speed optimisation
  prevSearch = '';

  // find statistics location
  stats = document.getElementById('stat');
  setStatistics(-1);

  // shows the searchfield
  document.getElementById('qs').style.display = 'block';
  document.getElementById('qsfield').onkeyup = testEvent;
}

function quickSearch(tInput){
  if (tInput.value.length == 0) {
    showAll();
    setStatistics(-1);
    qsfield.className = '';
    return;
  } else {
    // only search for valid RegExp
    try {
      var searchText = new RegExp(tInput.value,'i')
      closeAllInfo();
      qsfield.className = '';
    } catch (err) {
      prevSearch = tInput.value;
      qsfield.className = 'invalidsearch';
      return;
    }
  }

  // count number of hits
  var hits = 0;

  // start looping through all entries
  for (var i = 0; i < numEntries; i++){
    cRow = allEntries[i];
    // only show search the cells if it isn't already hidden OR if the search term is getting shorter, then search all
    // some further optimisation is possible: if the search string is getting shorter, and the row is already visible, skip it. Then be careful with hits!

    if(!searchOpt || cRow.className.indexOf('noshow')==-1 || tInput.value.length <= prevSearch.length) {
      var found = false;

      var inCells = cRow.getElementsByTagName('span');
      var numCols = inCells.length;

      for (var j = 0; j < numCols; j++) {
        cCell = inCells[j];        var t = cCell.innerText?cCell.innerText:getTextContent(cCell);
        if (t.search(searchText) != -1) {
          found=true; 
          break;
        } 
      }

      if (found) {
        removeClass(cRow, 'noshow');
        addClass(cRow, 'show');
        hits++;      } else {
        removeClass(cRow, 'show');
        addClass(cRow, 'noshow');
      }    }
  }

  // update statistics
  setStatistics(hits);

  // set previous search value
  prevSearch = tInput.value;
}

function toggleInfo(articleid, info) {
  var entry = document.getElementById(articleid);
  var abs = document.getElementById('abs_'+articleid);
  var rev = document.getElementById('rev_'+articleid);
  var bib = document.getElementById('bib_'+articleid);
	
  if (abs && info == 'abstract') {
    if (abs.className.indexOf('abstract') != -1) {
      abs.className.indexOf('noshow') == -1?abs.className = 'abstract noshow':abs.className = 'abstract';
    }
  } else if (rev && info == 'review') {
    if (rev.className.indexOf('review') != -1) {
      rev.className.indexOf('noshow') == -1?rev.className = 'review noshow':rev.className = 'review';
    }
  } else if (bib && info == 'bibtex') {
    if (bib.className.indexOf('bibtex') != -1) {
      bib.className.indexOf('noshow') == -1?bib.className = 'bibtex noshow':bib.className = 'bibtex';
    }		
  } else { 
    return;
  }

  // check if one or the other is available
  var revshow = false;
  var absshow = false;
  var bibshow = false;
  (abs && abs.className.indexOf('noshow') == -1)?absshow = true:absshow = false;
  (rev && rev.className.indexOf('noshow') == -1)?revshow = true:revshow = false;	
  (bib && bib.className == 'bibtex')?bibshow = true:bibshow = false;

  // highlight original entry
  if(entry) {
    if (revshow || absshow || bibshow) {
      entry.className = 'bibentry highlight show';
    } else {
      entry.className = 'bibentry show';
    }		
  }
}

function setStatistics (hits) {
  if (hits < 0) { hits=numEntries; }
  if (stats) { stats.firstChild.data = hits + '/' + numEntries}
}

function getTextContent(node) {
  // Function written by Arve Bersvendsen
  // http://www.virtuelvis.com
	
  if (node.nodeType == 3) {
    return node.nodeValue;
  } // text node
  if (node.nodeType == 1) { // element node
    var text = [];
    for (var chld = node.firstChild;chld;chld=chld.nextSibling) {
      text.push(getTextContent(chld));
    }
    return text.join('');
  }
  return ''; // some other node, won't contain text nodes.
}

function showAll(){
  // first close all abstracts, reviews, etc.
  closeAllInfo();
  
  for (var i = 0; i < numEntries; i++){
    removeClass(allEntries[i], 'noshow');
    addClass(allEntries[i], 'show'); 
  }
}

function closeAllInfo(){
  for (var i = 0; i < numAbs; i++){
    removeClass(absEntries[i], 'show');
    addClass(absEntries[i], 'noshow'); 
  }

  for (var i = 0; i < numRev; i++){
    removeClass(revEntries[i], 'show');
    addClass(revEntries[i], 'noshow'); 
  }
}

function testEvent(e){
  if (!e) var e = window.event;
  quickSearch(this);
}

function clearQS() {
  qsfield.value = '';
  quickSearch(qsfield);
}

function redoQS(){
  showAll();
  quickSearch(qsfield);
}

/*
 * UTILITIES
 */

function getElementsByClass(cls, domNode, tagName) {
  if (domNode == null) domNode = document;
  if (tagName == null) tagName = '*';
  var eles = new Array();
  var tags = domNode.getElementsByTagName(tagName);

  for (i = 0, j = 0; i < tags.length; i++) {
    if (hasClass(tags[i], cls)) eles[j++] = tags[i];
  }
  return eles;
}

function hasClass(ele,cls) {
  // http://www.openjs.com/scripts/dom/class_manipulation.php
  return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
 
function addClass(ele,cls) {
  // http://www.openjs.com/scripts/dom/class_manipulation.php
  if (!hasClass(ele,cls)) ele.className += ' '+cls;
}
 
function removeClass(ele,cls) {
  // http://www.openjs.com/scripts/dom/class_manipulation.php
  if (hasClass(ele,cls)) {
    var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
    ele.className=ele.className.replace(reg,' ');
  }
}

