(function($) {
		 
	$(document).ready(function(){
	  
		//give the body an extra class to signal CSS that JavaScript is enabled
		//then turn all html popup links into javascript popup links
		$('body')
			.addClass('enhanced')
			.handlePopups()
		;

		//do some special setup only for the static pages
		$('body.static')
			//create a tabbed interface for any
			//content that needs it
			.find('#pageTabs ul#pageTabset')
			.removeClass('piped')
			.tabs({
				fx: { opacity: 'toggle', duration: 250 }
				, cookie: { expires: 30 }
			})
			.end()
			//create the sidebar image slideshow
			.find('.imageSlideshow').cycle({
				fx: 'scrollRight'
				, speed: 750
				, timeout: 5000
				, nowrap: true
			})
			.end()
		;

		//turn an html table into a sortable one
		//while we're at it, remove the details link
		//from each row of the table
		//that last bit should be moved to ruby --TODOBD
		$('#listView table')
			.find('tr .unenhanced').remove()
			.end()
			.tablesorter({
				widgets: ['zebra']
			})
		;

		//on application pages only (search results) turn the entire result
		//set into a tabbed interface for list, content and map views
		var $pageTabs = $('.app #pageTabs')
			.find('ul#pageTabset')
			.find('li a').each(function(i) {
				var $this = $(this);
				switch(i) {
					case 0:
						$this.attr('href','#listView')
					break;
					case 1:
						$this.attr('href','#fullView')
					break;
					case 2:
						$this.attr('href','#mapView')
					break;
				}
			})
			.end()
			.tabs({
				fx: { opacity: 'toggle', duration: 250 }
				, cookie: { expires: 30 }
			})
		;

		//turn individual results into tabs for summary, detail and image views
		$('#resultTabs .result').each(function() {
	
			var $result = $(this);
			var $tabset = $result.find('ul.tabs');
	
			var ID = $result.attr("id");

			//add a click handler to jump over from list view to content view
			//and scroll down to the record in question, briefly changing its color
			$('#listView #list_' + ID + ' .scientific_name')
				.addClass('link')
				.click(function() {
					$pageTabs.tabs("select",1);
					setTimeout(function(){
						$.scrollTo($($tabset),800,{
							onAfter: function() {
								$result.animate(
									{backgroundColor: "#dfd"}
									, 1000
									, function() {
										$result.animate(
											{backgroundColor: "#fff"}
											,500
										);
									}
								);
							}
						});
					}, 500);
				})
			;

			//add a tab for the summary view
			$tabset.prepend('<li><a class="summary" href="#summary_' + ID + '"><span>summary</span></a></li>');

			//create the tabset
			$tabset.tabs({
				fx: { opacity: 'toggle', duration: 200 }
				, cookie: { expires: 1 }
				, cache: true
				, ajaxOptions: {
					success: function() {
						//handle popups loaded via Ajax
						$result.handlePopups();
						//create the toggle state inside of details
						$result.processDetails();
					}
				}
			});

		});
		
		//override the default view (list view) for enhanced users
		//so that they get a single-creen interface (list_and_content view)
		var $searchForm = $('#basicSearch')
			.find('form #viewstate').val('list_and_content')
			.end()
		;
		
		$searchForm.find('div.attributes').dynamicSearch();
		window.onload = function() {
  		$searchForm.find('#institutionsOnly').hideShow({
  			targets: '#specificInstitutions'
  			, condition: function() {
    				return $(this).is(':checked');
  			}
  		});
  	};

		//set up the dynamic pages
		$('body.app').each(function() {

			//send the search form to a lightbox
			//and redirect the "see current search" link to
			//call that lightbox
			var $basicSearch = $('#basicSearchWrapper').hide();
			var $searchPlayback = $('#searchPlayback');
			var $searchPlaybackLink = $searchPlayback.find('a.thickbox');
			var searchPlaybackNewURL = $searchPlaybackLink.attr('href').replace('#','#TB_inline?height=450&width=400&inlineId=');
			$searchPlaybackLink.attr('href',searchPlaybackNewURL);

			tb_setup('a.thickbox, area.thickbox, input.thickbox');
			$searchPlayback.click(function() {
				tb_show(null,searchPlaybackNewURL,null)
			});
		});

					//add some classes to elements for IE6
					//to overcome its CSS shortcomings
		$('dl.piped').handleChildrenForIE('dt','first');
		$('ul.piped').handleChildrenForIE('li','first');
	 
		$('div#export span#export_warning').hide();
		$('div#export a').click(function() { return confirm('Export may take a couple of minutes.\r\nDo you want to continue?'); });

	});
	
	/* BEGIN LOCAL PLUGINS - - - - - - - - - - - - - - - - - - - - -*/
	
	//add a class of "firstChild" or "lastChild" to any element that
	//needs it so CSS :first-child rules can be applied in "IE6
	$.fn.handleChildrenForIE = function(el,pos,returnChildren) {
		var $this = $(this);
		//find first or last children
		var $children = $this.find(el + ':' + pos)
		//if IE6, add the appropriate class
		if ($.browser.msie && $.browser.version < 7) {
			$children.addClass((pos == 'last' ? 'lastChild' : 'firstChild'));
		}
		//return either the original object or the children were
		//targeted by this operation - defaults to original object
		return returnChildren ? $children : $this;
	};
	
	//turn a search form with repeating fields into a dynamic one
	//with a global "add field" link and individual "remove field" links
	//for each field or set of fields
	$.fn.dynamicSearch = function(options) {
		//allow user to override options
		var opts = $.extend({}, $.fn.dynamicSearch.defaults, options);

		//private methods

		//hide and show the "add field" link based on the number of
		//currently visible chunks. this will get called once when
		//we initialize, and again at the end of each click on an add
		//or remove link 
		var toggleLinks = function() {
		
			//turn off the "add" link when all hidden fields
			//are shown. otherwise, show it
			var $visibleChunks = $root.find(opts.chunkSelector + ':visible');

			if ($visibleChunks.length == $chunks.length) {
				$addLink.hide();
			}
			else {
				$addLink.show();
			}

			//make sure if there's only one chunk visible,
			//it doesn't have a "remove" link
			var $visible = $root.find(opts.chunkSelector + ':visible');
			if ($visible.length == 1) {
					$visible.find('a:last').hide();
			} else {
					$visible.find('a:last').show();
			}
		};

		var $root = $(this);
		var $chunks = $root.find(opts.chunkSelector);
		var visibleFields = 0;

		//create the global "add field" link and give it a
		//click handler that finds the first hidden chunk, appends
		//it to the end of the series, and shows it
		var $addLink = $root
			.append(opts.showLink)
				.find('a:last')
				.click(function() {
					var $firstHidden = $root.find(opts.chunkSelector + ':hidden').eq(0);
					$firstHidden.appendTo($root).fadeIn(opts.fxTime, function() {
						$firstHidden.find(opts.inputToCheckForValues).focus();
						toggleLinks();
					});
				return false;
			})
		;

		//iterate through each chunk (which could be a single
		//field, a fieldset or any other repeated DOM node)
		$chunks.each(function(i) {
			var $singleChunk = $(this);
			var max = $chunks.length - 1;
			var keepVisible = false;
			//decide whehter to hide or show this chunk based
			//on whether the relevant field inside it has a value
			if ($singleChunk.find(opts.inputToCheckForValues).val() != '') {
					keepVisible = true;
					visibleFields ++;
			}
			//for each chunk, append a hide link and give it a
			//click handler that will blank out and hide that chunk
			$singleChunk
				.append(opts.hideLink)
				.find('a:last')
				.click(function() {
					 $singleChunk.fadeOut(opts.fxTime, function() {
							 $singleChunk.find(opts.inputToCheckForValues).val("");
						toggleLinks();
					 });
					 return false;
				})
			 ;
			//hide or show the chunk
			if (!keepVisible) {
				$singleChunk.hide();
			}
		});
		//make sure at least one chunk is always shown
		if (visibleFields == 0) {
				 $chunks.eq(0).show();
		}
		//focus the text field in the last visible chunk
		$root
			 .find(opts.chunkSelector + ':visible:last')
			 .find(opts.inputToCheckForValues)
			 .each(function() {
					$(this).focus();
			 }
		);
		//toggle the add and remove links
		toggleLinks();

	};
	$.fn.dynamicSearch.defaults = {
				chunkSelector: '.search_attribute'
				, inputToCheckForValues: 'input.text'
			//IE6 needs an href, otherwise it won't style it as a link
			//therefore make sure to return false on clicks
			, showLink: '<a href="#" title="add another search parameter" class="add">+</a>'
			, hideLink: '<a href="#" title="remove this search parameter" class="remove">-</a>'
			, fxTime: 10
			, linkContainerClass: 'hideShowLinks'
	};
		

	//create a toggle state between two view states
	//on the details tab - needs refactoring and generalization TODOBD
	$.fn.processDetails = function() {
		var $this = $(this);
		var $hidden = $this.find('.novalue, .noschema');
		var appendLink = function(text,handler) {
			$this.find('ul.detailsLinks')
				.find('li.link').remove().end()
				.prepend('<li class="link firstChild">' + text + '</li>')
				.find('li.link')
				.click(handler)
			;
		};
		var showMessage = 'show empty and non-standard fields';
		var hideMessage = 'hide empty and non-standard fields';
		var showAll = function() {
			$hidden.show();
			appendLink(hideMessage,hideAll);
		};
		var hideAll = function() {
			$hidden.hide();
			appendLink(showMessage,showAll);
		};
		hideAll();
	};

	//turn html popup links into javascript popup links
	//where you can control positioning, size, etc.
	$.fn.handlePopups = function(options) {
		//allow user to override options
		var opts = $.extend({}, $.fn.handlePopups.defaults, options);
	
		this.find('a').each(function() {
			var $link = $(this);
			if (!$link.data('convertedToJavaScript')) {
				$link.data('convertedToJavaScript',true)
				.click(function() {
					var $link = $(this);
					var target = $link.attr("target");
					if (target != "") {
						var url = $link.attr('href');
						var win = window.open(url, target, 'width=' + opts.width +
								 ',height=' + opts.height + ',' + opts.windowOptions
						);
						if (win && win.focus) {
							win.focus();
							return false;
						} else {
							win = null;
							return true;
						}
					}
				});
			}
		});
		return this;
	};
	$.fn.handlePopups.defaults = {
		width: 780
		, height: 450
		, windowOptions: 'menubar,resizable,status,location,toolbar,scrollbars'
	};
	

	/*
		a plugin to tie simple hide/show behavior to a condition
	*/
	$.fn.hideShow = function(options) {
		//TODO: exit gracefully is missing condition or targets
		var opts = $.extend({}, $.fn.hideShow.defaults, options);
		var $this = $(this);//the element on which this method is called
		var $targets = $(opts.targets);//one or more elements to hide or show
		//an event handler that executes a hide or show
		//operation based on a boolean function
		var handler = function() {
			$targets[opts.condition.call(this)//opts.condition is a boolean function
				? opts.showFn//opts.showFn is any jQ effects function
				: opts.hideFn]()//so is opts.showFn
			;
		};
		//bind the event handler to the correct event
		$this.bind(opts.triggerEvent, handler);
		//run the handler once for pageload event
		handler.call(this);
		return $this;
	};
	//defaults are simple hide and show onclicks
	$.fn.hideShow.defaults = {
		showFn: 'show'
		, hideFn: 'hide'
		, triggerEvent: 'click'
	};

  /*
  Description: a plugin to populate the element with google map and specified 'pin' locations.
  Arguments  : options.zoom = google map initial zoom (default = 12)
               options.pins = [{title: "Pin-1", latitute: <lat>, longitude: <lng>},{title: "Pin-2", latitude: <lat2>, longitude: <lng2>}]
  Comments   : Map centers on the first pin in specified 'pins' array.
  */
  $.fn.gmap = function(options) {
    var opts = $.extend({}, $.fn.gmap.defaults, options);
    var $center_pin = opts.pins[0];
    var $this = $(this)[0];
    if (GBrowserIsCompatible()) {
      map = new GMap2($this);
      map.setCenter(new GLatLng($center_pin.latitude,$center_pin.longitude),opts.zoom);
      for(i=0; i<opts.pins.length; i++) { 
        pin = opts.pins[i];
        map.addOverlay(addInfoWindowToMarker(new GMarker(new GLatLng(pin.latitude,pin.longitude),{title : pin.title}), pin.title,{}));
      }
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
    }
  };
	//defaults for function: gmap
	$.fn.gmap.defaults = {
    zoom: 12,
		pins: [{title : "Chicago Botanic Garden", latitude: 42.147222, longitude: -87.790556}]
	};

	/* END LOCAL PLUGINS - - - - - - - - - - - - - - - - - - - - -*/

}(jQuery));