jQuery.noConflict();
/*	What has been loaded by the time we get here...
		- topicdb, an assoc array of all of the topics in the current index, indexed by idname
		- instancedb, an assoc array of topics, and for each topic an array of all of the instances of it in the current index, 
			sorted by ascending section ID 
		- relatedresdb, an assoc array of topics, and for each topic an array of all of the instances it in related resources 
			(not the current indexed thing), sorted by descending relevance?
		- exresdb, an assoc array of all exhibition elements (including the current indexed one) that current topics link to
	Where did these come from?
		1. create list of topic instances in current exhibit, creating new topics where necessary --> instancedb
		2. make a list of all of the topics in the current exhibit (ie, normalize instancedb) --> topicdb
		3. make a list of all (or the most relevant) instances of the topics that are NOT in the exhibit --> relatedresdb
		4. (exresdb holds content that would be part of an exhibition item's record in RfL) 

	Instance and relatedres records have:
		inst_label: name of the resource in which this topic appears (all will be the current exhibition)
		inst_type: amnh_exhib (exhibition content), amnh_rfl (online resource), other_rfl (non-amnh online resource)
		inst_rel: a number between 0 and 1 indicating how relevant the instance is to the topic ("definitude")
		rid_ref: a reference to the resource containing the topic; depends on inst_type
			for inst_type="amnh_exhib":
				exhib_id: the unique idname of the exhibition
				excase_id: the unique section id of the exhibition element
				ex_physP: boolean; false=>only a mention in the text; true=>represented physically by artifact or specimen;
					(maybe this should be representation type: text, picture, video, artifact, specimen, model, etc; )
				ex_url: url of online digital representation of instance
			for inst_type="amnh_rfl":
				rid_ref: the RfL resource ID of the resource
				rfl_url: the URL of the resource; may be a page within the resource
		inst_kwic: keyword in context, short blurb in resource where topic is mentioned
		
	This page and its functions builds the index for the current exhibition.
*/

var sortProfile = new Array("sort_name");
var typeSort = false;
var thisExhibitBaseURL = "";
var thisExhibitName = 'Water: H<sub>2</sub>0=Life';
var thisExhibitID = 'water';
var topics;
var topicndxs;
var pageInstances;
var topicDetailTemplate;

function sortByGlobalProfile(item1,item2) {
	// items are topics
	// decide which one goes first, by comparing topic fields in the order specified by sortProfile (ie, either name or type/name)
	for (var i=0;i<sortProfile.length;i++) {
		var sp = sortProfile[i];
		var isp1 = item1[sp].toLowerCase();
		var isp2 = item2[sp].toLowerCase();
		if (isp1 > isp2) {
			return 1;
		} else if (isp1 < isp2) {
				return -1;
				}
		}
	// if they're equal
	return 0;
}
function sortBySectionNumber(inst1,inst2) {
	// sections should be fixed point numbers, but 4.9 comes before 4.10
	var inst1parts = inst1['section'].split('.');
	var inst2parts = inst2['section'].split('.');
	var ip1, ip2;
	if (inst1parts[0]!=inst2parts[0]) {
		ip1 = parseInt(inst1parts[0],10);
		ip2 = parseInt(inst2parts[0],10);
	} else {
		ip1 = parseInt(inst1parts[1],10);
		ip2 = parseInt(inst2parts[1],10);
	}
	if (ip1 > ip2) {
		return 1;
	} else if (ip1 < ip2) {
		return -2;
	}
	return 0;
}

/* building the topic list...
	we should be able to use this list to specify the data for:
		index list
		rollover popups and their links
		topic detail popup window
	we should be able to resort this list by name or by type/name without needing to rebuild it.
*/

