// QuickSearch script for tables
//
// Copyright (c) 2006, Mark Schenk
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//

// Some features:
// + combination of CSS/JavaScript degrades nicely
// + easy to set number of columns to search
// + 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$
// + easy toggling of Abstract/Review/BibTeX

if (window.addEventListener) {
	window.addEventListener("load",initSearch,false) }
else if (window.attachEvent) {
	window.attachEvent("onload", initSearch); }

var searchTable

function initSearch() {
	// basic object detection
	if(!document.getElementById || !document.getElementsByTagName) { return; }

	// check if QuickSearch table AND search area is present 
	if (!document.getElementById('qstable')||!document.getElementById('qs')) { return; }

	// give id of the table that has QuickSearch
	// is global variable on purpose
	searchTable = document.getElementById('qstable');
	
	setStatistics(-1)

	document.getElementById('qs').style.display = 'block';
	document.getElementById('qsfield').onkeyup = testEvent;
}

function quickSearch(tInput){
	searchText = new RegExp(tInput.value,"i");
	var allRows = searchTable.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
	var inRows = new Array();

	for (var i=0, k=0; i<allRows.length;i++) {
		if (allRows[i].className.indexOf('entry') != -1) {
	       	inRows[k++] = allRows[i];
		} else if (allRows[i].className.indexOf('noshow') == -1) {
		allRows[i].className = allRows[i].className + ' noshow';
		}
	}

	// find/set number of searchable columns
	// by default script searches all columns
	var cols = searchTable.getElementsByTagName('thead')[0].getElementsByTagName('th').length;
	// to set a fixed number of columns, uncomment next line
	// var cols = 4  // search the left 'cols' columns

	// count number of hits
	var hits = 0;

	// start looping through all rows
	for (var i = 0; cRow = inRows[i]; i++){
	
	inCells = cRow.getElementsByTagName('td');
	var gevonden = false; 
  
	for (var j=0; j<cols; j++) { // only first 'cols' columns
		cCell = inCells[j];
		var t = cCell.innerText?cCell.innerText:getTextContent(cCell);
		if ((tInput.value.length == 0) || (t.search(searchText) != -1)){ gevonden=true; } 
	}

	gevonden == true?cRow.className = 'entry show':cRow.className = 'entry noshow';
	gevonden == true?hits++:hits=hits;
	}

	// update statistics
	setStatistics(hits)
}

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);

	// Get the abstracts/reviews/bibtext in the right location
	// in unsorted tables this is always the case, but in sorted tables it is necessary. 
	// Start moving in reverse order, so we get: entry, abstract,review,bibtex
	if (searchTable.className.indexOf('sortable') != -1) {
		if(bib) { entry.parentNode.insertBefore(bib,entry.nextSibling); }
		if(rev) { entry.parentNode.insertBefore(rev,entry.nextSibling); }
		if(abs) { entry.parentNode.insertBefore(abs,entry.nextSibling); }
	}
	
	// toggle visibility
	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 = 'entry highlight show';
		} else {
		entry.className = 'entry show';
		}		
	}
	
	// When there's a combination of abstract/review/bibtex showing, need to add class for correct styling
	if(absshow) {
		(revshow||bibshow)?abs.className = 'abstract nextshow':abs.className = 'abstract';
	} 
	if (revshow) {
		bibshow?rev.className = 'review nextshow': rev.className = 'review';
	}
	
}

function setStatistics (hits) {
	var allRows = searchTable.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
	var entries = 0
	for (var i=0; i<allRows.length;i++) {
		if (allRows[i].className.indexOf('entry') != -1) {
	       	entries++;
		}
	}
	
	if(hits < 0) { hits=entries; }

	var stats = document.getElementById('stat');
	if(stats) { stats.firstChild.data = hits + '/' + entries}
}

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 testEvent(e){
	if (!e) var e = window.event;
	quickSearch(this);
}

function clearQS() {
	qsfield = document.getElementById('qsfield'); 
	qsfield.value = '';
	quickSearch(qsfield);
}

function redoQS(){
	qsfield = document.getElementById('qsfield'); 
	quickSearch(qsfield);
}

// Sort Table Script
//
// Copyright (c) 2006, Mark Schenk
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.

// NB: slow as molasses in FireFox, especially when sorting cols with a lot of text.
// An optimization is implemented which makes speed bearable, toggled by the following variable
var SPEED_OPT = true;
// a bit of browser preference: Opera does not need optimization
if(window.opera) { SPEED_OPT=false; }
// the optimization has one limitation on the functionality: when sorting search
// results, the expanded info, e.g. bibtex/review, is collapsed. In the non-optimized
// version they remain visible.


if (window.addEventListener) {
	window.addEventListener("load",initSortTable,false) }
else if (window.attachEvent) {
	window.attachEvent("onload", initSortTable); }

