var MYDEBUG = false;

// former rollover.js

function swap(imgName){
   var newImgName = imgName;
   var index = newImgName.indexOf("-");
   
    if (index > -1) {newImgName = newImgName.substring(0, index);}
    imgOn=eval(newImgName + "On.src");
    document[imgName].src= imgOn;    
 }

function restore(imgName){
    imgOff=eval(imgName + "Off.src");
    document[imgName].src= imgOff; 
}


function restoreAll(){

 for (i=0; i<document.images.length; i++) 
  {        
    if( eval("typeof document.images[" + i + "].name") != "undefined" )
    {
      var imgName = document.images[i].name.toString();
      if (imgName.indexOf("rDocs-") == 0) 
      {
        document.images[i].src = machine + "/images/related/related-info.gif";
      }
   }
 }
      
}

//macromedia code

function MM_swapImgRestore() { //v3.0
  var i, x, a=document.MM_sr;
  for (i=0; a && i < a.length && (x=a[i]) && x.oSrc; i++) 
    x.src=x.oSrc;
}

function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_findObj(n, d) { //v4.0
  var p,i,x;  
  
  if(!d) d=document; 
  
  if( (p=n.indexOf("?") )> 0&&parent.frames.length) {
  
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);
  }
  
  if(!(x=d[n])&&d.all) x=d.all[n]; 
  
  for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  
  if(!x && document.getElementById) x=document.getElementById(n); 
  
  return x;
  
}

function MM_swapImage() {
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}





// end of former rollover.js




/**
*******************************************************************************
DecoderSunglasses

AL May 2008 - add additional column to table with link to Decoder maintainance GUI per product/docId/row
*******************************************************************************
**/
function DecoderSunglasses() {

	// store the original table
	this.initTable = document.getElementById(DecoderSunglasses.ID_TABLE_DIV).innerHTML; //new Table(DecoderSunglasses.ID_TABLE_DIV); 
	
	// activate sunglasses?
	if(this.isActive()) {
		//this.tableNode = document.getElementById(DecoderSunglasses.ID_TABLE_DIV).getElementsByTagName("table")[0];
		this.tableNode = document.getElementById("parametric-table");
		this.tbody = this.tableNode.getElementsByTagName("tbody")[0];
		this.tbRows = this.tbody.getElementsByTagName("tr");
		this.thead = this.tableNode.getElementsByTagName("thead")[0];
		this.thRows = this.thead.getElementsByTagName("tr");
		this.colDefs = this.tableNode.getElementsByTagName("col");

		this.schemesAvailableRequest(this.xmlhttp);
/* SAMPLE RESPONE

						<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://visha1.inetu.net:8080/cocoon-2.1.11/vpm/my_resources/scheme_inventory_response.dtd">
						<scheme_inventory_response>
						  <for_document id="doc_20008" scheme_found="true">
						    <scheme_found file="crcw"/>
						  </for_document>
						  <for_document id="doc_28313" scheme_found="true">
						    <scheme_found file="a"/>
						    <scheme_found file="a2"/>
						  </for_document>
						</scheme_inventory_response>
*/

		if(this.xmlhttp.readyState == 4) {
			//alert(this.xmlhttp.responseText);
			//document.write(this.xmlhttp.responseText);
			//document.getElementById('lala').value = this.xmlhttp.responseText;
		}
		this.bespectacle();
	}
}


/**
	Need fully qualified target host for XHR here:
**/
//DecoderSunglasses.AJAX_URL = "http://lxmv37.corp.vishayint.com:80/partform/xhr";
/*
NEED REVERSE PROXY FOR THIS TO WORK AS XHR MAY ONLY GO TO ORGINAL SERVER
ProxyRequests Off
ProxyPass /partform/xhr http://lxmv33.corp.vishayint.com:8080/vpm-webapp-1.0-SNAPSHOT/vpm/vpm/xhr
ProxyPassReverse /partform/xhr http://lxmv33.corp.vishayint.com:8080/vpm-webapp-1.0-SNAPSHOT/vpm/vpm/xhr
*/
//DecoderSunglasses.AJAX_URL = "/partform/xhr";
DecoderSunglasses.AJAX_URL = "/partform/xhr";
//DecoderSunglasses.AJAX_URL = "http://pcse156.eu.vishayint.com/partform/xhr";
//DecoderSunglasses.AJAX_URL = "http://"+window.location.host.split(":")[0]+"/partform/xhr";
//DecoderSunglasses.AJAX_URL = "/xhr";

//alert(window.location.href);
/**
	Partform URL
**/
DecoderSunglasses.VPM_URL = "http://lxmv33.corp.vishayint.com:8080/vpm-webapp-1.0-SNAPSHOT/vpm/vpm/do-general.flow";

// Contants used for switching sunglasses on and off
DecoderSunglasses.ID_TABLE_DIV = "lsttbl";
DecoderSunglasses.COOKIE_NAME_ACTIVATE      = "sunglasses";
DecoderSunglasses.COOKIE_VALUE_ACTIVATE_ON  = "on";
DecoderSunglasses.COOKIE_VALUE_ACTIVATE_OFF = "off";






// is cookie set to activate sunglasses?
function DecoderSunglasses_isActive() {
	var result = false;

//	var activateCookieVal = _getCookie(DecoderSunglasses.COOKIE_NAME_ACTIVATE);
	var activateCookieVal = getCookie(DecoderSunglasses.COOKIE_NAME_ACTIVATE);
	result = (activateCookieVal && activateCookieVal == DecoderSunglasses.COOKIE_VALUE_ACTIVATE_ON);

	return result;
}

function DecoderSunglasses_bespectacle() {
	/* 
	===========================================================================
	Update col definitions
	===========================================================================
	*/
	var lastColDef = this.colDefs[this.colDefs.length - 1];
	this.tableNode.insertBefore(document.createElement("col"), lastColDef.nextSibling);

	/* 
	===========================================================================
	Update headline rows
	===========================================================================
	*/
	window.status = "Adding table headers for decoder sunglasses";
	for(var i = 0; i < this.thRows.length; i++) {
		var thRow = this.thRows[i];


		if((thRow)) { // && (thRow.getElementsByTagName("th").length > 0)
			if(i == 1) {
				var ths = this.thRows[1].getElementsByTagName("th");

				// get the last id from 'sortXYZ', ie. the number XYZ. increment and use as new (sortXYZ+1) id for decoder column
				var lastSortId = -1;
				var thsLen = ths.length;
				for(var cs = (thsLen - 1); cs >= 0 ; cs--) {
					var cSortCell = ths[cs];
					var btns = cSortCell.getElementsByTagName("img");
					for(i=0; i < btns.length; i++) {
						var idAttr = btns[i].getAttribute("id");
						var cSortId = -1;
						var sortPattern = /^sort.*/;
						if( sortPattern.test(idAttr) ) {
							cSortId = parseFloat( idAttr.match(/\d+/)[0] );
						}
						if(cSortId > lastSortId) {
							lastSortId = cSortId;
						}
					}
					if(lastSortId > -1) {
						break;
					}
				}
				var newSortId = lastSortId + 1;


				//var firstSortCell = this.thRows[1].firstChild;
				var newSortCell = document.createElement("th");
				newSortCell.innerHTML =
					'<img alt="" src="'+
					GLOBAL_IMAGE_PATH+'sort-dis.gif'+
					//firstSortCell.firstChild.getAttribute("src")+
					'" id="sort'+newSortId+'" name="decoder">';

				//alert(newSortCell.innerHTML);
				thRow.appendChild(newSortCell);
				
			}
			else {

				// add column header for this links-to-maintainance column
				var decoderHdr = document.createElement("th");
				decoderHdr.innerHTML = 'Decoder';
				thRow.appendChild(decoderHdr);
			}

		}
	}
	window.status = window.defaultStatus;


	/* 
	===========================================================================
	Update data rows
	===========================================================================
	*/
	window.status = "Adding green and red decoder links";


	
	var docId       = null;
	var series      = null;
	var productType = null;
	var uname = "";
	try { uname = getCookie("uname"); } catch(e) {}
	for(i = 0; i < this.tbRows.length; i++) {

		var tbRow = this.tbRows[i];
		var newDocRow = false;

		if (tbRow && (tbRow.getElementsByTagName("th").length === 0)) {

			try {
				docId       = DecoderSunglasses.getDocIdFromRow(tbRow);
				series      = DecoderSunglasses.getSeriesFromRow(tbRow);
				productType = DecoderSunglasses.getProductTypeFromRow(tbRow);
				newDocRow = true;
			} catch(e1) {
				newDocRow = false;
				if(i === 0) {
					alert(e1);
				}
				// else: ok, spanned rows, reuse last docId
			}

			var decoderLink = document.createElement("td");
			
			var schemeStatus = this.schemesStatusForDocId(docId);

			var target = DecoderSunglasses.VPM_URL+"?id="+docId+"&series="+series+"&prod="+productType+"&uname="+uname;

			if(schemeStatus === "production") {
				decoderLink.innerHTML = '<div><!-- GREEN --><a href="'+target+'" title="Edit scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'green.png" border="0"/></a></div>';
			} else if(schemeStatus === "first-draft") {
//				decoderLink.innerHTML = '<div><!-- YELLOW --><img src="'+GLOBAL_IMAGE_PATH+'yellow.png" border="0"/></a></div>';
				decoderLink.innerHTML = '<div><!-- RED --><a href="'+target+'" title="Edit scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'red.png" border="0"/></a></div>';
			} else if(schemeStatus === "revision") {
//				decoderLink.innerHTML = '<div><!-- YELLOW --><a href="'+target+'" title="Edit scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'yellow.png" border="0"/></a></div>';
				decoderLink.innerHTML = '<div><!-- GREEN --><a href="'+target+'" title="Edit scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'green.png" border="0"/></a></div>';				
			} else if(schemeStatus === "fixed-part") {
//				decoderLink.innerHTML = '<div><!-- YELLOW --><a href="'+target+'" title="Edit scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'yellow.png" border="0"/></a></div>';
//				decoderLink.innerHTML = '<div><!-- YELLOW --><img src="'+GLOBAL_IMAGE_PATH+'yellow.png" border="0"/></div>';
				decoderLink.innerHTML = '<div><!-- YELLOW --><a href="mailto:webmaster@vishay.com?subject=PartForm Approval request for document '+docId+' ('+series+')&body=Approval request for docID: '+docId+'" title="Request approval for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'yellow.png" border="0"/></a></div>';
			} else if(schemeStatus === "empty") {
				decoderLink.innerHTML = '<div><!-- RED --><a href="'+target+'" title="Create scheme for document '+docId+' ('+series+', '+productType+')"><img src="'+GLOBAL_IMAGE_PATH+'red.png" border="0"/></a></div>';
			}
			tbRow.appendChild(decoderLink);
		} else {
			decoderHdr = document.createElement("th");
			decoderHdr.innerHTML = 'Decoder';
			tbRow.appendChild(decoderHdr);
		}

	} // for
	window.status = window.defaultStatus;
} // function DecoderSunglasses_bespectacle()



// XHR to get red or green status per docId sent: red means that there is not yet a scheme for the docId. green there is.
function DecoderSunglasses_schemesAvailableRequest() {
	this.lastDocIdTested = null;
	this.lastDocIdSchemes = null;

	try { this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); }
	catch (e) { try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
	catch (e1) { try { this.xmlhttp = new XMLHttpRequest(); }
	catch (e2) { this.xmlhttp = false; }}}

	var requestDom = "<scheme_inventory_request>\n";
	var allDocIds = this.getAllDocIds();
	for(var i=0; i<allDocIds.length; i++) {
		requestDom += "\t<for_document with_doc_id=\""+allDocIds[i]+"\"/>\n";
	}
	requestDom += "</scheme_inventory_request>\n";
//alert("REQUEST=\n"+	requestDom);
	if(this.xmlhttp !== null){
		this.xmlhttp.onreadystatechange = this.schemesAvailableResponse;
//		var url = window.location.href + DecoderSunglasses.AJAX_URL;
		var url = DecoderSunglasses.AJAX_URL;
//alert("requesting URL "+url);
		this.xmlhttp.open("POST", url, false);
		this.xmlhttp.setRequestHeader("Content-Type",
                           "application/x-www-form-urlencoded"); 
		this.xmlhttp.send("message="+requestDom);
//alert("request message=\n"+requestDom);
	}
	else{
		alert("Your browser does not support XMLHTTP.");
	}
}

function DecoderSunglasses_schemesAvailableResponse() {
/*
	if(this.xmlhttp.readyState == 4) {
		//alert(xmlhttp.responseText);
	}
*/
//	if(this.xmlhttp.readyState != 4 && this.xmlhttp.status != 200) return;
}

/*
TODO see if xhr.responseXML.getElementById could be used instead of this lookup -- unfortunately NOT ?
*/
function DecoderSunglasses_schemesStatusForDocId(docId) {
	var result = false; //[]; //new Array();
	var res_idx = 0;

	

	
	if(docId === this.lastDocIdTested) {

		result = this.lastDocIdSchemes;
	} else {

		var docs = null;
		try {
			docs = this.xmlhttp.responseXML.getElementsByTagName("scheme_inventory_response")[0].getElementsByTagName("for_document");
		} catch(e) {
			//alert("No XHR response: "+e.message+"\nXMLHTTP STATUS:"+this.xmlhttp.statusText+" ("+this.xmlhttp.status+")");
		}

		for(var d=0; d<docs.length; d++) {
			var doc = docs[d];

			var idAttr = doc.getAttribute("id").substring(4);
//			var idAttr = doc.getAttribute("with_doc_id");
			var cDocId = parseFloat(idAttr, 10);

			if(cDocId == docId) {
				this.lastDocIdTested = cDocId;
				
				result = "" + doc.getAttribute("status");

				
// TODO REMOVE (backward combatibility hack)
if(result === null || result === "" || result === "null") {
  if(doc.getAttribute("scheme_found") === "true") {
    result = "empty";
//alert("0");
  } else {
    result = "production";
//alert("1");
  }
}
				
				res_idx++;
				this.lastDocIdSchemes = result;
				break;
			}
		}
	}


	return result;
}