function buildTopicList() {
	topics = new Array();
	topicndxs = new Array();
	var topicrelatedsP = new Array();
	pageInstances = new Array();
	// make a pageInstance array relating section codes to URLs
	for (sid in exresdb[thisExhibitID]['sections']) {
		pageInstances[exresdb[thisExhibitID]['sections'][sid]['rel_link']]={'sid': sid};
	}
	
	for (var topic in topicdb) {
		var item=topicdb[topic];
		var topicObj = new Object();
		topicndxs[topic]=topics.length;   // save this topic's index into topics array
		
		topicObj['topicid'] = topic;
		topicObj['print_name'] = item['print_name'];
		topicObj['sort_name'] = item['sort_name'];
		topicObj['type'] = item['type'];

		topicObj['inexhibit'] = new Array();
		topicObj['otherexhibit'] = new Array();
		topicObj['onlineres'] = new Array();
		topicrelatedsP[topic] = false;  // initialize until further data
		topics.push(topicObj);
	}
	
	// do relateds first, so we can know whether a topic has any
	//    only if it does will we include it in the pageInstances to be linked from the internal content pages
	// NOTE: a record in the relatedresdb might apply to more than one topic; 
	//    we build the related instance object first then run through the list of topics to assign it
	for (var i=0;i<relatedresdb.length;i++) {
		var rel = relatedresdb[i];
		var instObj = new Object();
		switch (rel['inst_type']) {
			case 'amnh_exhib':
				var sectionID = rel['rid_ref']['excase_id'];
				instObj['type'] = 'amnh_exhib';
				var exhibid = rel['rid_ref']['exhib_id'];
				instObj['exhibid'] = exhibid;
				instObj['exhibname'] = exresdb[exhibid]['print_name'];
				instObj['sectionid'] = sectionID;
				instObj['sectionname'] = (sectionID?exresdb[exhibid]['sections'][sectionID]['sname']:"");
				instObj['weight'] = rel['inst_rel'];
				instObj['mapurl'] = exresdb[exhibid]['basemap'];
				if (sectionID) {
					instObj['detaillink'] = exresdb[exhibid]['sections'][sectionID]['abs_link'] ||  exresdb[exhibid]['baseURL']+(exresdb[exhibid]['sections'][sectionID]['rel_link']||"");
				} else {
					instObj['detaillink'] = exresdb[exhibid]['baseURL'];
				}
				instObj['kwic'] = rel['inst_kwic'];
				var reltype = 'otherexhibit';
				break;
			case 'amnh_rfl':
			case 'other_rfl':
			default: // 
				instObj['type'] = rel['inst_type'];
				var exhibid = rel['rid_ref']['exhib_id'];
				instObj['resid'] = rel['rid_ref']['rfl_id'];
				instObj['resname'] = rel['inst_label'];
				instObj['weight'] = rel['inst_rel'];
				instObj['detaillink'] = rel['rid_ref']['rfl_url'];
				instObj['kwic'] = rel['inst_kwic'];
				var reltype = 'onlineres';
				break;
		}
		var instTopics = rel['topics'];
		for (var t=0;t<instTopics.length;t++) {
			var topic = instTopics[t];
			topicrelatedsP[topic] = true;
			if (topics[topicndxs[topic]]) {
				topics[topicndxs[topic]][reltype].push(instObj);
			} else {alert("Where's "+topic+"??");}
		}
	}

	for (var i=0;i<instancedb.length;i++) {
		// get the instances in the current exhibition index
		var inst = instancedb[i];
		var instObj = new Object();
		
		// 20080511: instances can belong to one or more topics
		var instTopics = inst['topics'];
		
		var sectionID = inst['rid_ref']['excase_id'];
		var pageURL;
		instObj['type'] = 'amnh_exhib';
		instObj['exhibid'] = thisExhibitID;
		instObj['exhibname'] = thisExhibitName;
		instObj['sectionid'] = sectionID;
		instObj['sectionname'] = (sectionID?exresdb[thisExhibitID]['sections'][sectionID]['sname']:"");
		instObj['weight'] = inst['inst_rel'];
		instObj['mapurl'] = exresdb[thisExhibitID]['basemap'];
		instObj['kwic'] = inst['inst_kwic'];
		if (sectionID && exresdb[thisExhibitID]['sections'][sectionID]!=null) {
			pageURL = thisExhibitBaseURL+exresdb[thisExhibitID]['sections'][sectionID]['rel_link'];
		}
		instObj['detaillink'] = pageURL;
		
		for (var t=0;t<instTopics.length;t++) {
			var topic = instTopics[t];
			var tndx = topicndxs[topic];
			if (pageURL!=null) {
				// and add this instance to the list of topic references indexed by web page IFF it has any related stuff
				if (topicrelatedsP[topic]) {
					if (!pageInstances[pageURL]['topics']) pageInstances[pageURL]['topics']=new Array();
					// keep the highest weight instance...
					if (!pageInstances[pageURL]['topics'][topic] || (inst['inst_rel'] > pageInstances[pageURL]['topics'][topic]['weight'])) {
						pageInstances[pageURL]['topics'][topic] = {"weight": inst['inst_rel'], "indx": topics[tndx]['inexhibit'].length};  
					}
				}
			}
			if (tndx!=null) {
				topics[tndx]['inexhibit'].push(instObj);
			} else {alert("NO ndx for " + topic);}   // should only need this for debugging
		}
	}			
}

function buildHTML() {
	var item_div = "";
	var currentType = "";
	var divbuffer;
	var bigbuffer = '';
	var instoverflow = 6;
	var instlineheight = 21;
	if (!topicDetailTemplate) topicDetailTemplate = jQuery('#topic_detail_master').html();

	//jQuery('#indexlist').html('');
	topics.sort(sortByGlobalProfile);
	

	for (var i=0;i<topics.length;i++) {
		var topic=topics[i];
		var topicid = topic['topicid'];
		topicndxs[topicid]=i;   // remember this topic's current sort index into topics array
		if (typeSort) {
		// if we're sorted by topic, is this a topic break?  If so, output a topic header
			if (topic['type'] != currentType) {
				currentType = topic['type'];
				divbuffer = '<div class="type_header" id="'+currentType+'">'+currentType.replace(/_/g,' ')+'</div>';
				// jQuery('#indexlist').append(divbuffer);
				bigbuffer += divbuffer;
			}
		}
		if (!topic['indexHTML']) {
			divbuffer = '<div class="index_item" id="'+topicid+'" tndx="'+i+'">';
			
			// long topic names can wrap to two lines.  
			//   longish names plus the info icon can also wrap
			var wrapit = (topic['print_name'].length>30);  // 28 a conservative estimate (for css specified font size)
			// output the name
			divbuffer += '<div class="index_item_topic_name"><div class="index_item_name">'+topic['print_name']+'</div>';
			// does it have a descrption?
			if (topicdb[topicid]['definition']) {
				divbuffer += '<div class="index_item_desc_icon">&nbsp;</div>';
				wrapit = (topic['print_name'].length>26);  // info icon about 4 chars worth?
			}
			divbuffer += '</div>';


			//   20080508: we have to manage the line height explicitly when the number of instances goes to more than one line
			//   I'm only checking whether in_exhibit instances wrap.  BY convention, the others shouldn't have that many instnaces.
			var instheight = instlineheight;
			if (topic['inexhibit'].length > instoverflow) { 				
					instheight = (Math.floor((topic['inexhibit'].length-1)/instoverflow)+1)*instlineheight; 
			} else if (wrapit) {
				instheight = 2*instlineheight;
			}
			// get the instances in the current exhibition index
			divbuffer += '<ul class="index_item_instances" style="height:'+instheight+'px;">';
			var instarray = topic['inexhibit'];			  
			for (var j=0;j<instarray.length;j++) {
				var inst = instarray[j];
				var exhibid = inst['exhibid'];
				var sectionID = inst['sectionid'];

				divbuffer += '<li class="index_item_instance" id="inexhibit_'+i+'_'+j+'" style="float:left;" tndx="' + i + '" iset="inexhibit" indx="' + j + '">';
				divbuffer += '&nbsp;&nbsp;&nbsp;&nbsp;</li>';
			}

			divbuffer += '</ul>';  // index_item_instances
		
			// instances in other permanent exhibition halls
			divbuffer += '<ul class="index_item_exinstances" style="height:'+instheight+'px;">&nbsp;';
			instarray = topic['otherexhibit'];

			for (var j=0;j<instarray.length;j++) {
				var inst = instarray[j];

				divbuffer += '<li class="index_item_instance" id="otherexhibit_'+i+'_'+j+'" tndx="' + i + '" iset="otherexhibit" indx="' + j + '">';
				divbuffer += '&nbsp;&nbsp;&nbsp;&nbsp;</li>';
				// ugh...this ul isn't wrapping, do it explicitly after every fourth item
				if (!((j+1)%4)) divbuffer += '<br />&nbsp;';
			}
			divbuffer += '</ul>';

			// instances in related resources
			divbuffer += '<ul class="index_item_relateds" style="height:'+instheight+'px;">&nbsp;';
			instarray = topic['onlineres'];
			for (var j=0;j<instarray.length;j++) {
				divbuffer += '<li class="index_item_instance" id="onlineres_'+i+'_'+j+'" tndx="' + i + '" iset="onlineres" indx="' + j + '">';
				divbuffer += '&nbsp;&nbsp;&nbsp;&nbsp;</li>';
			}
			divbuffer += '</ul>';
		
			// an empty div for the topic detail expansion
			divbuffer += '<br clear="all" />'
			divbuffer += '</div>';
			divbuffer += '<div class="index_item_topic_detail" id="'+topicid+'_detail"></div>';
			topic['indexHTML'] = divbuffer;
		} else {
			divbuffer = topic['indexHTML'];
		}
		//jQuery('#indexlist').append(divbuffer);
		bigbuffer += divbuffer;
	}
	jQuery('#indexlist').html(bigbuffer);
	
	//jQuery('.index_item_instance').hover(highlightTargetImage,unhighlightTargetImage);
	jQuery('.index_item_instance').click(showIndexDetail);
	//jQuery('.index_item_desc_icon').hover(highlightTargetImage,unhighlightTargetImage);
	jQuery('.index_item_desc_icon').click(showIndexDetail);
	
	jQuery('.index_item_name').hover(showGoogleToolTip,hideGoogleToolTip);
	jQuery('.index_item_name').click(searchGoogleWithTarget);
	document.getElementById("sortselect").selectedIndex = (typeSort)?1:0;
	document.getElementById("filterselect").selectedIndex = (showAllTopics)?1:0;
	
	initFilter();
}
function highlightTargetImage(e) {
	var obj = e.target;
	var ifn = jQuery(obj).css('background-image').split('_off.');
	var ifa = ifn[0]+'_on.'+ifn[1];
	jQuery(obj).css('background-image',ifa);
}
function unhighlightTargetImage(e) {
	var obj = e.target;
	var ifn = jQuery(obj).css('background-image').split('_on.');
	var ifa = ifn[0]+'_off.'+ifn[1];
	jQuery(obj).css('background-image',ifa);
}