function initSortTable() {
var alltables = document.getElementsByTagName('table');
for(i=0;i<alltables.length;i++) {
	var currentTable = alltables[i];
	if(currentTable.className.indexOf('sortable') !=-1) {
		var thead = currentTable.getElementsByTagName('thead')[0];
		thead.title = 'Click on any column header to sort';
		for (var i=0;cell = thead.getElementsByTagName('th')[i];i++) {
		 cell.onclick = function () { resortTable(this); };
		}
                thead.getElementsByTagName('th')[2].onclick();
                thead.getElementsByTagName('th')[2].onclick();
	}
}
}

var SORT_COLUMN_INDEX

function resortTable(td) {
	var column = td.cellIndex;
	var table = getParent(td,'TABLE');

	var allRows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
	var newRows = new Array();

	for (var i=0, k=0; i<allRows.length;i++) {

		var rowclass = allRows[i].className;

		if (rowclass.indexOf('entry') != -1) {
	       	newRows[k++] = allRows[i];
		}
		
		if (SPEED_OPT) {
		// remove highlight class
		allRows[i].className = rowclass.replace(/highlight/,'');
		// close information
		if(rowclass.indexOf('entry') == -1 && rowclass.indexOf('noshow') == -1) { allRows[i].className = rowclass + ' noshow';}
		} 
	}


	// If other sort functions are deemed necessary (e.g. for
	// dates and currencies) they can be added.
	var sortfn = ts_sort_firstchild_caseinsensitive;
	SORT_COLUMN_INDEX = column;
	newRows.sort(sortfn);

	// create a container for showing sort arrow
	var arrow =  td.getElementsByTagName('span')[0];
	if (!arrow) { var arrow = td.appendChild(document.createElement('span'));}
	
	if (td.className) {
		if (td.className.indexOf('sort_asc') !=-1) {
			td.className = td.className.replace(/_asc/,"_des");
			newRows.reverse();
			arrow.innerHTML = '&uArr;';
		} else if (td.className.indexOf('sort_des') !=-1) {
			td.className = td.className.replace(/_des/,"_asc");
			arrow.innerHTML = '&dArr;';
		} else { 
			td.className += ' sort_asc'; 
			arrow.innerHTML = '&dArr;';
		}
	} else {
		td.className += 'sort_asc';
		arrow.innerHTML = '&dArr;';
	}
	
	// Remove the classnames and up/down arrows for the other headers
	var ths = table.getElementsByTagName('thead')[0].getElementsByTagName('th');
	for (var i=0; i<ths.length; i++) {
		if(ths[i]!=td && ths[i].className.indexOf('sort_')!=-1) {
		// argh, moronic JabRef thinks (backslash)w is an output field!!
		//ths[i].className = ths[i].className.replace(/sort_(backslash)w{3}/,"");
		ths[i].className = ths[i].className.replace(/sort_asc/,"");
		ths[i].className = ths[i].className.replace(/sort_des/,"");

		// remove span
		var arrow =  ths[i].getElementsByTagName('span')[0];
		if (arrow) { ths[i].removeChild(arrow); }
		}
	}

	// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
	for (i=0;i<newRows.length;i++) { 
		table.getElementsByTagName('tbody')[0].appendChild(newRows[i]);

		if(!SPEED_OPT){
		// moving additional information, e.g. bibtex/abstract to right locations
		// this allows to sort, even with abstract/review/etc. still open
		var articleid = newRows[i].id;

		var entry = document.getElementById(articleid);
		var abs = document.getElementById('abs_'+articleid);
		var rev = document.getElementById('rev_'+articleid);
		var bib = document.getElementById('bib_'+articleid);		
	
		var tbody = table.getElementsByTagName('tbody')[0];
		// mind the order of adding the entries
		if(abs) { tbody.appendChild(abs); }
		if(rev) { tbody.appendChild(rev); }
		if(bib) { tbody.appendChild(bib); }
		}
	}
}

function ts_sort_firstchild_caseinsensitive(a,b) {
	// only search in .firstChild of the cells. Speeds things up tremendously in FF
	// problem is that it won't find all the text in a cell if the firstChild is an element
	// or if there are other elements in the cell. Risky fix, but the speed boost is worth it.
	var acell = a.cells[SORT_COLUMN_INDEX];
	var bcell = b.cells[SORT_COLUMN_INDEX];
	
	acell.firstChild? aa = getTextContent(acell.firstChild).toLowerCase():aa = "";
	bcell.firstChild? bb = getTextContent(bcell.firstChild).toLowerCase():bb = "";

	if (aa==bb) return 0;
	if (aa<bb) return -1;
	return 1;
}

function ts_sort_caseinsensitive(a,b) {
	aa = getTextContent(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
	bb = getTextContent(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
	if (aa==bb) return 0;
	if (aa<bb) return -1;
	return 1;
}

function ts_sort_default(a,b) {
	aa = getTextContent(a.cells[SORT_COLUMN_INDEX]);
	bb = getTextContent(b.cells[SORT_COLUMN_INDEX]);
	if (aa==bb) return 0;
	if (aa<bb) return -1;
	return 1;
}

function getParent(el, pTagName) {
	if (el == null) { 
		return null;
	} else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {
		return el;
	} else {
		return getParent(el.parentNode, pTagName);
	}
}