//var lalalalalalalalal = 0;
// get the document number from a row in table body
function DecoderSunglasses_getDocIdFromRow(tbRow) {
//++lalalalalalalalal;
//if(lalalalalalalalal == 1) alert(tbRow.getElementsByTagName("td")[0].getElementsByTagName("a")[0].getAttribute("href"));

	var href = undefined; 
	try {
		href=tbRow.getElementsByTagName("td")[0].getElementsByTagName("a")[0].getAttribute("href");
	} catch(e) { 
//		if(lalalalalalalalal<5)
//			alert(++lalalalalalalalal+"\n"+e);
		throw e;
	}
	
	var docId = parseFloat(href.slice(href.length - 6, href.length - 1), 10);
	return docId;
}

function DecoderSunglasses_getSeriesFromRow(tbRow) {
	var a = tbRow.getElementsByTagName("td")[0].getElementsByTagName("a")[0];
	return a.innerHTML;
}

function DecoderSunglasses_getProductTypeFromRow(tbRow) {
	var href = tbRow.getElementsByTagName("td")[0].getElementsByTagName("a")[0].getAttribute("href");
	if (href.indexOf("//") > -1) {
		href = href.substring(href.indexOf("//") + 2);
	}

	// after the double slash is gone, drop everything
	// up to and including the next single slash
	if (href.indexOf("/") > -1) {
		href = href.substring(href.indexOf("/") + 1);
	}

	// trim off the end
	var endIndex = href.indexOf("/list/");
	var gatewayName = href.slice(0, endIndex);

	return gatewayName;
}


// getall document numbers from table
function DecoderSunglasses_getAllDocIds() {
	var found = 0;
	var result = []; //new Array();
	
	for(var i = 0; i < this.tbRows.length; i++) {
		var docId = null;
		var tbRow = this.tbRows[i];
		if (tbRow && (tbRow.getElementsByTagName("th").length === 0)) {

			try {
				docId = DecoderSunglasses.getDocIdFromRow(tbRow);
			} catch(e) {
			}
			
			if(docId) {
				result[found] = docId;
				found++;
			}
		}
	}
	//alert(result);
	return result;
}

function DecoderSunglasses_toString() {
  return 'DecoderSunglasses';
}











































DecoderSunglasses.prototype.isActive = DecoderSunglasses_isActive;
DecoderSunglasses.prototype.bespectacle = DecoderSunglasses_bespectacle;
DecoderSunglasses.prototype.schemesAvailableRequest = DecoderSunglasses_schemesAvailableRequest;
DecoderSunglasses.prototype.schemesAvailableResponse = DecoderSunglasses_schemesAvailableResponse;
DecoderSunglasses.prototype.schemesStatusForDocId = DecoderSunglasses_schemesStatusForDocId;

DecoderSunglasses.getDocIdFromRow = DecoderSunglasses_getDocIdFromRow;
DecoderSunglasses.getSeriesFromRow = DecoderSunglasses_getSeriesFromRow;
DecoderSunglasses.getProductTypeFromRow = DecoderSunglasses_getProductTypeFromRow;

DecoderSunglasses.prototype.getAllDocIds = DecoderSunglasses_getAllDocIds;
DecoderSunglasses.prototype.toString = DecoderSunglasses_toString;

DecoderSunglasses.prototype.xmlhttp = null;

DecoderSunglasses.prototype.lastDocIdTested     = null;
DecoderSunglasses.prototype.lastDocIdSchemes    = null;








// TODO use already existing cookie function
// *************** DONT USE THOSE IN PRODUCTION ***************
/*
function _cookiesAllowed() {
   setCookie('checkCookie', 'test', 1);
   if (getCookie('checkCookie')) {
      deleteCookie('checkCookie');
      return true;
   }
   return false;
}
function _setCookie(name,value,expires, options) {
   if (options===undefined) { options = {}; }
   if ( expires ) {
      var expires_date = new Date();
      expires_date.setDate(expires_date.getDate() + expires);
   }
   document.cookie = name+'='+escape( value ) +
      ( ( expires ) ? ';expires='+expires_date.toGMTString() : '' ) + 
      ( ( options.path ) ? ';path=' + options.path : '' ) +
      ( ( options.domain ) ? ';domain=' + options.domain : '' ) +
      ( ( options.secure ) ? ';secure' : '' );
}
function _getCookie( name ) {
   var start = document.cookie.indexOf( name + "=" );
   var len = start + name.length + 1;
   if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) ) {
      return null;
   }
   if ( start === -1 ) { return null; }
   var end = document.cookie.indexOf( ';', len );
   if ( end === -1 ) { end = document.cookie.length; }
   return unescape( document.cookie.substring( len, end ) );
}
function _deleteCookie( name, path, domain ) {
   if ( getCookie( name ) ) { document.cookie = name + '=' +
      ( ( path ) ? ';path=' + path : '') +
      ( ( domain ) ? ';domain=' + domain : '' ) +
      ';expires=Thu, 01-Jan-1970 00:00:01 GMT';
	  }
}
*/
// class: ClientSniffer
//
// JG 2/05
//
// Simple client sniffer: only for occasional use
// (favor building to standards over sniffing)
//
function ClientSniffer() {
  // platform
  //this.agent = navigator.userAgent.toLowerCase();
  //this.isWindows = this.agent.indexOf("win") != -1;
  //this.isMac = this.agent.indexOf("mac") != -1;
  //this.isLinux = this.agent.indexOf("linux") != -1;
  //this.isUnix = this.agent.indexOf("x11") != -1;

  // browser/codebase
  //
  // Internet Explorer
  // (chose to detect using appName because userAgent string 
  //  can easily be changed in some browsers to include 'MSIE')
  this.isIE = navigator.appName.indexOf("Microsoft") != -1;
  //
  // Safari/OmniWeb (AppleWebKit), Konqueror (KHTML)
  // all use KJS (Konqueror JavaScript)
  this.isKJS = navigator.userAgent.indexOf("KHTML") != -1;
  
  // browser version
  //alert("navigator.appVersion = " + navigator.appVersion);
  if (this.isIE) {
    var msIndex = navigator.appVersion.indexOf("MSIE");
    var semicolonIndex = navigator.appVersion.indexOf(";", msIndex);
    this.browserVersion = parseFloat(navigator.appVersion.substring(msIndex + 4, semicolonIndex));
  }
  else {
    this.browserVersion = parseFloat(navigator.appVersion);
  }
  //alert("browserVersion = " + this.browserVersion);
}
// class: StringBuffer
//
// JG 11/04 - mimic Java StringBuffer class;
// faster performance for string concatenation
//
// (optimization idea by Robbie Morris, found at: 
// http://www.eggheadcafe.com/PrintSearchContent.asp?LINKID=392
//
function StringBuffer(string) {
  this.buffer = new Array();
  this.buffer.push(string);
}
StringBuffer.prototype.append = StringBuffer_append;
StringBuffer.prototype.toString = StringBuffer_toString;

// instance methods
function StringBuffer_append(string) {
  this.buffer.push(string);
}

function StringBuffer_toString() {
  return this.buffer.join('');
}
// class: StringUtilities
//
// JG 1/05
//
// Utility methods for string processing.
//
function StringUtilities() {
  // only contains class methods.
}
// class methods
StringUtilities.stripComments = StringUtilities_stripComments;
StringUtilities.trim = StringUtilities_trim;
/*
StringUtilities.stripLeadingComment = StringUtilities_stripLeadingComment;
function StringUtilities_stripLeadingComment(text) {
  // strips a leading comment (if present) from a string
  var strippedText = text;
  var commentEnd = -1;

  //commentEnd = text.indexOf("-->");
  try {
	commentEnd = text.indexOf("-->");
  } catch(e) {
	return strippedText;
  }
  
  if (commentEnd > -1) {
    strippedText = text.substring(commentEnd + 3);
  }
    
  return strippedText;
}

function StringUtilities_stripComments(text) {
  // first strip any leading comment
  var strippedText = StringUtilities.stripLeadingComment(text);

  // strip any trailing comment
  var commentStart = -1;
  
  //commentStart = strippedText.indexOf("<!--");
  try {
	commentStart = strippedText.indexOf("<!--");
  } catch(e) {
	return strippedText;
  }
  
  if (commentStart > -1) {
    strippedText = strippedText.substring(0, commentStart);
  }

  // trim off any leftover leading and trailing spaces
  strippedText = strippedText.replace(/^\s+/g, "");
  strippedText = strippedText.replace(/\s+$/g, "");

  return strippedText;
}
*/

function StringUtilities_trim(text) {
  var strippedText = text;
  strippedText = strippedText.replace(/^\s+/g, "");
  strippedText = strippedText.replace(/\s+$/g, "");

  return strippedText;
}

function StringUtilities_stripComments(text) {
	var strippedText = text;
	try {
		strippedText = strippedText.replace(/<!--.*-->/g, "");
	} catch(e) {
	}
	return strippedText;
}
// class: TableSortColumn
//
// JG 1/05
//
// A sort column for a table.
// Instances are added to a collection for multiple-column sorting.
//
function TableSortColumn (column, ascending) {
  this.column = column;
  this.ascending = ascending; // boolean: ascending or descending                                
}
TableSortColumn.prototype.toString = TableSortColumn_toString;

function TableSortColumn_toString() {
  var result = this.column + ":" + this.ascending;
  return result;               
}
// class: TableFilter
//
// JG 1/05
//
// A filter to be applied to a table.
//
function TableFilter(column, values) {
  this.column = column; // column number
  this.values = values; // list of values to constrain
}
TableFilter.prototype.valueExists = TableFilter_valueExists;
TableFilter.prototype.toString = TableFilter_toString;

////////////////////////////////////////////////////////////////////////
// instance methods

// true if the given value is permitted through the filter, false otherwise
function TableFilter_valueExists(inputValue) {
  var myFilters;
  var valueExists = false;

//alert("TableFilter_valueExists col="+this.column);
  myFilters = globalTable.currentFilters[this.column];

  // if the filter does not exist, then this value never passes the filter
  if (!myFilters) { return false; }

  for (var i = 0; i < this.values.length; i++) { // foreach acceptable value
    var myCurrentFilter;

    if (true == valueExists) { break; } // short-circuit if already true
    myCurrentFilter = myFilters.values[i];
    if (myCurrentFilter == inputValue) { valueExists = true; }
  } // next acceptable value  
  return valueExists;
}

// output a string representation of this object
function TableFilter_toString() {
  var numValues;
  var result;

  numValues = this.values.length;
  result = this.column + ":";

  for (var i = 0; i < numValues; i++) {
    result += escape( this.values[i].toString() );
    if (i < (numValues - 1)) {
      result += "||";
    }
  }

  return result;
}
// class: TableCell
//
// JG 1/05
//
// A single cell in a row of a table.
//
function TableCell(text, span) {
  // instance properties
  this.text = text;
  this.displayText = StringUtilities.stripComments(text);
  this.span = span;
}
TableCell.prototype.toString = TableCell_toString;

function TableCell_toString() {
  return this.displayText;
}
// class: TableRow
//
// JG 1/05
//
// A row of a table.
//
function TableRow(numColumns) {
  // instance properties
  this.numColumns = numColumns;
  this.cells = new Array(numColumns);
  this.group;     // allows for 'striping' table rows according to current sort column                               
}
TableRow.prototype.toString = TableRow_toString;

function TableRow_toString() {
  var result = "";
  
  for (var i = 0; i < this.cells.length; i++) {
    result += this.cells[i].toString();
    if (i < (this.cells.length - 1)) {
      result += " | ";
    }
  }
  
  return result;
}
// class: Table
//
// JG 1/05
//
// Abstract representation of a table.
//
// Intended to be displayed on a Web page.
//
function Table(tableDivElementId) {
  // instance properties
  this.tableDivElementId = tableDivElementId;

  this.originalHtmlRows = new Array();
  this.recordOriginalHtmlRows();

  this.numberOfColumns = this.countColumns();

  // these variables were used for single-column sorting,
  // but are now used to hold temporary state for each
  // of multiple column sorts
  this.sortColumn = Table.SORT_COLUMN_DEFAULT;
  this.sortAscending = Table.SORT_ASCENDING_DEFAULT;

  this.currentSortColumns = new Array();

  this.currentFilters = new Array();

  // the original table, the innerHTML way
  // (maybe reconcile this and originalHtmlRows)
  this.originalTable = document.getElementById(tableDivElementId).innerHTML;
  // the gateway name, from the datasheet links; set during data extraction
  this.gatewayName = "";

  // data matrix of values from the original HTML table
  // (formerly called rawData)
  this.dataRows = new Array();
  //alert("about to extract data");

  this.extractData();

  //alert("finished extracting data");
  //alert("about to clone data rows");
  this.filteredDataRows = this.cloneDataRows(this.dataRows);
  //alert("finished cloning data rows");

  // note the columns that contain all numeric data
  //alert("about to get numeric columns");
  this.numericColumns = this.getNumericColumns();
  //alert("finished getting numeric columns");

  // get the number of datasheets
  //alert("about to get number of datasheets");
  this.numberOfDatasheets = this.getNumberOfDatasheets();
  //alert("finished getting number of datasheets");

}
// class properties
Table.SORT_COLUMN_DEFAULT = -1;
Table.SORT_ASCENDING_DEFAULT = true;
// class methods
Table.alphaSort = Table_alphaSort;
Table.numericSort = Table_numericSort;
Table.compareValues = Table_compareValues;
Table.singleColumnSort = Table_singleColumnSort;
Table.multipleColumnSort = Table_multipleColumnSort;
// instance methods
Table.prototype.isHeaderRow = Table_isHeaderRow;
Table.prototype.recordOriginalHtmlRows = Table_recordOriginalHtmlRows;
Table.prototype.countColumns = Table_countColumns;
Table.prototype.cellText = Table_cellText;
Table.prototype.numberOfSpannedRows = Table_numberOfSpannedRows;
Table.prototype.getProductName = Table_getProductName;
Table.prototype.getGatewayName = Table_getGatewayName;
Table.prototype.getDocId = Table_getDocId;
Table.prototype.getRelatedDocsIndex = Table_getRelatedDocsIndex;
Table.prototype.extractData = Table_extractData;
Table.prototype.cloneDataRows = Table_cloneDataRows;
Table.prototype.getDistinctValues = Table_getDistinctValues;
Table.prototype.getNumberOfDatasheets = Table_getNumberOfDatasheets;
Table.prototype.getMultipleFilteredData = Table_getMultipleFilteredData;
Table.prototype.isNumericColumn = Table_isNumericColumn;
Table.prototype.getNumericColumns = Table_getNumericColumns;
Table.prototype.spanRows = Table_spanRows;
Table.prototype.areSameFilterValues = Table_areSameFilterValues;