function underlineTarget(e) {
	var obj = e.target;
	jQuery(obj).css("text-decoration","underline");
}
function nounderlineTarget(e) {
	var obj = e.target;
	jQuery(obj).css("text-decoration","none");
}

function getAbsolutePosition(element) {
    var r = { x: element.offsetLeft, y: element.offsetTop };
    if (element.offsetParent) {
      var tmp = getAbsolutePosition(element.offsetParent);
      r.x += tmp.x;
      r.y += tmp.y;
    }
    return r;
  };

function showGoogleToolTip(e) {
	var obj = e.target;
	var tt = 'Search for "' + jQuery(obj).html() + '" in Google.';
	var mouseX = e.pageX || (e.clientX ? e.clientX + document.body.scrollLeft : 0);
	var mouseY = e.pageY || (e.clientY ? e.clientY + document.body.scrollTop : 0);
	mouseX += 3;
	mouseY += 6;
	jQuery('#tooltip').html(tt).show().css({left: mouseX + "px", top: mouseY + "px"});
}
function hideGoogleToolTip(e) {
	jQuery('#tooltip').hide();
}
function searchGoogleWithTarget(e) {
	var obj = e.target;
	var lckey = jQuery(obj).html().toLowerCase();
	var gurl = 'http://www.google.com/search?q='+lckey.replace(/ /g,'+');
	var win = open(gurl,'detail');
	win.focus();
}


function showToolTip(e) {
	var obj = e.target;
	var tt = jQuery(obj).attr('label');
	var mouseX = e.pageX || (e.clientX ? e.clientX + document.body.scrollLeft : 0);
	var mouseY = e.pageY || (e.clientY ? e.clientY + document.body.scrollTop : 0);
	mouseX += 3;
	mouseY += 6;
	jQuery('#tooltip').html(tt).show().css({left: mouseX + "px", top: mouseY + "px"});
}
function hideToolTip(e) {
	jQuery('#tooltip').hide();
}
/*
function toolDispatch(e) {
	var obj = e.target;
	var cmd = jQuery(obj).attr('id');  // id = function
	var tndx = parseInt(jQuery('.instance_popup_data#tndx').text(),10);
	var iset = jQuery('.instance_popup_data#iset').text();
	var indx = parseInt(jQuery('.instance_popup_data#indx').text(),10);
	var exid = topics[tndx][iset][indx]['exhibid'];
	switch (cmd) {
		case 'full':
			var url = topics[tndx][iset][indx]['detaillink'];
			// popup window if not current exhibition, otherwise, use relative URL
			if (exid==thisExhibitID) {
				self.location.href=url;
			} else {
				var win = open(url,'detail');
				win.focus();
			}
			break;
		case 'map':
			var sid = topics[tndx][iset][indx]['sectionid'];
			var url = topics[tndx][iset][indx]['mapurl'];
			url += ((url.indexOf('?')!=-1)?'&':'?')+'exid='+exid+'&sid='+sid;
			// popup window if not current exhibition, otherwise, use relative URL
			if (exid==thisExhibitID) {
				self.location.href=url;
			} else {
				var win =  open(url,'map','width=570,height=640,menubar=no,scrollbars=no,toolbar=no,status=no,location=no,directories=no,hotkeys=no,resizable=no');
				win.focus();
			}
			break;
		default:
			alert('gonna get around to doing '+cmd+' someday!');
			break;
	}
}
*/
/* Index page strategy ...
	each index entry line has:  TOPIC   |  instances in this exhibit   |  instances elsewhere in museum   | related resources
	rolling over any of them highlights (and maybe a tool tip?)
	clicking on any of them opens the detail section below the index entry
	clicking on TOPIC gets:  description, if it exists; if not, first instance in this exhibit
	         on instance in this exhibit gets:  instance info (section name, KWIC; maybe pic of exhibit section)
             on instance elsewhere in museum gets: instance info (exhibition name, section name, kwic, maybe pic)
             on related resource gets: instance info (resource name, age level?, kwic, maybe pic)
	
*/
var prevHitObj;
function showIndexDetail(e) {
	// link from Topic in index
	// get the class of the target
	var obj = e.target;
	var iset, indx;
	var tndx = topicndxs[jQuery(obj).parents('.index_item').attr('id')];
	if (jQuery(obj).hasClass('index_item_instance')) {
		iset = jQuery(obj).attr('iset');
		indx = parseInt(jQuery(obj).attr('indx'),10);
	}
	showDetail(tndx,iset,indx);
}