/////////////////////////////////////////////////////////////////////////////
// class methods

// comparison functions used for sorting

function Table_alphaSort(valueA, valueB) {
  // put any blank spaces at bottom rather than at
  // the top (default)
  // NOTE: removed for faster performance
  //if (valueB == "") {
  //  return -1;
  //}
  //else if (valueA == "") {
  //  return 1;
  //}
  // alpha comparisons
  //
  // else
  if (valueA < valueB) {
    return -1;
  }
  else if (valueA == valueB) {
    return 0;
  }
  else if (valueA > valueB) {
    return 1;
  }
}


function Table_numericSort(valueA, valueB) {
  var difference = valueA - valueB;
  // put any blank spaces at bottom rather than at
  // the top (default)
  // NOTE: removed for faster performance
  //if (valueB == "") {
  //  return -1;
  //}
  //else if (valueA == "") {
  //  return 1;
  //}
  // numeric comparisons
  //
  // else
  if (difference < 0) {
    return -1;
  }
  else if (difference == 0) {
    return 0;
  }
  else if (difference > 0) {
    return 1;
  }
}


function Table_compareValues(valueA, valueB, isNumeric) {
  if (!isNumeric) {
    // alpha sort
    return Table.alphaSort(valueA, valueB);
  }
  else {
    // numeric sort
    return Table.numericSort(valueA, valueB);
  }
}

// handles only the first of current sort columns
function Table_singleColumnSort(a, b) {
  var column = globalTable.currentSortColumns[0].column;
  var ascending = globalTable.currentSortColumns[0].ascending;

  var result = Table.compareValues(a.cells[column].text,
                                   b.cells[column].text,
                                   globalTable.numericColumns[column]);

  // if the sort direction is descending, reverse the result by negating it
  if (!ascending) {
    result = -result;
  }

  return result;
}

// handle all current sort columns and their directions
function Table_multipleColumnSort(a, b) {
  var result = 0;
  var sortColumn;
  var valueA;
  var valueB;
  var columnResults = new Array(globalTable.currentSortColumns.length);
  var isNumeric;

  // get a list of comparison results: one for each sort column
  for (var i = 0; i < globalTable.currentSortColumns.length; i++) {
    sortColumn = globalTable.currentSortColumns[i];

    valueA = a.cells[sortColumn.column].text;
    valueB = b.cells[sortColumn.column].text;

    isNumeric = globalTable.numericColumns[sortColumn.column];
    columnResults[i] = Table.compareValues(valueA, valueB, isNumeric);

    // if the sort direction is descending, reverse the result by negating it
    if (!sortColumn.ascending) {
      columnResults[i] = -columnResults[i];
    }
  }

  // Now that we've got a list of comparison results,
  // we can decide our row-level result.
  //
  // multiply primary sort column result value by factor of 100
  result = (columnResults[0] * 100);
  // if secondary sort column is present, add its value, multiplied by 10
  if (columnResults[1]) {
    result += (columnResults[1] * 10);
  }
  // if tertiary sort column is present, add its value unchanged
  if (columnResults[2]) {
    result += (columnResults[2] * 1);
  }

//  // debug messages
 var msg = "comparing:\n";
// row a
//  msg += "row a:";
//  for (var j = 0; j < globalTable.currentSortColumns.length; j++) {
//    sortColumn = globalTable.currentSortColumns[j];
//    msg += " [col " + sortColumn.column + "]=" +
//           a.cells[sortColumn.column].text;
//  }
// row b
//  msg += "\nrow b:";
//  for (var j = 0; j < globalTable.currentSortColumns.length; j++) {
//    sortColumn = globalTable.currentSortColumns[j];
//    msg += " [col " + sortColumn.column + "]=" +
//           b.cells[sortColumn.column].text;
//  }
//  msg += "\n";
//  for (var j = 0; j < columnResults.length; j++) {
//    msg += "columnResults[" + j + "] = " + columnResults[j] + "\n";
//  }

//msg += "\nusing columnResults[" + i + "], which is " + columnResults[i];
//  msg += "\nusing calculated result: " + result;
//alert(msg);

//alert("multipleColumnSort: result = " + result);
  return result;
}

/////////////////////////////////////////////////////////////////////////////
// instance methods

function Table_isHeaderRow(rowNode) {
  // look at child nodes of the supplied table row
  // (tr) node; if we find header cells, return true
  var result = false;

  var thChildren = rowNode.getElementsByTagName("th");
  if (thChildren.length > 0) {
    result = true;
  }

  return result;
}

// record the original HTML rows from the Web page
function Table_recordOriginalHtmlRows() {
  //var tableNode = document.getElementById(this.tableDivElementId).getElementsByTagName("table")[0];
  var tableNode = document.getElementById("parametric-table");
  var node;
  var tbody = tableNode.getElementsByTagName("tbody")[0];
  var trows = tbody.getElementsByTagName("tr");

  for (var i = trows.length; i >= 0; i--) {
    node = trows[i];
    if ((node) && (this.isHeaderRow(node) == false)) {
      this.originalHtmlRows.unshift(node);
    }
  }

}

// count the number of columns from the original HTML data rows
function Table_countColumns() {
  return this.originalHtmlRows[0].getElementsByTagName("td").length;
}


// returns the text inside the supplied cell node
// TODO: This method is abused by extractData. Fix extractData, then remove the
//       kludge for img tags.
function Table_cellText(cellNode) {
  var children;
  var text = "";

  // cellNode may have empty or adjacent text nodes, so we must normalize
  cellNode.normalize();

  children = cellNode.childNodes;
  for (var i = 0; i < children.length; i++) { // foreach child node
    var node;

    node = children[i];
    if (null == node) { continue; } // short-circuit on null node
//if(i<1) { alert(node.innerHTML); }
    switch (node.nodeType) { // select node type
      case 1: // Node.ELEMENT_NODE
        // THIS IS A DIRTY KLUDGE!!!
        // TODO: Figure out the JavaScript equivalent of Java's node.toString()
		/*
        if (node.nodeName == "img" || node.nodeName == "IMG") {
          text += "<img";
          text += " alt=\"" + node.getAttribute("alt") + "\"";
          text += " class=\"" + node.getAttribute("class") + "\"";
          text += " height=\"" + node.getAttribute("height") + "\"";
          text += " id=\"" + node.getAttribute("id") + "\"";
          text += " src=\"" + node.getAttribute("src") + "\"";
          text += " width=\"" + node.getAttribute("width") + "\"";
          text += "/>";
        }
		// AL May 2008: Allowing decoder sunglasses column to be sortable
		else {
			if(globalDecoderSunglasses.isActive()) {
				text += node.parentNode.innerHTML;
			}
		}
		*/
		text += node.parentNode.innerHTML;
        break;
      case 3: // Node.TEXT_NODE
        if (text != "") { text += " "; }
        text += node.nodeValue;
        break;
      case 8: // Node.COMMENT_NODE
        // We sometimes use comments to sort, only if there is no other text
//        if (text == "") { text += "<!-- " + node.nodeValue + " -->"; } // no: FF returns nodes for lines of whitespace
        if (/[\t\n\r ]*/.test(text)) { text += "<!-- " + node.data + " -->"; }

        break;
      default:
        // do nothing -- we don't care about other node types
    } // end node type selection
  } // next child node

  // trim and squeeze whitespace from text
  text.replace(/^\s*(\S+)\s+(\S+)\s*$/g, "$1 $2");

  return text;
} // end function Table_cellText

// given a HTML table cell node, return the number of rows spanned
// (spanning across two rows = 2; a regular unspanned cell = 1)
function Table_numberOfSpannedRows(cellNode) {
  var numberSpanned = 1;
  var rowSpanValue;

  rowSpanValue = cellNode.getAttribute("rowspan");
  if (rowSpanValue != null && rowSpanValue != "") {
    numberSpanned = parseInt(rowSpanValue);
  }

  return numberSpanned;
}


// get the product name from the hyperlink label
// in the cell from the first column
function Table_getProductName(linkNode) {
  var productName;

  var children = linkNode.childNodes;
  for (var i = 0; i < children.length; i++) {
    node = children[i];

    if (node != null) {
      if (node.nodeType == 3 /* Node.TEXT_NODE */) {
        productName = node.nodeValue;
      }
    }
  }

  return productName;
}


// get the gateway name from the hyperlink href
// in the cell from the second (i-button) column
function Table_getGatewayName(linkNode) {
  var href = null;
  var gatewayName = false;
	
  try {
	href = linkNode.getAttribute("href");
  } catch(e) {
	return gatewayName;
  }
  

  // if a domain exists in the link, get rid of it

  // look for double-slash, in case we have a full
  // HTTP URI (like IE returns to us)
  if (href.indexOf("//") > -1) {
    href = href.substring(href.indexOf("//") + 2);
  }

  // after the double slash is gone, drop everything
  // up to and including the next single slash
  if (href.indexOf("/") > -1) {
    href = href.substring(href.indexOf("/") + 1);
  }

  // trim off the end
  var endIndex = href.indexOf("/list/");
  gatewayName = href.slice(0, endIndex);

  return gatewayName;
}

// get the docid from the hyperlink href
// in the cell from the second (i-button) column
//var maja = 0;
function Table_getDocId(linkNode) {
//maja++;
  var href = null;
  var docId = false;

  try {
	href = linkNode.getAttribute("href");
  } catch(e) {
  }
  
  try {
	//docId = href.slice(href.length - 6, href.length - 1);
	docId = href.split("?")[1];
  } catch(e) {
  }

//alert(linkNode+" -- "+docId);

  return docId;
}

// get the related documents menu index number from the hyperlink href
// in the cell from the second (i-button) column
function  Table_getRelatedDocsIndex(linkNode) {
  // for some reason IE returns an anonymous function when asking for
  // this attribute; need to explicitly convert it to a string
  var attributeValue = null;
  var relatedDocsIndex = false;

  try {
    attributeValue = linkNode.getAttribute("onmouseover").toString();
  } catch(e) {
	return relatedDocsIndex;
  }
  
  var parts = attributeValue.split("'");
  relatedDocsIndex = parts[1];

  return relatedDocsIndex;
}

// extract the data values from the original HTML rows into a matrix
// TODO: Fix this function's semantics!!! It produces a 2D array of strings.
//       These strings should ONLY be used to represent text on which to sort
//       and filter. However, they are being abused to repopulate the table
//       after sorting. When sorting a list of anything, one post condition is
//       that the contents of the output list must be identical to the contents
//       of the input list, just in a different order. This is not true for
//       this function as it is currently written. We need to create another
//       2D array, of DOM nodes. Each node in the array should contain as
//       children all of the DOM nodes that were children of that cell in the
//       original table.
function Table_extractData() {
  // the original (unsorted) table always has the same number of rows as the
  // datasheets
  this.dataRows = new Array(this.originalHtmlRows.length);

  // must initialize all rows before populating any, b/c rowspans might allow
  // row (i + k) to be populated during row i's operation
  for (var i = 0; i < this.dataRows.length; i++) {
    this.dataRows[i] = new TableRow(this.numberOfColumns);
  }

  for (var i = 0; i < this.dataRows.length; i++) { // for each row
    var cellNodes;
    var matrixColumn = 0; // the current columnar position

    cellNodes = this.originalHtmlRows[i].getElementsByTagName("td");
    for (var j = 0; j < cellNodes.length; j++) { // for each cell in row
      var node;
      var textValue;

      node = cellNodes[j];
      if (null == node) { continue; }

      // table data cell: figure out in which location(s) to place it in the
      // data matrix. check the underlying matrix value at the current table
      // column position. if this underlying matrix value isn't already filled
      // in, place our cell value there. otherwise, move rightward in the
      // matrix until we find an empty spot to place it.
      while (this.dataRows[i].cells[matrixColumn] != undefined) {
        matrixColumn++;
      }

      // how to get the cell's text depends on the column number
      switch (matrixColumn) {
        case 0: // first column = product page hyperlink
          textValue = this.getProductName(node.getElementsByTagName("a")[0]);
          // if we haven't gotten the gateway name yet and this is
          // the first row, get the gateway name from the link
          if ((this.gatewayName == "") && (i == 0)) {
            this.gatewayName = this.getGatewayName(node.getElementsByTagName("a")[0]);
          }
          break;
        case 1: // second column = i-button cell
          // docid and related docs menu index number, separated by comma
		  if(seriesColspan >= 2 ) { // we have PDF column
			  textValue = this.getDocId(node.getElementsByTagName("a")[0]) +
						  "," + this.getRelatedDocsIndex(node.getElementsByTagName("a")[0]);
						  //alert("A");
		  } else {
            textValue = this.cellText(node);
						//  alert("B");
		  }
          break;
        case 2: // third column - PDF link
          // docid and related docs menu index number, separated by comma
          textValue = this.cellText(node);
          break;
        default: // all others = ordinary plain text cell
          textValue = this.cellText(node);
          break;
      }


      // populate the cell, plus any downward cells, based on rowspan, if any
      for (var k = 0; k < this.numberOfSpannedRows(node); k++) {
        this.dataRows[i + k].cells[matrixColumn] = new TableCell(textValue, 0);
if(i==0) {
	//alert("Setting dataRow["+(i+k)+"].cells["+matrixColumn+"] = \""+textValue+"\"");
}	
      }

      matrixColumn++;
    } // next cell in row
  } // next row
} // end function Table_extractData