function showPageDetail(e) {
	hidePageTip();
	// link from Topic on an internal content page
	var obj = e.target;
	var topicid = jQuery(obj).attr('topicid'); 
	var indx = parseInt(jQuery(obj).attr('indx'),10);
	openObject = null;   //  we're rebuilding page, nothing should be open...
	jQuery.get('exindex/svars_index.php',{'tx': topicid, 'is': 'inexhibit', 'ix': indx.toString()},browseToIndex);
}

function browseToIndex() {
	browse('exindex',false);   // this will come back to us as indexInit()
}

function showDetail(tndx,iset,indx,testScrollP) {
	// if iset & indx are not null, show the details of that instance
	//   otherwise, show the topic detail
	// if testScrollP, check whether top of item is visible in current window scroll, if not, scroll to make it visible
	
	// 20080427: since we decided to have only one item showing at a time, we can reuse the topic_detail div
	//   (and let us cross fingers that we don't change our minds again)

	// if it's already visible, we're done...
	if (openObject && openObject['tndx']==tndx && openObject['iset']==iset && openObject['indx']==indx) return;
	
	// if there is something else visible, close it...
//	jQuery('#topic_detail_master').slideUp('normal');
	
	var topicid = topics[tndx]['topicid'];
	var ie = jQuery("#"+topicid);
	var iedd = jQuery('#'+topicid+'_detail');


/*  removed 20080428; false (or at least complicated) economy? 
	restored 20080508:  not false economy at all, though still complicated...
	 	but now we want to detach it from where it was and reattach it here... */
		var ddiv = jQuery(iedd).find('.topic_detail');
		if (ddiv.length==0) {   
			jQuery(iedd).html(topicDetailTemplate);
			jQuery(iedd).find('.topic_detail').removeAttr('id').show();
			jQuery(iedd).find('.topic_detail_close_button').hover(highlightTargetImage,unhighlightTargetImage);
			jQuery(iedd).find('.topic_detail_close_button').click(hideDetail);
		}
	
	//  apply our "selected" style to it, which we hopehas all kinds of nice outcomes
	jQuery(ie).addClass('index_item_selected');
	
	// and highlight the item's target, if it is an instance 
	if (prevHitObj) {
		jQuery(prevHitObj).removeClass('hit');
		jQuery(prevHitObj).click(showIndexDetail);
		// jQuery(prevHitObj).hover(highlightTargetImage,unhighlightTargetImage);
	}
	prevHitObj = null;
	if (iset) {
		var obj = jQuery('#'+iset+'_'+tndx+'_'+indx);
	} else {
		var obj = jQuery(ie).find('.index_item_desc_icon');
	}
	var nullfn = function() {};
	jQuery(obj).unbind('click');
	//jQuery(obj).unbind('onMouseOver');
	//jQuery(obj).unbind('onMouseOut');
	jQuery(obj).addClass('hit');
	prevHitObj = obj;
	
	// set data & links
	jQuery(iedd).find('.topic_detail_link').click(resourceDispatch);
	jQuery(iedd).find('.topic_detail_data#tndx').html(tndx.toString());
	jQuery(iedd).find('.topic_detail_data#iset').html(iset);
	jQuery(iedd).find('.topic_detail_data#indx').html((indx!=null)?indx.toString():'');	

	// if there is a different object open, close it.
	if (openObject && (openObject['tndx']!=tndx)) hideDetail();   // there are effects that might not be done yet, but...

	if (iset!=null) {
		showInstanceDetail(tndx,iset,indx);
	} else {
		showTopicDetail(tndx);
	}

	jQuery(visobj).fadeIn(200);   // visobj is assigned a value in showXXXDetail
	if (!openObject) jQuery(iedd).slideDown('normal');
	openObject = new Object;
	openObject['tndx'] = tndx;
	openObject['iset'] = iset;
	openObject['indx'] = indx;
	
	if (testScrollP==true) {
		makeElementVisible(document.getElementById(topicid));
	}
}

function hideDetail() {
	if (openObject) {
		var tndx = openObject['tndx'];
		var topicid = topics[tndx]['topicid'];
		var iedd = jQuery('#'+topicid+'_detail');
		jQuery(iedd).slideUp('normal',function() {jQuery(iedd).html('');});
		//  if (visobj) jQuery(visobj).fadeOut(100);
		jQuery('#'+topicid).removeClass('index_item_selected');
		visobj = null;
		openObject = null;
		if (prevHitObj) {
			jQuery(prevHitObj).removeClass('hit');
			jQuery(prevHitObj).click(showIndexDetail);
			prevHitObj = null;
		}
	}
}
var visobj;
var openObject;

function showTopicDetail(tndx) {
	var topicid = topics[tndx]['topicid'];
	var elpath = '#'+topicid+'_detail';
	var topicthumbnailpath = "exindex/data/images/";
	
	// fill out the "topic detail" div
	jQuery(elpath).find('.topic_detail_description_body').html(topicdb[topicid]['definition']);
	jQuery(elpath).find('.topic_detail_description_body').find('p').css({'lineHeight': '14px', 'padding-right': '20px'});
	if (topicdb[topicid]['thumbnail_image']) {
		jQuery(elpath).find('.topic_detail_thumbnail').html('<img src="'+topicthumbnailpath+topicdb[topicid]['thumbnail_image']+'" width="150" >');}
	// the wiki and google links
	/*  Museum will not abide in links to wiki;  google map link needs more work;  do google link on name click instead
	var lctopic = topics[tndx]['print_name'].toLowerCase();
	var divbuffer = '<div>';
	//no wiki links from AMNH...
	divbuffer += '<a  class="topic_detail_external_link" href="http://en.wikipedia.org/wiki/'+lctopic.replace(/ /g,'_')+'" target="detail">';
	divbuffer += 'Look up "'+topics[tndx]['print_name']+'" in Wikipedia...</a></div>';

	divbuffer += '<div class="topic_detail_external_link">';
	divbuffer += '<a class="topic_detail_external_link" href="http://www.google.com/search?q='+lctopic.replace(/ /g,'+')+'" target="detail">';
	divbuffer += 'Search for "'+topics[tndx]['print_name']+'" in Google...</a></div>';
	// if type==place...google map link?
	//  the code below works, but the search term isn't precise enough to give consistently good results
	if (topics[tndx]['type']=='place') {
		divbuffer += '<div class="topic_detail_external_link">';
		divbuffer += '<a class="topic_detail_external_link" href="http://maps.google.com/maps?q='+lctopic.replace(/ /g,'+')+'" target="detail">';
		divbuffer += 'Locate "'+topics[tndx]['print_name']+'" on a Google Map...</a></div>';
	}

	jQuery(elpath).find('.topic_detail_external_links').html(divbuffer);
	*/
		
	if (visobj) jQuery(visobj).fadeOut(100);
	visobj = jQuery(elpath).find('.topic_detail_description');
}

function showInstanceDetail(tndx,iset,indx) {

		// get the item's topic, array, and ndx
		var topic = topics[tndx];
		var topicid = topic['topicid'];
		var elpath = '#'+topicid+'_detail';
		var inst = topics[tndx][iset][indx];

		var title, ridref, newvisobj;
		switch (inst['type']) {
			case 'amnh_exhib':
				jQuery(elpath).find('.topic_detail_name').html("<em>"+inst['exhibname']+"</em>");
				jQuery(elpath).find('.topic_detail_section').html(inst['sectionname']);
				jQuery(elpath).find('.topic_detail_description_text').html(inst['kwic']);
				newvisobj = jQuery(elpath).find('.topic_detail_onexhibit');
				break;

			case 'amnh_rfl':
			case 'other_rfl':
				jQuery(elpath).find('.topic_detail_name').html("<em>"+inst['resname']+"</em>");
				jQuery(elpath).find('.topic_detail_description_text').html(inst['kwic']);
				newvisobj = jQuery(elpath).find('.topic_detail_online');
			default:
				break;
		}
		if (newvisobj != visobj) jQuery(visobj).fadeOut(100);
		visobj = newvisobj;
		
	}

function resourceDispatch(e){
	var obj = e.target;
	var cmd = jQuery(obj).attr('id');
	var td = jQuery(obj).parents('.topic_detail_background');
	var tndx = parseInt(jQuery(td).find('.topic_detail_data#tndx').text(),10);
	var iset = jQuery(td).find('.topic_detail_data#iset').text();
	var indx = parseInt(jQuery(td).find('.topic_detail_data#indx').text(),10);
	var inst = topics[tndx][iset][indx];
	var exid = inst['exhibid'];
	var url;
	
	switch (cmd) {
		case 'map':
			var sid = inst['sectionid'];
			url = inst['mapurl'];
			url += ((url.indexOf('?')!=-1)?'&':'?')+'exid='+exid+'&sid='+sid;
			var winargs =  'width=570,height=640,menubar=no,scrollbars=no,toolbar=no,status=no,location=no,directories=no,hotkeys=no,resizable=no';
			
			var winname = 'map';
			break;
		case 'full':
			var url = inst['detaillink'];
			var winargs = '';
			var winname = 'detail';
		default:
				break;
	}
	if (url) {
		// popup window if not current exhibition, otherwise, use relative URL
		if (exid==thisExhibitID) {
			// set svar for text to select on that page
			jQuery.get('exindex/svars_index.php',{stext: topics[tndx]['sort_name']});
			// self.location.href=url;
			// SG|20080815: be a good citizen in this content framework, use browse instead of loding page...
			var brAddr = deriveBrowseAddress(url);
			browse(brAddr['section'],brAddr['page'],brAddr);
		} else {
			var win = open(url,winname,winargs);
			win.focus();
		}
	}
}

function deriveBrowseAddress(srch) {
	// srch is search part of address; return assoc array of args and values
	var barray = new Object;
	var arglist = srch.split('?')[1];
	var argarray = arglist.split('&');
	for (var ndx=0;ndx<argarray.length;ndx++) {
		var arg = argarray[ndx].split('=');
		barray[arg[0]] = arg[1];
	}
	return barray;
}

function restoreIndexParams(svobj) {
	// svobj may have persisent values in it for type sort and relevance
	if (svobj['st']!=null) {
		typeSort = svobj['st'];
		if (typeSort) {
			sortProfile=["type","sort_name"];
		} else {
			sortProfile=["sort_name"];
		}
	}
	if (svobj['ft']!=null) {
		showAllTopics = svobj['ft'];
	}
}
function resortList() {
	typeSort = (document.getElementById("sortselect").selectedIndex==1);
	var selt,sels,seli;
	if (openObject) {
		selt = topics[openObject['tndx']]['topicid'];
		sels = openObject['iset'];
		seli = openObject['indx'];
		hideDetail();
	}
	toggleSort();
	buildHTML();
	if (selt) showDetail(topicndxs[selt],sels,seli,false);
	// remember sort
	jQuery.get('exindex/svars_index.php',{st: typeSort});
}
function toggleSort() {
	if (!typeSort) {
		sortProfile=["sort_name"];
	} else {
		sortProfile=["type","sort_name"];
	}
}

function initFilter() {
	filterIndex();
}