// clone a set of data rows
function Table_cloneDataRows(dataRows) {
  //alert("about to clone data rows");
  var clone = new Array(dataRows.length);

  for (var i = 0; i < dataRows.length; i++) {
    clone[i] = new TableRow(dataRows[i].cells.length);

    for (var j = 0; j < dataRows[i].cells.length; j++) {
	//window.status = "i="+i+" j="+j;
      clone[i].cells[j] = new TableCell(dataRows[i].cells[j].text,
                                        dataRows[i].cells[j].span);
    }
  }

  //alert("finished cloning data rows");
  return clone;
}


// get the distinct display values for a column
function Table_getDistinctValues(tableData, column) {

  var distinctValues = new Array();

  var testValue;
  var previousTestValue;
  var found;
  var j;

  // first, copy the column to an array
  var columnValues = new Array(tableData.length);
  for (var i = 0; i < tableData.length; i++) {
    columnValues[i] = tableData[i].cells[column].displayText;
  }

  // sort the array
  if ( !this.numericColumns[column] ) {
    columnValues.sort(Table.alphaSort);
  }
  else {
    columnValues.sort(Table.numericSort);
  }

  // go through the array and add a value only when
  // it's different from the previous one
  for (var i = 0; i < columnValues.length; i++) {
    testValue = columnValues[i];
    if ((testValue != "") && (testValue != previousTestValue)) {
      // found a value we haven't encountered yet; add it
      distinctValues.push(testValue);
      previousTestValue = testValue;
    }
    // debug
    //setTimeout("window.status='getDistinctValues: " + i + "'", 1);
  }

  return distinctValues;
}


// get the number of datasheeets by counting the unique docids
function Table_getNumberOfDatasheets() {
  var distinctDocIds = new Array();
  var docId;
  var found;
  var j;

  for (var i = 0; i < this.dataRows.length; i++) {
    docId = this.dataRows[i].cells[1].displayText;
    docId = docId.substring(0, docId.indexOf(","));
    //alert("getNumberOfDatasheets: docId = " + docId);

    if (docId != "") {
      // if the test value isn't in our distinct values list
      // yet, add it
      found = false;
      j = 0;
      while ((found == false) && (j < distinctDocIds.length)) {
        if (distinctDocIds[j] == docId) {
          found = true;
        }
        j++;
      }
      if (!found) {
        distinctDocIds.push(docId);
      }
    }
  }

  return distinctDocIds.length;
}


// PRE: an array of TableRow, an array of TableFilter, a boolean
// POST: a new array of TableRow, containing copies of the TableRow elements
//       from the input TableRow array that pass all of the tests provided by
//       the input TableFilter array
function Table_getMultipleFilteredData(tableData, filters, unusedBoolean) {
  var filteredData;

  filteredData = new Array();

if(MYDEBUG) alert("Table_getMultipleFilteredData\nfilters="+filters);
  for (var i = 0; i < tableData.length; i++) { // foreach row
    var isRowKept = true;
    var newRow;
    var thisRow;

    thisRow = tableData[i];

	var len = filters.length;
	var count = 0;
//    for (var j in filters) { // foreach filter
    for (var j=0; j<filters.length; j++) { // foreach filter
//	  var j = filters[f];
/*
		if(count >= len || j == 'each' || j == 'call')
		break;
		else
		++count;
*/
if(! filters[j]) continue;
	var cellDisplayText;
      var thisColumn;
//if(MYDEBUG) alert("filter #"+j+"\n"+filters);
      thisColumn = filters[j].column;
      cellDisplayText = thisRow.cells[thisColumn].displayText;
      if (!filters[j].valueExists(cellDisplayText)) {
        //alert("\"" + filters[j] + "\" does not contain the text \"" +
        //      cellDisplayText + "\"");

        isRowKept = false;
      }
    } // next filter

    if (isRowKept) {
      var newRow;

      newRow = new TableRow(thisRow.cells.length);
      for (var j = 0; j < thisRow.cells.length; j++) { // foreach cell in row
        newRow.cells[j] = new TableCell(thisRow.cells[j].text, 0);
      } // next cell
      filteredData.push(newRow);
    }
  } // next row

  return filteredData;
} // end function Table_getMultipleFilteredData


function Table_isNumericColumn(columnNumber) {
  // return true if the column is numeric,
  // that is, all the non-empty values are numbers
  // and not text
  //
  // NOTE: in the real program, check this only once
  // and store it somewhere in the table object.
  //
  var isNumeric = true;
  var testValue;

  var i = 0;
  while ((isNumeric == true) && (i < this.dataRows.length)) {
    //alert("isNumericColumn: i = " + i + " columnNumber = " + columnNumber);
    testValue = this.dataRows[i].cells[columnNumber].text;
    if ((testValue != "") && (isNaN(testValue))) {
      isNumeric = false;
    }
    i++;
  }

  return isNumeric;
}


// returns an array of values, one for each column
// in the table, with boolean value of true if
// that column contains all numeric values; false
// if there are alpha characters in it
function Table_getNumericColumns() {
  var numericColumns = new Array(this.numberOfColumns);
  for (var i = 0; i < this.numberOfColumns; i++) {
    numericColumns[i] = this.isNumericColumn(i);
  }
  return numericColumns;
}


// set row spans for later display
function Table_spanRows() {
  var keyColumn = 0;
//alert("spanning keyColumn="+keyColumn);

  if (this.currentSortColumns[0]) {
    keyColumn = this.currentSortColumns[0].column;
  }

  // assume we have some rows to display; if not, alert
  if (this.filteredDataRows.length < 1) {
    alert("No rows to display. (internal error)");
  }

  // go through key column, set group and span
  //alert("this.filteredDataRows[0] = " + this.filteredDataRows[0] +
  //      "\nkeyColumn = " + keyColumn);
  var keyColumnText = this.filteredDataRows[0].cells[keyColumn].text;
  var group = false;
  var span = 0;

  for (var r = 0; r < this.filteredDataRows.length; r++) {
    var keyCell = this.filteredDataRows[r].cells[keyColumn];

    // if the key column contents are different,
    // set the span of the first cell, and start a new group
    if (keyCell.text != keyColumnText) {
      this.filteredDataRows[r - span].cells[keyColumn].span = span;

      keyColumnText = keyCell.text;
      span = 1;

      // toggle group value;
      group = !group;
    }
    else {
      // contents are the same
      span++;
    }
    this.filteredDataRows[r].group = group;
  }

  // set final span
  this.filteredDataRows[this.filteredDataRows.length - span].cells[keyColumn].span = span;

  // loop through the columns and span rows
  // as necessary
  var text;
  for (var c = 0; c < this.filteredDataRows[0].cells.length; c++) {
    span = 0;

    // we already spanned the sort column
    if (c != keyColumn) {
      row = this.filteredDataRows[0];
      text = row.cells[c].text;
      group = row.group;

      for (var r = 0; r < this.filteredDataRows.length; r++) {
        row = this.filteredDataRows[r];
        var cell = row.cells[c];

        // if the group is different, OR
        // if the contents are different and the group is the same:
        // set the span of the first cell, and get the group
        if (group != row.group ||
            (cell.text != text && group == row.group)) {
          this.filteredDataRows[r - span].cells[c].span = span;
          text = cell.text;
          span = 1;
          group = row.group;
        }
        else {
          // contents and group are the same
          span++;
        }
      }

      // set final span
      this.filteredDataRows[this.filteredDataRows.length - span].cells[c].span = span;
    }
  }

}


// returns true if the values passed in are the same ones as
// in the specified filter
function Table_areSameFilterValues(filterColumn, filterValues) {
  var areSame = true;

  // if no filter defined, it's a new filter
  if (!this.currentFilters[filterColumn]) {
    areSame = false;
  }
  else {
    var filter = this.currentFilters[filterColumn];

    // if the lengths are different, values aren't the same
    if (filter.values.length != filterValues.length) {
      areSame = false;
    }
    else {
      // loop through, looking for different values
      var i = 0;
      while ( (areSame) && (i < filter.values.length) ) {
        if (filter.values[i] != filterValues[i]) {
          areSame = false;
        }
        i++;
      }
    }

  }

  //alert("areSame = " + areSame);
  return areSame;
}
// class: TableAction
//
// JG 2/05
//
// The sort and filter states of the table, representing
// a single sort or filter action taken by the user.
//
function TableAction(sortState, filterState) {
  // instance properties
  this.sortState = sortState;
  this.filterState = filterState;                               
}
TableAction.prototype.toString = TableAction_toString;

function TableAction_toString() {
  var result = "sortState = " + this.sortState + 
               "    filterState = " + this.filterState;
  return result;               
}
// class: TableView
//
// JG 1/05
//
// View class for updating the list page table interface
//
function TableView() {
  // initialize filter menus
  //alert("about to generate menus");
  this.generateMenus();
  //alert("about to get menu values");
  this.allMenuValues = this.getAllMenuValues();
  //alert("finished getting menu values");
  //alert("about to update menus");
  this.updateMenus();

  // clear any filter menu selections
  //alert("about to clear any filter menu selections");
  this.clearAllFilterSelections();

  // is control key pressed
  // TO DO: reconsider whether this belongs in the View,
  // but use here for now since the controller is static
  this.isCtrlKeyPressed = false;

  // 'action stack' - for undo/redo of individual
  // sort/filter actions
  this.actionStack = new Array();

  // different cursor name for IE
  if (document.all) {
    TableView.POINTER_CURSOR = "hand";
  }

  this.isFilteringEnabled = true;
}
// class properties
TableView.MAX_MENU_HEIGHT = 16;
TableView.RESET_BUTTON_ID = "tblresetbtn";
TableView.UNDO_BUTTON_ID = "tblundobtn";
TableView.MENU_DIV_ID_PREFIX = "menu";
TableView.MENU_SELECT_ID_PREFIX = "s-menu";
TableView.SORT_STATE_FIELD = "s-st";
TableView.FILTER_STATE_FIELD = "f-st";
TableView.POINTER_CURSOR = "pointer";
TableView.FILTERING_ENABLED_MESSAGE = "Click the buttons to sort and filter the table."
TableView.FILTERING_DISABLED_MESSAGE = "You can sort the table below. Due to a browser issue, filtering is not available in this browser.";
// instance methods
TableView.prototype.generateMenus = TableView_generateMenus;
TableView.prototype.getAllMenuValues = TableView_getAllMenuValues;
TableView.prototype.getSelectMenuSize = TableView_getSelectMenuSize;
TableView.prototype.updateMenus = TableView_updateMenus;
TableView.prototype.updateButtonStates = TableView_updateButtonStates;
TableView.prototype.getMenuValues = TableView_getMenuValues;
TableView.prototype.menuNumericSort = TableView_menuNumericSort;
TableView.prototype.clearAllFilterSelections = TableView_clearAllFilterSelections;
TableView.prototype.resetMenu = TableView_resetMenu;
TableView.prototype.resetAllButtonCursors = TableView_resetAllButtonCursors;
TableView.prototype.resetAllButtonStates = TableView_resetAllButtonStates;
TableView.prototype.updateTableStatusMessage = TableView_updateTableStatusMessage;
TableView.prototype.updateTable = TableView_updateTable;
TableView.prototype.changeSortButtonState = TableView_changeSortButtonState;
TableView.prototype.restoreOriginalTable = TableView_restoreOriginalTable;
TableView.prototype.getTop = TableView_getTop;
TableView.prototype.getLeft = TableView_getLeft;
TableView.prototype.showMenu = TableView_showMenu;
TableView.prototype.closeMenu = TableView_closeMenu;
TableView.prototype.toggleMenu = TableView_toggleMenu;
TableView.prototype.getFirstOpenMenuColumn = TableView_getFirstOpenMenuColumn;
TableView.prototype.updateStateFields = TableView_updateStateFields;
TableView.prototype.restoreSortColumns = TableView_restoreSortColumns;
TableView.prototype.restoreFilters = TableView_restoreFilters;
TableView.prototype.restorePreviousState = TableView_restorePreviousState;
TableView.prototype.undoLastAction = TableView_undoLastAction;

/////////////////////////////////////////////////////////////////////////////
// instance methods

// generate divs that hold a form and drop-down menu for each filter
// and append to the document body
function TableView_generateMenus() {
  var menuDivNode;
  var menuFormNode;
  var menuSelectNode;
  var menuSelectEl;

  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    // only generate a menu for columns that have filter buttons
    if (document.getElementById("filt" + i)) {
      //alert("generate a menu for column " + i);
      menuDivNode = document.createElement("div");
      menuDivNode.setAttribute("id", TableView.MENU_DIV_ID_PREFIX + i);
      menuDivNode.className = "tblmnu";

      menuFormNode = document.createElement("form");

      menuSelectNode = document.createElement("select");
      menuSelectNode.setAttribute("id", TableView.MENU_SELECT_ID_PREFIX + i);
      // allow multi-select
      menuSelectNode.setAttribute("multiple", "multiple");

      menuFormNode.appendChild(menuSelectNode);
      menuDivNode.appendChild(menuFormNode);
      document.body.appendChild(menuDivNode);

      // deselect all menu options
      menuSelectEl = document.getElementById(TableView.MENU_SELECT_ID_PREFIX + i);
      menuSelectEl.options.selectedIndex = -1;
    }

  }

}


// returns an array of arrays containing all menu values
// for the table; this is called only once upon initialization
function TableView_getAllMenuValues() {
  var allMenuValues = new Array();

  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    menuDivId = TableView.MENU_DIV_ID_PREFIX + i;

    // only get values if a menu exists for this column
    if (document.getElementById(menuDivId)) {
      //alert("update menu: " + i);
      menuValues = globalTable.getDistinctValues(globalTable.dataRows, i);
      allMenuValues[i] = menuValues;
    }
  }

  return allMenuValues;
}


// set the size of a select menu without exceeding
// the maximum menu height
function TableView_getSelectMenuSize(numberOfValues) {
  var selectMenuSize;

  // allow an extra space for the 'all' menu item at the top
  // of each menu
  if (numberOfValues + 1 > TableView.MAX_MENU_HEIGHT) {
    selectMenuSize = TableView.MAX_MENU_HEIGHT;
  }
  else {
    selectMenuSize = numberOfValues + 1;
  }

  // can't do size = 1; that makes a drop-down box
  if (selectMenuSize == 1) {
    selectMenuSize++;
  }

  return selectMenuSize;
}


// update the drop-down menus with appropriate values according to filters
function TableView_updateMenus() {
  //alert("start: updateMenus");
  var menuValues;
  var menuValue;
  var menuDivId;
  var menuSelectId;
  var menuSelectEl;
  var optionEl;
  var selectedState;



  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    menuDivId = TableView.MENU_DIV_ID_PREFIX + i;

    // only update the menu if it exists
    // (some columns don't have menus)
    if (document.getElementById(menuDivId)) {
      //alert("update menu: " + i);

      // if there are filters set, need to call
      // the method that filters and updates menu values;
      // otherwise, can grab the full list of values
      // that were found during initialization
      if (globalTable.currentFilters.length > 0) {
        menuValues = this.getMenuValues(i);
      }
      else {
        menuValues = this.allMenuValues[i];
      }

      menuSelectId = TableView.MENU_SELECT_ID_PREFIX + i;
      menuSelectEl = document.getElementById(menuSelectId);

      // clear the menu
      menuSelectEl.options.length = 0;

      // add the 'all' option
      menuSelectEl.options[0] = new Option("(all)", "", false, false);

      // add the remaining values
      for (var j = 0; j < menuValues.length; j++) {
        menuValue = menuValues[j];
        selectedState = false;
        // if there's a filter for the menu, check to see if this menu value
        // is currently filtered
        if (globalTable.currentFilters[i]) {
          selectedState = globalTable.currentFilters[i].valueExists(menuValue);
        }
        menuSelectEl.options[j + 1] = new Option(menuValue, menuValue,
                                                 false, selectedState);
      }

      // if there is no filter for the menu, deselect all options
      if (!globalTable.currentFilters[i]) {
        menuSelectEl.options.selectedIndex = -1;
      }

      // resize the height of the menu to the number visible
      menuSelectEl.size = this.getSelectMenuSize(menuValues.length);

    } // end if

  } // end for i
  //alert("end: updateMenus");
}


// after the table is restored from a previous state (either
// hidden form fields or undo), set each button to the correct state
function TableView_updateButtonStates() {
  var filterButtonId;

  // clear any existing highlighted buttons
  this.resetAllButtonStates();

  // sort column
  if (globalTable.currentSortColumns.length > 0) {
    var sortColumn = globalTable.currentSortColumns[0];
    var sortButtonId = "sort" + sortColumn.column;
    var image;
    if (sortColumn.ascending) {
      image = "sort-asc.gif";
    }
    else {
      image = "sort-desc.gif";
    }
    document.getElementById(sortButtonId).src = GLOBAL_IMAGE_PATH + image;
  }

  // filters
	var len = globalTable.currentFilters.length;
	var count = 0;
//  for (filter in globalTable.currentFilters) {

// followinf line was:
//  for (var i=0; i < globalTable.currentFilters.length; i++) {
//	var filter = globalTable.currentFilters[i];
  for (var filter=0; filter < globalTable.currentFilters.length; filter++) {
//	var filter = globalTable.currentFilters[i];
/*
	if(count >= len || filter == 'each' || filter == 'call')
	break;
	else
	++count;
*/
	if(globalTable.currentFilters[filter]) {
		filterButtonId = "filt" + globalTable.currentFilters[filter].column;
if(MYDEBUG) alert("scanning for\nfilter="+filter+"\nfilterButtonId="+filterButtonId+"\ndocument="+document+"\ndocument.getElementById(filterButtonId)="+document.getElementById(filterButtonId));
		document.getElementById(filterButtonId).src = GLOBAL_IMAGE_PATH + "filter-on.gif";
	}
  }
}


// Return an array of filtered values that can be used to update a filter's menu
function TableView_getMenuValues(menuColumn) {
  //alert("getMenuValues for column " + menuColumn + "...");
  var tempFilters = new Array();
  var tempTableData;
  //var msg = "";

  // Construct a temporary filter set.
  // This filter set consists of all current filters EXCEPT the
  // current column's filter.
	var len = globalTable.currentFilters.length;
	var count = 0;
//  for (filter in globalTable.currentFilters) {
  for (var filter=0; filter<globalTable.currentFilters.length; filter++) {
//	var filter = globalTable.currentFilters[f];
/*
	if(count >= len || filter == 'each' || filter == 'call')
	break;
	else
	++count;
*/
    // if this filter isn't the current column,
    // add to our temp filters list

    //alert("globalTable.currentFilters[" + filter + "].column = " +
    //      globalTable.currentFilters[filter].column +
    //      "\nmenuColumn = " + menuColumn);

    if (globalTable.currentFilters[filter] && globalTable.currentFilters[filter].column != menuColumn) {
      //alert("getMenuValues(" + menuColumn + "): tempFilters added " +
      //      "the filter on column " + globalTable.currentFilters[filter].column);
      tempFilters[globalTable.currentFilters[filter].column] =
          new TableFilter(globalTable.currentFilters[filter].column,
                          globalTable.currentFilters[filter].values);
    }
  } // end for

  //alert( "tempFilters:\n" + tempFilters.toString() );

  // get a copy of the table data, filtered with the temporary filters.
  // debugging: if a certain value, do the filtering with debug flag turned on
//  if ( (menuColumn == 7) &&
//       (globalTable.currentFilters[6]) ) {
//    tempTableData = globalTable.getMultipleFilteredData(globalTable.dataRows, tempFilters, true);
//  }
//  else {
    //alert("getMenuValues: about to filter");
if(MYDEBUG) alert("TableView_getMenuValues\ntempFilters="+tempFilters);
    tempTableData = globalTable.getMultipleFilteredData(globalTable.dataRows, tempFilters, false);
    //alert("getMenuValues: done filtering");
//  }

  // Get all distinct values in the current column.
  // This set of values are to be swapped in to this column's filter menu.
  //alert("getMenuValues: about to get distinct values");
  var menuValues = globalTable.getDistinctValues(tempTableData, menuColumn);
  //alert("getMenuValues: done getting distinct values");

  return menuValues;
}


// sort numeric menu values
function TableView_menuNumericSort(a, b) {
  return a - b;
}


// clear all filter selections: used when loading the page
// or resetting the table
function TableView_clearAllFilterSelections() {
  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    var selectId = TableView.MENU_SELECT_ID_PREFIX + i;
    if (document.getElementById(selectId) != null) {
      // maybe do this another way, to get rid of resetMenu function
      this.resetMenu(i);
    }
  }
}


function TableView_resetMenu(column) {
  // reset any user selections from the menu
  //alert('about to reset menu ' + column);
  selMenu = document.getElementById(TableView.MENU_SELECT_ID_PREFIX + column);

  // default: first menu item ('all') selected
  // disabled because it interferes with multi-select:
  // user has to be sure to deselect the 'all' option
  //selMenu.options[0].selected = true;

  // if we do multiple select, reset like this:
  //for (var i = 0; i < selMenu.options.length; i++) {
  //  selMenu.options[i].selected = false;
  //}
}


function TableView_resetAllButtonCursors() {
  var sortEl;
  var filterEl;
  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    sortEl = document.getElementById("sort" + i);
    if (sortEl) {
      sortEl.style.cursor = TableView.POINTER_CURSOR;
    }
    filtEl = document.getElementById("filt" + i);
    if (filtEl) {
      filtEl.style.cursor = TableView.POINTER_CURSOR;
    }
  }
}

// reset all buttons to default enabled states
// used when loading page or resetting the table
function TableView_resetAllButtonStates() {
  var sortEl;
  var filterEl;
  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    sortEl = document.getElementById("sort" + i);
    if (sortEl) {
      sortEl.src = GLOBAL_IMAGE_PATH + "sort.gif";
      sortEl.style.cursor = TableView.POINTER_CURSOR;
      sortEl.setAttribute("alt", "Click to sort");
    }
    // filtering, unless disabled
    if (this.isFilteringEnabled) {
      filtEl = document.getElementById("filt" + i);
      if (filtEl) {
        filtEl.src = GLOBAL_IMAGE_PATH + "filter.gif";
        filtEl.style.cursor = TableView.POINTER_CURSOR;
        filtEl.setAttribute("alt", "Click to filter");
      }
    }
  }
}

// update the status/instructions message above the table
function TableView_updateTableStatusMessage() {
  var node = document.getElementById("tblmsg");
  var existingText = node.firstChild;
  if (existingText) {
    var removedNode = node.removeChild(existingText);
  }
  var message;
  var totalDatasheets = globalTable.numberOfDatasheets;

  if (this.isFilteringEnabled) {
    // normal use: update message with the number of datasheets

    // get the current number of datasheets by looking at column 1 (i-button)
    var currentDatasheets = globalTable.getDistinctValues(globalTable.filteredDataRows, 1).length;

    if (currentDatasheets < totalDatasheets) {
      message = currentDatasheets + " of " + totalDatasheets;
    }
    else {
      message = "All " + totalDatasheets;
    }
    message += " datasheets shown. " + TableView.FILTERING_ENABLED_MESSAGE;
  }
  else {
    message = "All " + totalDatasheets + " datasheets shown. " +
              TableView.FILTERING_DISABLED_MESSAGE;
  }

  var text = document.createTextNode(message);
  node.appendChild(text);
}