var showAllTopics = false;
function resetFilter() {
	var val = (document.getElementById("filterselect").selectedIndex==1);
	if (showAllTopics !=val) {
		showAllTopics = val;
		// remember filter
		jQuery.get('exindex/svars_index.php',{ft: showAllTopics});
		filterIndex();
	}
}
function filterIndex() {
	// 20080508:  so, we're trying a two-state display
	//   either show just the topics that have more than one instance in the exhibition, or show everything

	jQuery(".index_item").show();

	var zebP = true;
	var displayTypeP = new Array();  // keep track of whether any items of each type are displayed
	for (var i=0;i<topics.length;i++) {
		var topic = topics[i];
		var show_topic = false;
		var instarray = topic['inexhibit'];
		if (!displayTypeP[topic['type']]) displayTypeP[topic['type']] = false;  // initialize if not already set
		/* Here is where we implement the rather arbitrary rule for "key topic" vs "everything"
			a "key topic" has more than one instance in current exhibit, 
			or one or more instance in another exhibit, or one or more instance of an online resource.
		*/
		show_topic = showAllTopics || (instarray.length>1) || (topic['otherexhibit'].length>0) || (topic['onlineres'].length>0);

		if (show_topic) {
			var zebc = ' zeb'+zebP.toString();
			zebP = !zebP;
			// is this topic open?  if so, keep its highlight class
			var selc = '';
			if (openObject && (i==openObject['tndx'])) selc = " index_item_selected"
			jQuery('#'+topic['topicid']).removeClass().addClass("index_item "+zebc+selc).show();
			displayTypeP[topic['type']] = true;
		} else {
			// is this topic open? if so, it's disappearing, so close its detail
			if (openObject && (i==openObject['tndx'])) hideDetail();
			jQuery('#'+topic['topicid']).hide();
		}
	}
	// if we're sorted by type, and showing type headers, just show the ones that have items
	for (var t in displayTypeP) {
		jQuery('#'+t).css("display",displayTypeP[t]?"block":"none");
	}
}

var highlightCountdown = 0;
function highlightPageTopics(s,p) {
	if (topics==null) buildTopicList();
	// s is section, p is page
	var pikey = "?section="+s;
	if (p && (p!=s)) pikey += "&page="+p;
	if (!pageInstances[pikey]) return;
	var pageTopics = pageInstances[pikey]['topics'];
	var sid = pageInstances[pikey]['sid'];
	jQuery('img.header').after('<div class="floor_plan_link" sid="'+sid+'" label="Locate this exhibition section on floor plan">&gt;&gt;floor plan</div>');	
	if (pageTopics) {
		for (topicid in pageTopics) {   // why does this also iterate through function bindings?
			if (!isNaN(topicndxs[topicid])) {  /// wow, is this awkward...
				var topic = topics[topicndxs[topicid]]['sort_name'];
				highlightCountdown = 0;
				jQuery('p').each(function () { highlightPageTopic(this, topic.toUpperCase(), topicid, pageTopics[topicid]['indx']); });
			}
		}
		jQuery('div.floor_plan_link').after('<div id="topic_tooltip" style="font-family:Verdana,Arial,sans-serif; font-size:10px; color:#000; background-color:#ffc; border:1px solid black; width:auto; height:auto; display:none; position:absolute; z-index:1001; padding:2px; text-align:left;"></div>');
		jQuery('.topic_highlight').hover(showTopicTip,hidePageTip).click(showPageDetail);
	}
	// is there a selection to highlight?
	jQuery.getJSON('exindex/svars_index.php', finishHighlightPage);
}

function highlightPageTopic(node,topic,topicid,indx) {
	var pos, skip, spannode, middlebit, endbit, middleclone;
	skip = false;
	if (node.nodeType == 3) {
		pos = node.data.toUpperCase().indexOf(topic);  // This  finds "ice" in "Twice"
		//var rstr = "\b"+topic+"\b";      I don't know why this doesn't work...
		//var rex = new RegExp(rstr,'i');
		//pos = node.data.search(rex);
		if (pos >= 0) {
			if (highlightCountdown==0) {
				spannode = document.createElement('span');
				spannode.className = 'topic_highlight';
				spannode.setAttribute('topicid',topicid);
				spannode.setAttribute('indx',indx);
				middlebit = node.splitText(pos);
				endbit = middlebit.splitText(topic.length);
				middleclone = middlebit.cloneNode(true);
				spannode.appendChild(middleclone);
				middlebit.parentNode.replaceChild(spannode, middlebit);
				skip = true;
				highlightCountdown = 3;  // highlight every 5th? paragraph occurance on a page
			}  else --highlightCountdown;
		}
	}
	else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
		for (var i = 0; i < node.childNodes.length; ++i) {
			if (highlightPageTopic(node.childNodes[i], topic, topicid, indx)) return;   // stop after we've found one in this node
		}
	}
	return skip;
}
function finishHighlightPage(svs) {
	if (svs['stext']!=null) {
		jQuery('p').each(function () { highlightTextSelection(this, svs['stext'].toUpperCase()); });
		jQuery.get('exindex/svars_index.php',{'stext': null});
		// did we find any?
		if (!firstSelection) makeElementVisible(document.getElementById('first_text_selection'));
	}
	jQuery('.floor_plan_link').hover(showMapTip,hidePageTip).click(showPageLocation);
}
var firstSelection = true;
function highlightTextSelection(node,seltext) {
	var pos, skip, spannode, middlebit, endbit, middleclone;
	skip = false;
	if (node.nodeType == 3) {
		pos = node.data.toUpperCase().indexOf(seltext);   //  This finds"ice" in "Twice"
		//var rstr = "\b"+seltext+"\b";    I don't know why this doesn't work
		//var rex = new RegExp(rstr,'i');
		//pos = node.data.search(rex);
		if (pos >= 0) {
			spannode = document.createElement('span');
			spannode.className = 'topic_text_selection';
			if (firstSelection) { spannode.setAttribute('id','first_text_selection'); firstSelection = false;}
			middlebit = node.splitText(pos);
			endbit = middlebit.splitText(seltext.length);
			middleclone = middlebit.cloneNode(true);
			spannode.appendChild(middleclone);
			middlebit.parentNode.replaceChild(spannode, middlebit);
			skip = true;
		}
	}
	else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
		for (var i = 0; i < node.childNodes.length; ++i) {
			if (highlightTextSelection(node.childNodes[i], seltext)) return;   // stop after we've found one in this node
		}
	}
	return skip;
}

function showPageLocation(e) {
	var obj = e.target;
	var sid = jQuery(obj).attr('sid');
	browse('exmap','',{'sid':sid});
	//self.location.href=exresdb[thisExhibitID]['basemap']+'&sid='+sid;	
}
function showTopicTip(e) {
	var obj = e.target;
	var tt = "More about "+ jQuery(obj).text();
	var mouseX = e.pageX || (e.clientX ? e.clientX + document.body.scrollLeft : 0);
	var mouseY = e.pageY || (e.clientY ? e.clientY + document.body.scrollTop : 0);
	showPageTip(tt,mouseX,mouseY);
}
function showMapTip(e){
	var obj = e.target;
	var tt = jQuery(obj).attr('label');
	var mouseX = e.pageX || (e.clientX ? e.clientX + document.body.scrollLeft : 0);
	var mouseY = e.pageY || (e.clientY ? e.clientY + document.body.scrollTop : 0);
	showPageTip(tt,mouseX,mouseY);
}
function showPageTip(tt,x,y) {
	x += 3;
	y += 6;
	jQuery('#topic_tooltip').html(tt).show().css({left: x + "px", top: y + "px"});
	
}
function hidePageTip(e) {
	jQuery('#topic_tooltip').hide();
}