// update the Web page with the table object's current sort and filter state
function TableView_updateTable(isNewAction) {
  var rowNodes = new Array();
  var rowNode;
  var cellNode;
  var cellText;
  var linkNode;
  var linkText;
  var attribute;
  var textContent;
  var parts;
  var docId;
  var relatedDocsIndex;
  var classBuffer;

  var htmlBuffer = new StringBuffer("");

  //alert("globalTable.gatewayName = " + globalTable.gatewayName);

  // don't set cursor to 'wait'; this was
  // already done by the event trigger

  // set window status to a wait state
  window.status = "Rebuilding table...";

  // build the array of row nodes
  // from the (cloned) raw data matrix
  for (var i = 0; i < globalTable.filteredDataRows.length; i++) {
    var row = globalTable.filteredDataRows[i];

    // append a dot to the status message
    // for a 'wait' animation effect
    // REMOVED FOR FASTER PERFORMANCE
    //if (i % 2 == 0) {
    //  window.status += ".";
    //}

    htmlBuffer.append("<tr");
	if (row.group == false) {
	  htmlBuffer.append(" class='tblrow-gray'");
	}
	
    htmlBuffer.append(">");

    // one cell for each column
    for (var j = 0; j < row.cells.length; j++) {

      // if this cell doesn't have a row span of 1 or greater,
      // don't display it
      if (row.cells[j].span < 1) {
        continue;
      }

      // create node for each cell
      htmlBuffer.append("<td");
      classBuffer = new StringBuffer("");

      // if the row span is greater than 1,
      // set the table cell's rowspan attribute
      if (row.cells[j].span > 1) {
        htmlBuffer.append(" rowspan='");
        htmlBuffer.append(row.cells[j].span);
        htmlBuffer.append("'");
        //alert("row " + i + ", col " + j + " = " + row.cells[j].span);
      }
		

      // depending on the column, add attributes
      if (j == 0) {
        // product page link cell
        classBuffer.append("listdscol");
      }
      else if (j == 1 && seriesColspan >= 2) {
        // i-button cell
        classBuffer.append("listcol");
        htmlBuffer.append(" valign='middle' width='35'");
      }
      else if (j == 2 && seriesColspan >= 2) {
        // PDF link cell
        classBuffer.append("listcol");
        htmlBuffer.append(" valign='middle' width='40'");
      }


	  // alter color in each row without span
	  /*
      if (! (row.cells[j].span > 1)) {
		if(i%2==0)
			classBuffer.append(" tblrow-gray-alter");
	  if(i==0 && false) alert("cell "+j+": "+globalTable.filteredDataRows[i].cells[j].displayText
	  +" SPAN="+row.cells[j].span+" COND1="+(! (row.cells[j].span > 1))+" COND2="+(i%2==0));
	  }
	  */
	  

      // sort column styles
	  /*
      if(globalTable.currentSortColumns[0]) {
		if(j < seriesColspan) {
var msg = "A j="+j+" -- sortCol="+globalTable.currentSortColumns[0].column;
			if(globalTable.currentSortColumns[0].column < seriesColspan - 2) {
				classBuffer.append(" sortcol-gray");
msg += " sort=on";
			}
if(i==0 && false) alert(msg);
		} else {
var msg = "B j="+j+" -- sortCol="+globalTable.currentSortColumns[0].column;
			if(globalTable.currentSortColumns[0].column == (j-2)) {
				classBuffer.append(" sortcol-gray");
msg += " sort=on";
			}
if(i==0 && false) alert(msg);
		}
	  }
      else if(globalTable.currentSortColumns[1]) {
		if(j < seriesColspan) {
			if(globalTable.currentSortColumns[1].column < seriesColspan - 2) {
				classBuffer.append(" sortcol1");
			}
		} else {
			if(globalTable.currentSortColumns[1].column == (j-2)) {
				classBuffer.append(" sortcol1");
			}
		}
	  }
      else if(globalTable.currentSortColumns[2]) {
		if(j < seriesColspan) {
			if(globalTable.currentSortColumns[2].column < seriesColspan - 2) {
				classBuffer.append(" sortcol2");
			}
		} else {
			if(globalTable.currentSortColumns[2].column == (j-2)) {
				classBuffer.append(" sortcol2");
			}
		}
	  }
	  */

      if ( (globalTable.currentSortColumns[0]) &&
           ( (globalTable.currentSortColumns[0].column == j)
             ||
             ((globalTable.currentSortColumns[0].column == 0) && (j < seriesColspan)) // (j == 1 || j == 2)
           )
         ) {
        classBuffer.append(" sortcol-gray");
      }
      else if ( (globalTable.currentSortColumns[1]) &&
                ( (globalTable.currentSortColumns[1].column == j)
                  ||
                  ((globalTable.currentSortColumns[1].column == 0) && (j < seriesColspan))
                )
              ) {
        classBuffer.append(" sortcol2");
      }
      else if ( (globalTable.currentSortColumns[2]) &&
                ( (globalTable.currentSortColumns[2].column == j)
                  ||
                  ((globalTable.currentSortColumns[2].column == 0) && (j < seriesColspan))
                )
              ) {
        classBuffer.append(" sortcol3");
      }

	  
	  
      // if this column currently has a filter applied,
      // set a style to allow for appearance change
      if (globalTable.currentFilters[j]) {
        classBuffer.append(" filt");
      }

      if (classBuffer.toString() != "") {
        htmlBuffer.append(" class='");
        htmlBuffer.append( classBuffer.toString() );
        htmlBuffer.append("'");
      }
      htmlBuffer.append(">");

      textContent = globalTable.filteredDataRows[i].cells[j].displayText;

      // add the cell's content;
      // depending on column, present differently
      if (j > 1) {
        htmlBuffer.append(textContent);
      }
      else if (j == 0) {
        // product page link
        parts = globalTable.filteredDataRows[i].cells[j + 1].text.split(",");
        docId = parts[0];
        htmlBuffer.append("<a href='/");
        htmlBuffer.append(globalTable.gatewayName);
        htmlBuffer.append("/list/product-");
        htmlBuffer.append(docId);
        htmlBuffer.append("/'>");
        htmlBuffer.append(textContent);
        htmlBuffer.append("</a>");
      }
      else if (j == 1) {
        // i-button link
		//var mojo = 0; mojo++; if(mojo==1) { alert(globalTable.filteredDataRows[i].cells[j].text); }
        parts = globalTable.filteredDataRows[i].cells[j].text.split(",");
        docId = parts[0];
        relatedDocsIndex = parts[1];
		/*
        htmlBuffer.append("<a href='/");
        htmlBuffer.append(globalTable.gatewayName);
        htmlBuffer.append("/list/product-");
        htmlBuffer.append(docId);
        htmlBuffer.append("/' onmouseover='m(");
        htmlBuffer.append(docId);
        htmlBuffer.append(", this, \"");
        htmlBuffer.append(relatedDocsIndex);
        htmlBuffer.append("\", \"rDocs-");
        htmlBuffer.append(relatedDocsIndex);
        htmlBuffer.append("\")'><img src='");
        htmlBuffer.append(GLOBAL_IMAGE_PATH);
        htmlBuffer.append("related/related-info.gif' alt='info' align='middle' border='0' name='rDocs-");
        htmlBuffer.append(relatedDocsIndex);
        htmlBuffer.append("' /></a>");
		*/
/*
<a class="link-no-style" href="http://www.vishay.com/doc?12013">
	<img class="img-link" alt="pdf" src="images/pdfmini.gif"><br>
	Datasheet
</a>
*/
		if(seriesColspan >= 2) { // we have PDF column
			htmlBuffer.append("<a class=\"link-no-style\" href=\"http://www.vishaypg.com/doc?");
			htmlBuffer.append(docId);
			htmlBuffer.append("\">\n");
			htmlBuffer.append("<img class=\"img-link\" alt=\"pdf\" src=\"images/pdfmini.gif\"><br>\n");
			htmlBuffer.append("Datasheet");
			htmlBuffer.append("</a>");
		} else {
			htmlBuffer.append(textContent);
		}
      }

      htmlBuffer.append("</td>");
    } // end for j

    htmlBuffer.append("</tr>\n");
  } // end for i

  // get the table and table body
  var tableDiv = document.getElementById(globalTable.tableDivElementId);

  // grab the top of the table
  var tableHtml = tableDiv.innerHTML.toString();
 // alert("tableHtml=\n"+tableHtml);
  // different browsers use different cases (IE is uppercase)
  var tableBodyIndex = tableHtml.toLowerCase().indexOf("<tbody");
  //alert("tableBodyIndex: " + tableBodyIndex);
  var tableTop = "\n<!-- TOP -->\n" + tableHtml.substring(0, tableBodyIndex);
  //alert(tableTop);

  // grab the bottom of the table
  var lastRowIndex = tableHtml.toLowerCase().lastIndexOf("<tr");
  //alert("lastRowIndex: " + lastRowIndex);
  var tableBottom = tableHtml.substring(lastRowIndex);
  //alert(tableBottom);

  // IE - need to have table in an enclosing div and replace
  // that enclosing div's innerHTML (the full table.)
  // In IE, if you try to just replace the table tbody or rows,
  // you get a weird 'unknown runtime error.'
  //
  // (Note: could replace table rows using DOM manipulation, but
  // rowspans mess up the table in IE when this method is used.)
  var tableBody = "\n<!-- BODY -->\n" + htmlBuffer.toString();
  //alert("tableTop\n"+tableTop.substring(300));
  //alert("tableBody\n"+tableBody);
  var inner = tableTop + tableBody; //+ tableBottom; // bottom removed in new design with floating header
  //inner.replace(/<TBODY>/, "<tbody id=\"parametric-table-body\">");
  tableDiv.innerHTML = inner;
  tableDiv.getElementsByTagName("TBODY")[0].id = "parametric-table-body";
  //alert("inner\n"+inner);
  //alert("innerHTML\n"+tableDiv.innerHTML);

  // update the hidden form fields used for maintaining
  // sort/filter state across back/forward navigation
  this.updateStateFields();

  // if this is a new sort/filter action, add it to the action stack
  if (isNewAction) {
    var sortState = globalTable.currentSortColumns.toString();
    var filterState = globalTable.currentFilters.toString();
    //alert("add to actionStack:\n" + sortState + "\n" + filterState);
    this.actionStack.push( new TableAction(sortState, filterState) );
    // enable the undo button
    var undoButtonEl = document.getElementById(TableView.UNDO_BUTTON_ID);
    undoButtonEl.disabled = false;
    undoButtonEl.style.background = "#ddd url(" + GLOBAL_IMAGE_PATH + "undo.gif) no-repeat center left";
    undoButtonEl.style.cursor = TableView.POINTER_CURSOR;
  }

  // need to assign the event handlers again, since they were blown away
  TableController.setEventHandlers();

  // update the message line above the table
  this.updateTableStatusMessage();

  // set page body cursor back to default
  document.body.style.cursor = "auto";

  // set button cursors back to default
  this.resetAllButtonCursors();

  // update button states
  this.updateButtonStates();

  // enable the reset button
  var resetButtonEl = document.getElementById(TableView.RESET_BUTTON_ID);
  resetButtonEl.disabled = false;
  resetButtonEl.style.cursor = TableView.POINTER_CURSOR;

  // set window status back to default
  window.status = window.defaultStatus;
}


// toggle the state of the sort button
function TableView_changeSortButtonState(buttonId) {
  //var buttonEl = document.getElementById(this.id);
  var buttonEl = document.getElementById(buttonId);
  //alert("buttonEl.src=" + buttonEl.src);
  if (buttonEl.src.indexOf("/sort.gif") > -1) {
    // TO DO: add more down states for total of three:
    // one for 'no sort', two more for asc/desc.
    buttonEl.src = GLOBAL_IMAGE_PATH + "sort-dn.gif";
  }
  else {
    buttonEl.src = GLOBAL_IMAGE_PATH + "sort.gif";
  }
}


function TableView_restoreOriginalTable() {
  // set window status to a wait state
  window.status = "Rebuilding table...";

  // TO DO: move these state changes back to the model (table),
  // then call a method here in the view to update the page.

  // set back to default sort and filter states
  globalTable.sortColumn = Table.SORT_COLUMN_DEFAULT;
  globalTable.sortAscending = Table.SORT_ASCENDING_DEFAULT;
  globalTable.currentFilterColumn = Table.CURRENT_FILTER_COLUMN_DEFAULT;
  globalTable.currentFilterValue = Table.CURRENT_FILTER_VALUE_DEFAULT;
  globalTable.currentSortColumns = new Array();

  // clear the current filters list
  globalTable.currentFilters = new Array();

  // put the original table HTML back the way it was
  //var tableDiv = document.getElementById("lsttbl-div");
  var tableDiv = document.getElementById(globalTable.tableDivElementId);
  tableDiv.innerHTML = globalTable.originalTable;

  // update the filter menus
  this.updateMenus();

  // update the hidden form fields used for maintaining
  // sort/filter state across back/forward navigation
  this.updateStateFields();

  // need to reassign event handlers
  //assignEventHandlers();
  TableController.setEventHandlers();

  // reset the data table and update the message line
  //clonedRawData = cloneData(rawData);
  globalTable.filteredDataRows = globalTable.cloneDataRows(globalTable.dataRows);
  this.updateTableStatusMessage();

  // finally, clear any existing filter menu (form) selections
  // and restore default button states
  this.clearAllFilterSelections();
  this.resetAllButtonStates();

  // clear the undo action stack and disable the undo button
  this.actionStack = new Array();
  var undoButtonEl = document.getElementById(TableView.UNDO_BUTTON_ID);
  undoButtonEl.disabled = true;
  undoButtonEl.style.background = "#ddd url(" + GLOBAL_IMAGE_PATH + "undo-dis.gif) no-repeat center left";

  // disable the reset button
  document.getElementById(TableView.RESET_BUTTON_ID).disabled = true;

  // set page body cursor back to default
  document.body.style.cursor = "auto";

  // set button cursors back to default
  this.resetAllButtonCursors();

  // AL May 2008: add span to decoder sunglasses column
  if(globalDecoderSunglasses.isActive()) {
    globalTable.spanRows();
    globalTableView.updateTable(false);
  }

  // set window status back to default
  window.status = window.defaultStatus;
}


// get the pixel position of the top of an element
function TableView_getTop(elementId) {
  var top = 0;
  var myEl = document.getElementById(elementId);

  while (myEl.offsetParent) {
    top += myEl.offsetTop;
    myEl = myEl.offsetParent;
  }

  return top;
}


// get the pixel position of the left side of an element
function TableView_getLeft(elementId) {
  var left = 0;
  var myEl = document.getElementById(elementId);

  while (myEl.offsetParent) {
    left += myEl.offsetLeft;
    myEl = myEl.offsetParent;
  }

  return left;
}


function TableView_showMenu(buttonId) {
  var column = buttonId.substring(TableView.MENU_DIV_ID_PREFIX.length);
  var menuId = TableView.MENU_DIV_ID_PREFIX + column;
  var selectId = TableView.MENU_SELECT_ID_PREFIX + column;
  var selectEl = document.getElementById(selectId);
  var selIndex = selectEl.options.selectedIndex;

  // if any other menu is open, close it
  var openMenuColumn = this.getFirstOpenMenuColumn();
  if (openMenuColumn > -1) {
    this.closeMenu(TableView.MENU_DIV_ID_PREFIX + openMenuColumn);
  }

  // swap image to put menu in the down position;
  // handle both active and inactive states
  if (selIndex > 0) {
    document.getElementById(buttonId).src = GLOBAL_IMAGE_PATH + "filter-on-dn.gif";
  }
  else {
    document.getElementById(buttonId).src = GLOBAL_IMAGE_PATH + "filter-dn.gif";
  }

  var el = document.getElementById(menuId);
  if (el.style.display != "inline") {

    // get the number of values, so we can set the menu height
// NOTE: SET MENU HEIGHT ELSEWHERE?
//    var numberOfValues = globalTable.getDistinctValues(globalTable.dataRows, column).length;

    var selectEl = document.getElementById(selectId);
    var buttonEl = document.getElementById(buttonId);

    el.style.position = 'absolute';

    el.style.top = (this.getTop(buttonId) +
                    buttonEl.offsetHeight + 1) + 'px';

    el.style.right = (document.body.clientWidth - this.getLeft(buttonId) -
                      buttonEl.offsetWidth) + 'px';

    //msg = "this.getTop = " + this.getTop(buttonId) +
    //      "\nthis.getLeft = " + this.getLeft(buttonId) +
    //      "\nbuttonEl.offsetHeight = " + buttonEl.offsetHeight +
    //      "\nbuttonEl.offsetWidth = " + buttonEl.offsetWidth +
    //      "\nbuttonEl.parentNode.offsetHeight = " + buttonEl.parentNode.offsetHeight +
    //      "\nbuttonEl.parentNode.offsetWidth = " + buttonEl.parentNode.offsetWidth +
    //      "\nbuttonEl.parentNode.parentNode.offsetHeight = " + buttonEl.parentNode.parentNode.offsetHeight +
    //      "\nbuttonEl.parentNode.parentNode.offsetWidth = " + buttonEl.parentNode.parentNode.offsetWidth;
    //msg += "\ndocument.body.clientWidth = ";
    //if (document.body.clientWidth) {
    //  msg += document.body.clientWidth;
    //}
    //alert(msg);

    // make the menu appear
    el.style.display = "inline";
	el.style.zIndex  = 5;
  }
}


function TableView_closeMenu(id) {
  var column = id.substring(TableView.MENU_DIV_ID_PREFIX.length);

  // actually, the 'menuId' is the overall div id
  var menuId = TableView.MENU_DIV_ID_PREFIX + column;
  var buttonId = "filt" + column;

  // id of the actual select (menu) element
  var selectId = TableView.MENU_SELECT_ID_PREFIX + column;
  var selectEl = document.getElementById(selectId);
  var selIndex = selectEl.options.selectedIndex;

  el = document.getElementById(menuId);
  if (el.style.display != "none") {
    el.style.display = "none";
  }

  // swap image to put menu in the up position
  if (selIndex > 0) {
    document.getElementById(buttonId).src = GLOBAL_IMAGE_PATH + "filter-on.gif";
  }
  else {
    document.getElementById(buttonId).src = GLOBAL_IMAGE_PATH + "filter.gif";
  }

  // get all selected items
  var selectedValues = new Array();
  var valueText;
  for (var i = 0; i < selectEl.options.length; i++) {
    if (selectEl.options[i].selected) {
      valueText = selectEl.options[i].text;
      selectedValues.push(valueText);
    }
  }

  var filterValuesSame = globalTable.areSameFilterValues(column, selectedValues);
  var allSelected = selectEl.options[0].selected;
  var filterExists = globalTable.currentFilters[column];

  // if 'all' was selected but there was previously no filter here,
  // this is a 'do-nothing' action;
  // deselect 'all' and any other selected values so they don't appear
  // selected the next time the menu is opened
  if ( (allSelected) && (!filterExists) ) {
    selectEl.options.selectedIndex = -1;
  }

  // the button element;
  var buttonEl = document.getElementById(buttonId);

  // if the filter values have changed and 'all' is not selected (change filter)
  // and there was at least one value selected
  // OR
  // if a filter exists on the column and 'all' is selected (remove filter),
  // then trigger the filtering process
  if ( ( (!filterValuesSame) && (!allSelected) && (selectedValues.length > 0) )
       ||
       ( (filterExists) && (allSelected) )
     ) {
    buttonEl.style.cursor = "wait";
    document.body.style.cursor = "wait";
    var command = "TableController.filterChanged(" + column + ");"

    window.setTimeout(command, 1);
  }
}


function TableView_toggleMenu(buttonId) {
  var column = buttonId.substring(TableView.MENU_DIV_ID_PREFIX.length);
  var menuId = TableView.MENU_DIV_ID_PREFIX + column;

  var el = document.getElementById(menuId);
  if (el.style.display != "inline") {
    this.showMenu(buttonId);
  }
  else {
    this.closeMenu(buttonId);
  }
}


// returns the column number of the first
// encountered column with an open menu
function TableView_getFirstOpenMenuColumn(startIndex) {
  var result = -1;
  var menuDivId;

  var i = 0;

  // if optional startIndex parameter specified,
  // use it as the starting point instead of zero
  if (startIndex) {
    i = startIndex;
  }

  while ((i < globalTable.numberOfColumns) && (result == -1)) {
    menuDivId = TableView.MENU_DIV_ID_PREFIX + i;
    if ( (document.getElementById(menuDivId)) &&
         (document.getElementById(menuDivId).style.display == "inline") ) {
      result = i;
    }
    i++;
  }

  return result;
}


// writes the current sort and filter state to hidden
// form fields used to reconstruct the table state
// after user navigates back or forward in the browser
function TableView_updateStateFields() {
  var sortStateEl = document.getElementById(TableView.SORT_STATE_FIELD);
  var filterStateEl = document.getElementById(TableView.FILTER_STATE_FIELD);
  //alert("s-id="+TableView.SORT_STATE_FIELD+", s="+sortStateEl+", f-id="+TableView.FILTER_STATE_FIELD+", f="+filterStateEl);
  sortStateEl.value = globalTable.currentSortColumns.toString();
  filterStateEl.value = globalTable.currentFilters.toString();
}


// restore the table's sort columns from a string that represents
// the previous state
function TableView_restoreSortColumns(sortState) {

  // clear any old state:
  // sort columns list
  globalTable.currentSortColumns = new Array();
  // reset temporary variables used in multiple column sorting
  globalTable.sortColumn = Table.SORT_COLUMN_DEFAULT;
  globalTable.sortAscending = Table.SORT_ASCENDING_DEFAULT;

  // add sort column
  if (sortState != "") {
    var columns = sortState.split(",");
    var parts;
    var column;
    var ascending;

    for (var i = 0; i < columns.length; i++) {
      //alert("columns[" + i + "] = " + columns[i]);
      parts = columns[i].split(":");
      column = parts[0];
      ascending = parts[1];
      globalTable.currentSortColumns[i] = new TableSortColumn(column, ascending);
    }
  }
}


// restore the table's filters from a string that represents
// the previous state
function TableView_restoreFilters(filterState) {

  // clear any old state
  globalTable.currentFilters = new Array();

  // add filters
  if (filterState != "") {
    var filters = filterState.split(",");
    var parts;
    var column;
    var values;

    for (var i = 0; i < filters.length; i++) {
      // since it's a sparse array, the comma separated
      // list of filters sometimes doesn't have anything
      // between two commas; if so, ignore
      if (filters[i] != "") {
        //alert(filters[i]);
        var parts = filters[i].split(":");
        column = parts[0];
        //alert("column = " + column);
        values = parts[1].split("||");

        for (var j = 0; j < values.length; j++) {
          values[j] = unescape( values[j] );
          //alert("values[" + j + "] = " + values[j]);
        }
if(MYDEBUG) alert("restoreFilters column="+column);
        globalTable.currentFilters[i] = new TableFilter(column, values);
      }
    } // end for
  } // end if
}


// if there is "previous state" information present (for remembering
// the table state when the browser goes to another page), restore the
// table display accordingly
function TableView_restorePreviousState() {
  var sortStateEl = document.getElementById(TableView.SORT_STATE_FIELD);
  var filterStateEl = document.getElementById(TableView.FILTER_STATE_FIELD);
  //var msg = "";

  var hasState = false;

  if (sortStateEl.value != "") {
    hasState = true;
    this.restoreSortColumns(sortStateEl.value);
    //msg += "previous sort state present.\n";
  }
  if (filterStateEl.value != "") {
    hasState = true;
    this.restoreFilters(filterStateEl.value);
    //msg += "previous filter state present.";
  }

  if (hasState) {
    //alert(msg);

    // filter, sort, span, and display table
if(MYDEBUG) alert("TableView_restorePreviousState\ntempFilters="+globalTable.currentFilters);
    globalTable.filteredDataRows = globalTable.getMultipleFilteredData(globalTable.dataRows,
                                                                       globalTable.currentFilters);
	//Derek test Code                                                                       
    //alert("this is the error: "+Table.multipleColumnSort);                                    
    
    var derek = 0;
    globalTable.filteredDataRows.sort();            
    //globalTable.filteredDataRows.sort(Table.multipleColumnSort);            
    //globalTable.filteredDataRows.sort(Table.singleColumnSort);
    globalTable.spanRows();
    this.updateTable(TableView.DEBUG_OFF); // this is not a new action, so prevent
                                           // it from going into the action stack
  }
}


// Undo the last action: restore the table to the last action
// in the action stack. Redo is not currently supported; each undo
// operation clobbers its corresponding action.
//
// Also, the undo information (action stack) is currently lost
// when the browser goes to another page and returns. If desired,
// we could store the entire action stack in a hidden form field
// so the undo history is not lost.
function TableView_undoLastAction() {
  //alert("undo: actionStack.length = " + this.actionStack.length);
  var currentAction = this.actionStack.pop(); // throw away current state

  // if there is a previous action, restore it
  //alert("actionStack length = " + this.actionStack.length);
  if (this.actionStack.length > 0) {
    var lastAction = this.actionStack[this.actionStack.length - 1];

    //alert("lastAction: " + lastAction);
    var restoreSortState = "";
    if (lastAction.sortState) {
      restoreSortState = lastAction.sortState;
    }
    //alert("about to call restoreSortColumns(" + restoreSortState + ")");
    this.restoreSortColumns(restoreSortState);

    var restoreFilterState = "";
    if (lastAction.filterState) {
      restoreFilterState = lastAction.filterState;
    }
    //alert("about to call restoreFilters(" + restoreFilterState + ")");
    this.restoreFilters(restoreFilterState);
    //alert("lastAction:\n" + lastAction.sortState + "\n" + lastAction.filterState);

    // filter, sort, span, and display table
    globalTable.filteredDataRows = globalTable.getMultipleFilteredData(globalTable.dataRows,
                                                                       globalTable.currentFilters);
    // if there are any sort columns, sort the table
    if (globalTable.currentSortColumns[0]) {
      globalTable.filteredDataRows.sort(Table.multipleColumnSort);
      //globalTable.filteredDataRows.sort(Table.singleColumnSort);
    }
    globalTable.spanRows();
    this.updateTable(TableView.DEBUG_OFF);  // this is not a new action, so prevent
                                            // it from going into the action stack
    this.updateMenus();
  }
  else {
    // there is no previous action; reset the table to the original state
    globalTableView.restoreOriginalTable();
  }

  // if there are no more actions, disable the undo button
  if (this.actionStack.length == 0) {
    document.getElementById(TableView.UNDO_BUTTON_ID).disabled = true;
  }
}
// class: TableController
//
// JG 1/05
//
// Controller for table sorting and filtering.
//
function TableController() {
  // TableController has only class methods.
  // I had trouble making it a proper object and assigning events to its methods.
  // I could get the events to fire, but the instance would somehow not
  // have its properties available. Looks like in assigning an event handler,
  // the assignment just grabs the method instead of assigning the instance
  // and its method together.
}
// class properties
TableController.DEBUG_ON = true;
TableController.DEBUG_OFF = false;
// class methods
TableController.setEventHandlers = TableController_setEventHandlers;
TableController.doChangeSortButtonState = TableController_doChangeSortButtonState;
TableController.doFilter = TableController_doFilter;
TableController.doSort = TableController_doSort;
TableController.filterChanged = TableController_filterChanged;
TableController.doRestoreOriginalTable = TableController_doRestoreOriginalTable;
TableController.doUndoLastAction = TableController_doUndoLastAction;
TableController.doCloseMenu = TableController_doCloseMenu;
TableController.doKeyDown = TableController_doKeyDown;
TableController.doKeyUp = TableController_doKeyUp;
TableController.doCancelMenu = TableController_doCancelMenu;

TableController.doServerSideSort = TableController_doServerSideSort;

/////////////////////////////////////////////////////////////////////////////////////
// class methods
function TableController_setEventHandlers(isFilteringDisabled) {
  var headerNode = document.getElementById("btnrow");
  var children = headerNode.childNodes; // child nodes of the header row
  var thChildren; // child nodes of each header cell node

  // set handler on reset button
  document.getElementById("tblresetbtn").onclick = TableController.doRestoreOriginalTable;

  // set handler on undo button
  document.getElementById("tblundobtn").onclick = TableController.doUndoLastAction;

  // set handlers on sort buttons and filter buttons
  for (var i = 0; i < children.length; i++) {
    node = children[i];
    if (node.nodeType == 1 /* Node.ELEMENT_NODE */) {
      //alert(node.nodeName);
      if (node.nodeName.toLowerCase() == "th") {

        thChildren = node.childNodes;
        for (var j = 0; j < thChildren.length; j++) {
          node = thChildren[j];
          if (node.nodeType == 1 /* Node.ELEMENT_NODE */) {

            if (node.nodeName.toLowerCase() == "img") {
              // filter, provided it's not disabled
              if (node.getAttribute("id").indexOf("filt") > -1) {
                if (globalTableView.isFilteringEnabled) {
                  node.onmousedown = TableController.doFilter;
                }
              }
              else if (node.getAttribute("id").indexOf("sort") > -1) {
                // sort
                node.onclick = TableController.doSort;
                node.onmousedown = TableController.doChangeSortButtonState;
              }
            }

          }
        } // end for

      }
    }
  } // end for


  // set handlers on hidden filter menus
  var menuSelectNode;
  for (var i = 0; i < globalTable.numberOfColumns; i++) {
    if (document.getElementById(TableView.MENU_DIV_ID_PREFIX + i)) {
      menuSelectNode = document.getElementById(TableView.MENU_SELECT_ID_PREFIX + i);
      menuSelectNode.onchange = TableController.doCloseMenu;
    }
  }

  // Set handler to close (cancel) a menu by clicking on the page, off the menu.
  //
  // the table area
  document.getElementById("lst").onmousedown = TableController.doCancelMenu;
  // above the table
  document.getElementById("hd").onmousedown = TableController.doCancelMenu;
  document.getElementById("top").onmousedown = TableController.doCancelMenu;
  // below the table
  //document.getElementById("ft").onmousedown = TableController.doCancelMenu;

  // set keyboard handlers (for multiple filter selections)
  document.onkeydown = TableController.doKeyDown;
  document.onkeyup = TableController.doKeyUp;
}


function TableController_doChangeSortButtonState() {
  globalTableView.changeSortButtonState(this.id);
}


function TableController_doFilter(event) {
  globalTableView.toggleMenu(this.id);

  // IE: cancel the bubble event of clicking elsewhere on the table
  if (document.all) {
    window.event.cancelBubble = true;
  }
  else {
    // non-IE (Moz, etc.)
    event.stopPropagation();
  }

}