function indexinit() {
	if (topics==null) buildTopicList();
	jQuery.getJSON('exindex/svars_index.php', finishindexinit);
}
function finishindexinit(svs) {
	restoreIndexParams(svs);
	buildHTML();

	jQuery('.instance_popup_tool').hover(showToolTip,hideToolTip);
	if (svs['tx']!=null) {
		showDetail(topicndxs[svs['tx']],svs['is'],svs['ix'],true);
		jQuery.get('exindex/svars_index.php',{'tx': null, 'is': null, 'ix': null});	
		}
}

/* map functions */

		var floorURLarg = 1;
		var exidURLarg = 'water';
		var sidURLarg = '';
		var excasedb;

		function getargs() {
			var tmp = self.location.search.split('?')[1];
			if (tmp) {
				var args = tmp.split('&');
				for (var i=0;i<args.length;i++) {
					var arg = args[i].split('=');
					switch (arg[0]) {
						case 'f': floorURLarg = arg[1]; break;
						case 'exid': exidURLarg = arg[1]; break;
						case 'sid': sidURLarg = arg[1]; break;
						default: break;
					}
				}
			}
		}


		function mapLocation(sid) {
			// the origin and width and height of the mappable area we will scale to
			var ox = 15; 
			var oy = 6;
			var mh = 790;
			var mw = 310;  
			var x = excasedb[sid]['mx'];
			var y = excasedb[sid]['my'];
			var mx = Math.round(ox + (x*mw));
			var my = Math.round(oy + (y*mh));
			return {"x": mx, "y": my};
		}

		function showExhibitCases() {
			var ep = jQuery('.exhibition_popup').clone();
			for (var sid in excasedb) {
				var excase = excasedb[sid];
				var rex = new RegExp('\\.','g');
				var safesid = sid.replace(rex,'dot');
				var sec = sid.split('.')[0];
				var divbuffer = '<div class="locator-dot" id="sid-'+safesid+'" label="'+excase['sname']+'" sec="s'+sec+'" sid="'+sid+'">&nbsp;</div>';
				jQuery('#exhibition_map').append(divbuffer);
				//divbuffer = '<div class="section_info_label" sid="'+sid+'">'+excase['sname']+'</div>';
				// add a section detail div to the div we just appended
				ep = jQuery(ep).clone().attr('sid',sid);
				jQuery('#s'+sec+'_i').append(ep);
			}
		}

		function locateExhibitCases() {
			for (var sid in excasedb) {
				var coord = mapLocation(sid);
				var xc = coord['x'] + 'px';
				var yc = coord['y'] + 'px';
				var rex = new RegExp('\\.','g');
				var safesid = sid.replace(rex,'dot');
				jQuery('#sid-'+safesid).css("left", xc);
				jQuery('#sid-'+safesid).css("top", yc);
			}
			jQuery(".locator-dot").hover(highlightSectionLabel,unhighlightSectionLabel);

			jQuery(".locator-dot").click(showSectionDetailsEvent);
			jQuery(".locator-dot").hide();
		}

/*   for map data development... 
var dint = false;
var lobj;
var mx = 335; //parseInt(jQuery("#exhibition_floor_plan").css("left"),10);
var my = 310; // parseInt(jQuery("#exhibition_floor_plan").css("top"),10);
function seepos(e){
	var px = e.pageX - mx;
	var py = e.pageY - my;
	var xc = px + 'px';
	var yc = py + 'px';
	jQuery(lobj).css("left", xc);
	jQuery(lobj).css("top", yc);
	jQuery('#coords').html('Page: ('+px+','+py+')');
}
function toggleDragoo(e) {
	if (!dint) {
		dint = true;
		lobj = e.target;
		document.captureEvents(Event.MOUSEMOVE);
		document.onmousemove = seepos;
		jQuery('#coords').html('');
	} else {
		document.onmousemove = null;
		var px = parseInt(jQuery(lobj).css("left"),10)-15;
		var py = parseInt(jQuery(lobj).css("top"),10)-6;
		var nx = px/310;
		var ny = py/790;
		jQuery('#coords').html('Page: ('+px+','+py+')<br />Norm: ('+nx+','+ny+')');
		dint = false;
	}
}
*/

		function highlightSectionLabel(e) {
			// map dot rollover...show tooltip & underline corresponding name
			var obj = e.target;
			var tt = jQuery(obj).attr('label');
			var sid = jQuery(obj).attr('sid');
			var mouseX = e.pageX || (e.clientX ? e.clientX + document.body.scrollLeft : 0);
			var mouseY = e.pageY || (e.clientY ? e.clientY + document.body.scrollTop : 0);
			mouseX += 6;
			mouseY += 6;
			jQuery('#tooltip').html(tt).show().css({left: mouseX + "px", top: mouseY + "px"});
		}
		function unhighlightSectionLabel(e) {
			jQuery('#tooltip').hide();
			var obj = e.target;
			var sid = jQuery(obj).attr('sid');
		}

		var prevSectionDetail;
		function showSectionDetailsEvent(e) {

			// a click on a map dot or a name line...
			var obj = e.target;
			var sid = jQuery(obj).attr('sid');
			var sec = jQuery(obj).attr('sec');
			showSectionDetails(sec,sid);
		}

		function showSectionDetails(sec,sid) {
			var ep = jQuery('.exhibition_popup[sid="'+sid+'"]');
			var sectiondata = excasedb[sid];
			jQuery(ep).find('.exhibition_popup_thumbnail').html('<img src="exindex/images/water/'+sectiondata['thumbnail']+'" width="210" />');
			jQuery(ep).find('.exhibition_popup_description').html(sectiondata['description']);
			jQuery(ep).find('.exhibition_popup_title').html(sectiondata['sname']);
			jQuery(ep).find('.exhibition_popup_link').attr('sid',sid).click(visitWebPage);
			jQuery(ep).find('.exhibition_popup_data#exhibid').html('water');
			jQuery(ep).find('.exhibition_popup_data#sid').html(sid);
			if (!prevSectionDetail) {
				jQuery(ep).slideDown('normal');
			} else {	// if there was a previous highlight...
				jQuery('.locator-dot[sid='+prevSectionDetail+']').css({backgroundImage: "url(exindex/images/map_circle_on.gif)"});
				jQuery('.exhibition_popup[sid="'+prevSectionDetail+'"]').hide();
				jQuery(ep).show();
			}
			prevSectionDetail = sid;
			jQuery('.locator-dot[sid='+prevSectionDetail+']').css({backgroundImage: "url(exindex/images/link_circle_selected_hit.gif)"});
		}

		function hideSectionDetails() {
			if (prevSectionDetail) {
				// reset the map dot to map_circle_off
				// slide up the exhibition_popup; 
				jQuery('.locator-dot[sid='+prevSectionDetail+']').css({backgroundImage: "url(exindex/images/map_circle_on.gif)"});
				jQuery('.exhibition_popup[sid="'+prevSectionDetail+'"]').hide();
				prevSectionDetail = null;
			}
		}

		function highlightMapDot(e) {
			// section name rollover....underline name and highlight dot on map
			var obj = e.target;
			var sid = jQuery(obj).attr('sid');
			jQuery(obj).css('text-decoration','underline');
			var dotobj = jQuery('.locator-dot[sid="'+sid+'"]');
			highlightDot(dotobj,true);
		}
		function unhighlightMapDot(e) {
			var obj = e.target;
			var sid = jQuery(obj).attr('sid');
			jQuery(obj).css('text-decoration','none');
			var dotobj = jQuery('.locator-dot[sid="'+sid+'"]');
			highlightDot(dotobj,false);
		}

		var highlightObj;
		function highlightDot(obj,onP) {
			if (onP) {
				if (highlightObj) {highlightDot(highlightObj,false);}
				jQuery(obj).css({backgroundImage: "url(exindex/images/map_circle_on.gif)"} );
				highlightObj = obj;
			} else {
				jQuery(obj).css({backgroundImage: "url(exindex/images/map_circle_off.gif)"} );
				highlightObj = null;
			}
		}

		function visitWebPage(e) {
			var obj = e.target;
			var sectionID = jQuery(obj).attr('sid');
			var url = excasedb[sectionID]['rel_link'];
			//self.location.href = url;
			// SG|20080815: be a good citizen in this content framework, ie call browse rather than load a page
			var brAddr = deriveBrowseAddress(url);
			browse(brAddr['section'],brAddr['page'],brAddr);
		}

		function mapToolDispatch(e) {
			var obj = e.target;
			var cmd = jQuery(obj).attr('id');  // id = function
			var exid = jQuery('.exhibition_popup_data#exhibid').text();
			var sid = jQuery('.exhibition_popup_data#sid').text();
			switch (cmd) {
				default:
					//alert('gonna get around to doing '+cmd+' someday!');
					break;
			}
		}

	function mapinit(xargs) {
		if (xargs&&xargs['sid']) sidURLarg = xargs['sid'];
		excasedb = exresdb[exidURLarg]['sections'];
		showExhibitCases();
		locateExhibitCases();
		jQuery('.section_details').hide();
		jQuery('.section_header').click(showSectionInfoEvent);

		if (sidURLarg) {
			var sec = 's'+sidURLarg.split('.')[0];
			showSectionInfo(sec);
			showSectionDetails(sec,sidURLarg);
			makeElementVisible(document.getElementById(sec));
		}

	}

	function highlightSection(sec) {
		// sec is sn, n is the section number
		jQuery('#'+sec+'_h').css('text-decoration','underline');
	}
	function unhighlightSection(sec) {
		// sec is sn, n is the section number
		jQuery('#'+sec+'_h').css('text-decoration','none');
	}
	var prevSection;
	function showSectionInfoEvent(e) {
		var obj=e.target;
		var sec = jQuery(obj).parent().attr('id');
		showSectionInfo(sec);
	}
	function showSectionInfo(sec) {
		if (sec==prevSection) return;
		hideSectionInfo();
		jQuery('#'+sec).addClass('selected');
		jQuery('#exhibition_popup_intro').show();
		jQuery('#'+sec+'_d').slideDown('normal');
		jQuery('.locator-dot[sec="'+sec+'"]').fadeIn('normal');
		prevSection = sec;
	}
	function hideSectionInfo(e) {
		if (prevSection!=null) {
			hideSectionDetails();
			jQuery('#'+prevSection+'_d').slideUp('normal');
			jQuery('#'+prevSection).removeClass('selected');
			jQuery('.locator-dot[sec="'+prevSection+'"]').fadeOut('normal');
			prevSection = null;
		}
	}

/* general window utilities */
function getScrollXY() {
	var scX = 0;
	var scY = 0;
	if( typeof( window.pageYOffset ) == 'number' ) { scX = window.pageXOffset; scY = window.pageYOffset; }
	else if( document.body && document.body.scrollTop ) { scX = document.body.scrollLeft; scY = document.body.scrollTop; }
	else if( document.documentElement && document.documentElement.scrollTop )
	{ //IE6 
	   scX = document.documentElement.scrollLeft; scY = document.documentElement.scrollTop;
	}
	 return [scX, scY];
}
function getInnerHeight() {
  var inHi = 0;
  if( typeof( window.innerHeight ) == 'number' ) {
    //Netscape compliant
    inHi = window.innerHeight;
  } else if( document.body && document.body.clientHeight ) {
    //DOM compliant
    inHi = document.body.clientHeight;
  } else if( document.documentElement && document.documentElement.clientHeight ) {
    //IE6 standards compliant mode
    inHi = document.documentElement.clientHeight;
  }
  return inHi;
}
function getOffsetTop(obj)  // Get full top offset
{
	var ot = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			ot += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		ot += obj.y;
	return ot;
}

function makeElementVisible(obj) {
	if (!obj) return;
	// simple blunt instrument...
	var sc = getScrollXY();
	var vtop = sc[1];
	var vbot = vtop + getInnerHeight();
	var otop = getOffsetTop(obj);
	if (otop<vtop || otop>vbot) window.scrollTo(sc[0],otop);
}