function TableController_doSort() {
  var buttonEl = document.getElementById(this.id);
  buttonEl.style.cursor = 'wait';
  document.body.style.cursor = 'wait';

  var newSortColumn = parseInt(this.id.substring(TableView.MENU_DIV_ID_PREFIX.length), 10);
  var command;
//alert("newSortColumn="+newSortColumn+" ("+this.id+")");
  // if a filter menu is open, close it
  var openMenuColumn = globalTableView.getFirstOpenMenuColumn();
  if (openMenuColumn > -1) {
    globalTableView.closeMenu(TableView.MENU_DIV_ID_PREFIX + openMenuColumn);
  }

  // filter:
  // return a clone of the original data, taking filters into account.
  // (Do we really still need to filter here too?)
  globalTable.filteredDataRows = globalTable.getMultipleFilteredData(globalTable.dataRows,
                                                                     globalTable.currentFilters);

  // sort
  // TO DO: move some of this logic into the table object?
  // ALSO: reconcile with filterChanged method: eliminate code duplication
  //alert("sortColumn = " + sortColumn + "  newSortColumn = " + newSortColumn);

  // Three things that can happen:
  // 1. After a previous sort, a new column is sorted
  // 2. After a previous sort, the same column is sorted, reversing the order
  // 3. New sort; there were no previous sorts
  var currentSortColumn = -1;
  if (globalTable.currentSortColumns[0]) {
    currentSortColumn = globalTable.currentSortColumns[0].column;
  }

  if (currentSortColumn != newSortColumn) {
    // sorting a new column: add it to the top of the sort columns collection
    globalTable.currentSortColumns.unshift( new TableSortColumn(newSortColumn,
                                                                Table.SORT_ASCENDING_DEFAULT) );

    // reset icon on old sort column
    // (if there are more than three sort columns total)
    //if (globalTable.currentSortColumns[3]) {
    //  var oldButtonEl = document.getElementById("sort" + globalTable.currentSortColumns[3].column);
    if (globalTable.currentSortColumns[1]) {
      var oldButtonEl = document.getElementById("sort" + globalTable.currentSortColumns[1].column);
      oldButtonEl.src = GLOBAL_IMAGE_PATH + "sort.gif";
    }
  }
  else {
    // same column, but reverse the sort order
    globalTable.currentSortColumns[0].ascending = !globalTable.currentSortColumns[0].ascending;
  }

  // change sort button state
  if (globalTable.currentSortColumns[0].ascending) {
    // TO DO: change this to 'down' button state
    // should have previous direction until sorting finished.
    // remember, three states: no sort, asc, desc.
    buttonEl.src = GLOBAL_IMAGE_PATH + "sort-asc.gif";
    buttonEl.setAttribute("alt", "Click to sort (descending)");
  }
  else {
    // TO DO: change this to 'down' button state as above
    buttonEl.src = GLOBAL_IMAGE_PATH + "sort-desc.gif";
    buttonEl.setAttribute("alt", "Click to sort");
  }

  // debug: show columns that will be sorted on
  //var msg = "sort: \n";
  //for (var i = 0; i < globalTable.currentSortColumns.length; i++) {
  //  msg += globalTable.currentSortColumns[i].column + " " +
  //         globalTable.currentSortColumns[i].ascending +
  //         "\nisNumeric = " +
  //         globalTable.numericColumns[globalTable.currentSortColumns[i].column] +
  //         "\n";
  //}
  //alert(msg);

  // build command for sorting
  command = "globalTable.filteredDataRows.sort(Table.multipleColumnSort);";
  //command = "globalTable.filteredDataRows.sort(Table.singleColumnSort);";

  // need to span rows and update table immediately after, in same command
  command += " globalTable.spanRows(); globalTableView.updateTable(TableController.DEBUG_ON);";

  //alert("command: " + command);
  window.setTimeout(command, 1);
}


// this is triggered by a change to a filter menu's selection
function TableController_filterChanged(filterColumn) {
  var option;
  var command = "";
  var selectEl = document.getElementById(TableView.MENU_SELECT_ID_PREFIX +
                                         filterColumn);

  var filterValues = new Array();
  for (var i = 0; i < selectEl.options.length; i++) {
    option = selectEl.options[i];
    if (option.selected) {
      filterValues.push(option.text);
    }
  }

if(MYDEBUG) alert("TableController_filterChanged column="+filterColumn);

  // update the current filters collection
  //alert("TableController_filterChanged: column = " + filterColumn + "\n" +
  //      "values = " + filterValues.toString() );
  globalTable.currentFilters[filterColumn] = new TableFilter(filterColumn,
                                                             filterValues);

  // if the user selected '(all)', delete this filter
  if ( globalTable.currentFilters[filterColumn].valueExists("(all)") ) {
if(MYDEBUG) alert("delete " + filterColumn);
    delete globalTable.currentFilters[filterColumn];
    // deselect the 'all' menu option
    selectEl.options[0].selected = false;
  }

  // filter
  globalTable.filteredDataRows = globalTable.getMultipleFilteredData(globalTable.dataRows, globalTable.currentFilters);

  // check for error condition
  // we need a unified error message page
  if (globalTable.filteredDataRows.length < 1) {
    alert("Assertion failed in TableController.filterChanged! " +
          "This is an internal code error! Table filtered to 0 length! " +
          "Contact Web Group and tell them exactly what page you were on " +
          "what column you were filtering, and what value(s) you were using " +
          "as the filter.");
  }

  // sort
  if (globalTable.currentSortColumns.length > 0) {
    command += "globalTable.filteredDataRows.sort(Table.multipleColumnSort);";
    //command += "globalTable.filteredDataRows.sort(Table.singleColumnSort);";
  }

  // need to span rows and update table immediately after, in same command
  command += " globalTable.spanRows(); globalTableView.updateTable(TableController.DEBUG_ON); globalTableView.updateMenus();";

  //alert("command: " + command);
  window.setTimeout(command, 1);
}


function TableController_doRestoreOriginalTable() {
  var buttonEl = document.getElementById(this.id);
  buttonEl.style.cursor = 'wait';
  document.body.style.cursor = 'wait';

  // if a menu is open, close it
  var openMenuColumn = globalTableView.getFirstOpenMenuColumn();
  if (openMenuColumn > -1) {
    var menuDivId = TableView.MENU_DIV_ID_PREFIX + openMenuColumn;
    globalTableView.closeMenu(menuDivId);
  }

  var command = "globalTableView.restoreOriginalTable();";
  window.setTimeout(command, 1);
}


function TableController_doUndoLastAction() {
  globalTableView.undoLastAction();
}


function TableController_doCloseMenu() {
//  var column = this.id.substring(TableView.MENU_SELECT_ID_PREFIX.length);
//  var menuDivId = TableView.MENU_DIV_ID_PREFIX + column;
//  globalTableView.closeMenu(menuDivId);

  // only close the menu if the key is not being pressed
  if (!globalTableView.isCtrlKeyPressed) {
    //alert("globalTableView.isCtrlKeyPressed = " + globalTableView.isCtrlKeyPressed);
    var column = this.id.substring(TableView.MENU_SELECT_ID_PREFIX.length);
    var menuDivId = TableView.MENU_DIV_ID_PREFIX + column;
    globalTableView.closeMenu(menuDivId);
  }
}


function TableController_doKeyDown() {
  //alert("doKeyDown");
  globalTableView.isCtrlKeyPressed = true;
}


function TableController_doKeyUp() {
//  if (globalTableView.numberOfVisibleMenus() > 0) {
  var openMenuColumn = globalTableView.getFirstOpenMenuColumn();
  //alert("openMenuColumn = " + openMenuColumn);
  if (openMenuColumn > -1) {
    //alert("doKeyUp");
    var menuDivId = TableView.MENU_DIV_ID_PREFIX + openMenuColumn;
    globalTableView.closeMenu(menuDivId);
  }
  globalTableView.isCtrlKeyPressed = false;
}


// used for canceling a menu if the user clicks off of it
function TableController_doCancelMenu() {
  // TO DO: rework this
  var column = -1;
  if (this.id) {
    column = this.id.substring(TableView.MENU_SELECT_ID_PREFIX.length);
  }
  var openMenuColumn = globalTableView.getFirstOpenMenuColumn();
  //alert("openMenuColumn = " + openMenuColumn);
  if (openMenuColumn > -1) {
    //alert("doCancelMenu: this.id = " + this.id + " column = " + column + " openMenuColumn = " + openMenuColumn);
    var menuDivId = TableView.MENU_DIV_ID_PREFIX + openMenuColumn;
    globalTableView.closeMenu(menuDivId);
  }
}

// for older browsers (currently only IE 5.0x): server-side sorting
function TableController_doServerSideSort() {
  //alert("serverSideSort: id=" + this.id + " name=" + this.name);
  var sortCol = this.name;

  var newUri = window.location.href;
  if (newUri.indexOf("sort") > -1) {
    newUri = window.location.href.substring(0, window.location.href.indexOf("sort"));
  }
  newUri += "sort?col=" + sortCol;

  // if sort column is present in the URI, and it's the same
  // column as the new sort column, reverse the sort order
  var oldSortIndex = window.location.href.indexOf("col=");
  if (oldSortIndex > -1) {
    var oldSortCol = window.location.href.substring(oldSortIndex + 4, oldSortIndex + 9);
    //alert("oldSortCol = " + oldSortCol);
    if (sortCol == oldSortCol) {
      if (window.location.href.indexOf("ord=desc") == -1) {
        newUri += "&ord=desc";
      }
    }
  }

  //alert("newUri = " + newUri);
  window.location.href = newUri;
}// table sorting and filtering: globals and initialization

///////////////////////////////////////////////////////////////////
// globals
//
GLOBAL_IMAGE_PATH = "/images/"; // for sites on servers
//GLOBAL_IMAGE_PATH = "file:///C:/tmp/vishaypg/192.168.204.21%3A8080/images/"; // for local development
var globalTable;
var globalTableView;
var globalDecoderSunglasses;

var seriesColspan = 2; // cols 0, 1, 2 belong to combined first "Series" column
// global function for initializing the
// system; run once on page load
function tableInit(seriesColspanByUser) {

  if(seriesColspanByUser && seriesColspanByUser > 0)
	seriesColspan = seriesColspanByUser;

//alert("seriesColspan="+seriesColspan);
  // fire a message as the first line of code executed, to see
  // how long it takes to decode the JS
  //alert("first line of code executed");

  globalClientSniffer = new ClientSniffer();

  
  
  
	// add links to decoder to original table before sorting and filtering starts
	globalDecoderSunglasses = new DecoderSunglasses();
	// NOTE: best to detect capabilities, not specific browsers
  // and version numbers.
  //
  // The approach here: detect the exceptional cases (known problems)
  // and let all others through


  // For old browsers and those with known serious problems, disable
  // some or all functionality
  //
  // Tested on IE 5.0/Win; seem to be quite a few problems, so decided not 
  // to support IE 5.0 (also, IE 5 for Mac has lots of problems as well).
  // IE 5.5 works well enough to at least support sorting (see below).
  //
  if (globalClientSniffer.browserVersion < 5) {
    // pre-version-5 browsers: don't do anything
    return;
  }  
  else if ((globalClientSniffer.isIE) &&
           (globalClientSniffer.browserVersion < 5.5)) {
    // for IE 5.0x, do server-side sorting; disable filtering
    var message = document.createTextNode(" You can sort the table below. " +
                                          "Due to a browser issue, filtering " + 
                                          "is not available; please upgrade " +
                                          "your browser or try another.");   
    document.getElementById("tblmsg").appendChild(message);   
    setServerSideSortHandlers();    
  }
  else {
    globalTable = new Table("lsttbl");

    globalTableView = new TableView();

    // some browsers: partial functionality
    //
    // Because of bug in KJS (Konqueror JavaScript),
    // filtering crashes browser in Safari/OmniWeb (Mac);
    // disable filtering
    // (Konqueror not tested yet)
    if (globalClientSniffer.isKJS) {
      globalTableView.isFilteringEnabled = false;
    }   

    TableController.setEventHandlers();

    // set table to previous state if the page
    // loaded after a browser back or forward operation
    globalTableView.restorePreviousState();

    // enable/update buttons
    globalTableView.updateButtonStates();

    // show initial message
    globalTableView.updateTableStatusMessage();
  }

  if(globalDecoderSunglasses.isActive()) {
    globalTable.spanRows();

    globalTableView.updateTable(false);
  }

}



// set handlers for server-side sorting; because this
// uses DOM, it only works for something like IE 5.0x,
// not very old (< 5.0) browsers
//
function setServerSideSortHandlers() {
  var headerNode = document.getElementById("btnrow");
  var children = headerNode.childNodes; // child nodes of the header row
  var thChildren; // child nodes of each header cell node

  // set handlers on sort buttons
  for (var i = 0; i < children.length; i++) {
    node = children[i];  
    if (node.nodeType == 1 /* Node.ELEMENT_NODE */) {
      //alert(node.nodeName);
      if (node.nodeName.toLowerCase() == "th") {
            
        thChildren = node.childNodes;
        for (var j = 0; j < thChildren.length; j++) {
          node = thChildren[j];  
          if (node.nodeType == 1 /* Node.ELEMENT_NODE */) {

            if (node.nodeName.toLowerCase() == "img") {
              if (node.getAttribute("id").indexOf("sort") > -1) {
                // sort
                node.onclick = TableController.doServerSideSort;
                
                // while we're here, enable the button icon,
                // unless it's not the current sort column which already
                // has its correct icon
                if (node.getAttribute("src").indexOf("sort-dis.gif") > -1) {
                  node.setAttribute("src", "/images/sort.gif");
                }
              }
            }
            
          }
        } // end for
        
      }
    }
  } // end for
  
}
