(function () {
	var afsMode = true;
	var afsLoadRequested = false;
	function LocalSearch(opt_options) {

		// inline ads
		this.inlineAds = null;
		if (document.getElementById("_gmls_inline_ads_div_")) {
			if (afsMode && afsLoadRequested == false) {
				// defer load afs, now it can safely access the named div from
				// above...
				afsLoadRequested = true;
				var scriptUrl = "http://www.google.com/afsonline/show_dynamic_afs_ads.js";
				setTimeout(methodClosure(this, loadAfs, [scriptUrl]), 0);
			}
			this.inlineAds = new InlineAds();
			if (this.inlineAds.adDiv == null) {
				this.inlineAds = null;
			}
		}
		this.options = opt_options;
	}
	LocalSearch.RESULT_LIST_INLINE = "inline";
	LocalSearch.RESULT_LIST_SUPPRESS = "suppress";

	// Export as google.maps.LocalSearch()
	if (!window["google"]) {
		window["google"] = {};
	}
	if (!window["google"].maps) {
		window["google"].maps = {};
	}
	window["google"].maps.LocalSearch = LocalSearch;

	LocalSearch.prototype = new GControl(false,true);
	LocalSearch.prototype.initialize = function(map) {

		this.gmap = map;
		this.parseOptions(this.options);

		var container = document.createElement("div");
		cssSetClass(container, css.control_root);
		this.buildControl(container);


		map.getContainer().appendChild(container);
		if (this.inlineAds && this.inlineAds.facts.parent == "map") {
			map.getContainer().appendChild(this.inlineAds.root);
		}
		return container;
	}

	LocalSearch.prototype.getDefaultPosition = function() {
		var x = 5;
		var y = 65;
		if (br_IsIE()) {
			x = 2;
		}
		return new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(y,x));
	}

	LocalSearch.prototype.parseOptions = function(options) {
		this.suppressInitialResultSelection = false;
		this.suppressZoomToBounds = false;
		this.inlineResultList = true;
		this.externalResultListContainer = null;
		this.externalAds = null;
		this.linkTarget = GSearch.LINK_TARGET_BLANK;
		this.addressLookupMode = GlocalSearch.ADDRESS_LOOKUP_ENABLED;
		this.searchFormHint = GSearch.strings["search-the-map"];
		this.onIdleCallback = null;
		this.onSearchCompleteCallback = null;
		this.onCloseFormCallback = null;
		this.onMarkersSetCallback = null;
		this.onGenerateMarkerHtmlCallback = null;
		if (options) {

			// external ads
			if (options.externalAds) {
				this.externalAds = new ExternalAds(options.externalAds);
			}

			// address lookup mode
			if (options.addressLookupMode) {
				this.addressLookupMode = options.addressLookupMode;
			}

			// suppress initial result selection
			if (options.suppressInitialResultSelection) {
				this.suppressInitialResultSelection =
				options.suppressInitialResultSelection;
			}

			// zoom to bounds when results loaded
			if (typeof(options.suppressZoomToBounds) != "undefined") {
				this.suppressZoomToBounds = options.suppressZoomToBounds;
			}

			// result list style
			if (options.resultList) {
				if (options.resultList == LocalSearch.RESULT_LIST_INLINE) {
					this.inlineResultList = true;
				} else if (options.resultList == LocalSearch.RESULT_LIST_SUPPRESS) {
					this.inlineResultList = false;
				} else {
					this.inlineResultList = true;
					this.externalResultListContainer = options.resultList;
				}
			}

			// linkTarget
			if (options.linkTarget) {
				this.linkTarget = options.linkTarget;
			}

			// search form hint string
			if (options.searchFormHint) {
				this.searchFormHint = options.searchFormHint;
			}

			// callbacks
			if (options.onIdleCallback) {
				this.onIdleCallback = options.onIdleCallback;
			}
			if (options.onSearchCompleteCallback) {
				this.onSearchCompleteCallback = options.onSearchCompleteCallback;
			}
			if (options.onCloseFormCallback) {
				this.onCloseFormCallback = options.onCloseFormCallback;
			}
			if (options.onMarkersSetCallback) {
				this.onMarkersSetCallback = options.onMarkersSetCallback;
			}
			if (options.onGenerateMarkerHtmlCallback) {
				this.onGenerateMarkerHtmlCallback = options.onGenerateMarkerHtmlCallback;
			}
		}

		// adjust css.app, .app_active, .app_idle, and .app_no_results to account
		// for cull or compact mode
		var browserAdd = "gmls-std-mode";
		if (br_IsIE()) {
			browserAdd = "gmls-ie-mode";
		}
		var classAddOn = " " + css.app_compact_mode + " " + browserAdd;
		if (this.inlineResultList == true) {
			classAddOn = " " + css.app_full_mode + " " + browserAdd;;
		}
		css.app += classAddOn;
		css.app_active += classAddOn;
		css.app_idle += classAddOn;
		css.app_no_results += classAddOn;
	}

	LocalSearch.prototype.buildControl = function(container, opt_options) {
		this.root = container;

		this.buildContainerGuts(opt_options);

		// result scroller
		this.currentResultIndex = 0;
		this.markers = new Array();
		this.idle = true;

		// bind up the controls
		this.searchForm.input.onclick = methodClosure(this, LocalSearch.prototype.onPreActive, []);
		this.searchForm.input.onfocus = methodClosure(this, LocalSearch.prototype.onPreActive, []);
		this.searchForm.setOnSubmitCallback(this,LocalSearch.prototype.formSubmit);
		if (this.onCloseFormCallback) {
			this.searchForm.setOnClearCallback(this,LocalSearch.prototype.formClear);
		}
		this.next.onclick = methodClosure(this, LocalSearch.prototype.onNext, []);
		this.prev.onclick = methodClosure(this, LocalSearch.prototype.onPrev, []);

		// build the icons
		var baseIcon = new GIcon();
		baseIcon.image = "http://www.google.com/mapfiles/gadget/markerSmall80.png";
		baseIcon.shadow = "http://www.google.com/mapfiles/gadget/shadow50Small80.png";
		baseIcon.iconSize = new GSize(16, 27);
		baseIcon.shadowSize = new GSize(30, 28);
		baseIcon.iconAnchor = new GPoint(8, 27);
		baseIcon.infoWindowAnchor = new GPoint(5, 1);
		this.letteredIcons = new Array();
		for ( var i=0; i < 16; i++ ) {
			var icon = new GIcon(baseIcon);
			var iconImageKey =
			icon.image = "http://www.google.com/mapfiles/gadget/letters/marker" +
			String.fromCharCode(65+i) + ".png";
			this.letteredIcons[i] = icon;
		}

		if (opt_options) {
			if (opt_options.linkTarget) {
				this.linkTarget = opt_options.linkTarget;
			}
		}
		this.bootComplete(null);
	}

	LocalSearch.prototype.buildContainerGuts = function(opt_options) {

		// build out the control including the form and results popup
		removeChildren(this.root);
		this.appContainer = createDiv(null, css.app);

		// create a container for the ads to sit in. this is their
		// permanent home in the dom
		if (this.inlineAds) {
			this.inlineAds.root = createDiv(null, css.ads[this.inlineAds.facts.format]);
			this.inlineAds.root.appendChild(this.inlineAds.adDiv);
		}

		// the search form
		var enableClear = false;
		if ( this.onCloseFormCallback ) {
			enableClear = true;
			css.search_form_active += " gmls-search-form-withclear";
			css.search_form_idle += " gmls-search-form-withclear";
		}
		this.searchFormDiv = createDiv(null, css.search_form_idle);
		this.searchForm = new GSearchForm(enableClear, this.searchFormDiv);

		// todo(markl):
		// this really needs a home, maybe in the search form user cell
		this.attributionDiv = createDiv(null, css.attribution);

		this.buildPrevNextControl();

		/**
* This is the basic structure we are building
*  <resultsPopup>
*    <resultsList>
*      <resultsTable/>
*      <adsBox/>?
*      <resultsControls/>
*      <attributionDiv/>
*    </resultsList>
*  </resultsPopup>
*/
		this.resultsPopup = createDiv(null, css.results_popup);
		this.resultsList = createDiv(null, css.results_list);
		this.resultsTableDiv = createDiv(null, css.results_table);
		this.resultsTable = createTable(css.results_table);
		this.resultsTableDiv.appendChild(this.resultsTable);

		var adsBox = null;
		if (this.inlineAds && this.inlineAds.facts.parent == "results") {
			adsBox = createDiv(null,css.results_ads_box);
			adsBox.appendChild(this.inlineAds.root);
		}
		this.resultControls = createDiv(null, css.results_controls);

		// bind everything up
		this.resultsPopup.appendChild(this.resultsList);
		this.resultsList.appendChild(this.resultsTableDiv);
		if (adsBox) {
			this.resultsList.appendChild(adsBox);
		}
		this.resultsList.appendChild(this.resultControls);

		this.resultsList.appendChild(this.attributionDiv);

		// IF we have an external ResultListContainer,
		// attach the resultsPopup there, otherwise
		// attach in the appContainer
		if (this.externalResultListContainer) {
			removeChildren(this.externalResultListContainer);
			var appContainer = createDiv(null, css.app);
			appContainer.appendChild(this.resultsPopup);
			this.externalResultListContainer.appendChild(appContainer);
			this.externalResultListContainer = appContainer;
		} else {
			this.appContainer.appendChild(this.resultsPopup);
		}

		this.appContainer.appendChild(this.searchFormDiv);
		this.root.appendChild(this.appContainer);
	}

	LocalSearch.prototype.resetResultsTable = function() {
		this.resultsTableDiv.innerHTML = "";
		this.resultsTable = createTable(css.results_table);
		this.resultsTableDiv.appendChild(this.resultsTable);
	}

	LocalSearch.prototype.buildPrevNextControl = function() {
		// controls
		this.prevNext = createDiv(null, css.prev_next_active);
		this.prev = createDiv(null, css.prev);
		this.next = createDiv(null, css.next);

		this.prev.innerHTML = "&nbsp;";
		this.next.innerHTML = "&nbsp;";
		this.prev.title = GSearch.strings["previous"];
		this.next.title = GSearch.strings["next"];

		var prevNextCenter = createDiv(null, css.prev_next_center);
		prevNextCenter.appendChild(this.prev);
		prevNextCenter.appendChild(this.next);

		this.prevNext.appendChild(prevNextCenter);
	}

	LocalSearch.prototype.bootComplete = function() {
		// create a searcher and bind to the map
		this.gs = new GlocalSearch();
		this.gs.setResultSetSize(GSearch.LARGE_RESULTSET);
		this.gs.setLinkTarget(this.linkTarget);
		this.gs.setCenterPoint(this.gmap);
		this.gs.setAddressLookupMode(this.addressLookupMode);
		if (this.adsenseList) {
			for (var i=0; i<this.adsenseList.length; i++) {
				this.gs.addRelatedSearcher(this.adsenseList[i]);
			}
		}
		this.gs.setSearchCompleteCallback(this, LocalSearch.prototype.searchComplete, [null]);
		GEvent.bind(this.gmap, "click", this, this.onMapClick);
		this.goIdle();
	}

	// clear the old markers off of the map
	LocalSearch.prototype.clearMarkers = function() {
		cssSetClass(this.prevNext, css.prev_next_idle);

		this.gmap.closeInfoWindow();
		for (var i=0; i < this.markers.length; i++) {
			this.gmap.removeOverlay(this.markers[i].marker);
			if ( this.markers[i].resultsListItem &&
					this.markers[i].resultsListItem.onclick ) {
				this.markers[i].resultsListItem.onclick = null;
			}
			this.markers[i].resultsListItem = null;
		}
		this.resetResultsTable();

		// result scroller
		this.currentResultIndex = 0;
		this.markers = new Array();
	}

	// drop the new markers on the map
	LocalSearch.prototype.setMarkers = function() {//________________________________________________________MODIFIER________________
		// result scroller
		this.currentResultIndex = 0;
		this.markers = new Array();
		var bestResultUrl = null;
		this.resetResultsTable();
		var zoomToBounds = new GLatLngBounds();
		var zoomable = true;

		if ( this.gs.results && this.gs.results.length > 0) {
			if(this.gs.results.length == 1){
				MonPoint=new GLatLng(parseFloat(this.gs.results[0].lat), parseFloat(this.gs.results[0].lng));;
				handleClicks(MonPoint);
				map.setCenter(MonPoint, 12);
				return true;
			}else{
				
				
				for (var i = 0; i < this.gs.results.length && i < 16; i++) {
					var result = this.gs.results[i];
					var icon = this.letteredIcons[i];
					var localResult = new LocalResult(this, result, icon, i,
					this.inlineResultList);
					this.markers.push(localResult);
					zoomToBounds.extend(localResult.marker.getPoint());

					// If we have mixed results, don't enable zoom to bounds
					if ( result.addressLookupResult &&
							result.addressLookupResult == "/maps" &&
							this.gs.results.length > 1) {
						zoomable = false;
					}

					// find the first, non-address result and hold on to it for the more-link
					if (bestResultUrl == null && !result.addressLookupResult ) {
						bestResultUrl = result.url;
					}
				}
				if (bestResultUrl == null) {
					bestResultUrl = this.gs.results[0].url;
				}

				if (zoomable && this.suppressZoomToBounds == false) {
					this.gmap.setCenter(zoomToBounds.getCenter());
					var zoomLevel = this.gmap.getBoundsZoomLevel(zoomToBounds);
					// todo(markl): verify this with pamela and brandon...
					// excessive zooming, especially on single point queries
					// seems to be a problem. For instance, a query for
					// nyc returns a valid bounds, but zoom level of 19, which
					// exceeds the resolution of the map.
					if (zoomLevel > 16) {
						zoomLevel = 16;
					}
					this.gmap.setZoom(zoomLevel);
				}

				this.idle = false;
				this.selectMarker(0, this.suppressInitialResultSelection);

				this.addResultsControl(bestResultUrl);

				cssSetClass(this.prevNext, css.prev_next_active);
				this.setAppContainerClass(css.app_active);
				return true;
			}
		} else {
			this.showNoResultsMessage();
			this.setAppContainerClass(css.app_active);
			return false;
		}
	}

	LocalSearch.prototype.addResultsControl = function(bestResultUrl) {
		removeChildren(this.resultControls);

		// NOW, take the URL and nuke from &latlnt.*&near ->&near
		var newUrl = bestResultUrl.replace(/&latlng=.*&near/,"&near");
		var moreDiv = createDiv(null, css.more_results);
		var alink = createLink(newUrl, GSearch.strings["more-results"],
		this.linkTarget, css.more_results);
		moreDiv.appendChild(alink);

		var clearDiv = createDiv(GSearch.strings["clear-results-uc"],
		css.clear_results);
		clearDiv.onclick = methodClosure(this,
		LocalSearch.prototype.goIdle,
		[]);
		// create a table for these to sit within
		var prevNextTable = createTable(css.results_controls);
		var row = createTableRow(prevNextTable);
		var moreTd = createTableCell(row, css.more_results);
		var prevNextTd = createTableCell(row, css.prev_next);
		var clearTd = createTableCell(row, css.clear_results);

		moreTd.appendChild(moreDiv);
		prevNextTd.appendChild(this.prevNext);
		clearTd.appendChild(clearDiv);

		this.resultControls.appendChild(prevNextTable);
	}

	LocalSearch.prototype.showNoResultsMessage = function() {

		var row = createTableRow(this.resultsTable);
		var tdiv = createDiv(GSearch.strings["no-results"], css.result_list_item_warning_text);
		var resultTd = createTableCell(row, null);
		var resultDiv = createDiv(null, css.result_list_item);
		var key = createDiv("!", css.result_list_item_warning_symbol);
		resultDiv.appendChild(key);
		resultDiv.appendChild(tdiv);
		resultTd.appendChild(resultDiv);

		removeChildren(this.resultControls);
		var moreDiv = createDiv("", css.more_results);
		var clearDiv = createDiv(GSearch.strings["close"],
		css.clear_results);
		clearDiv.onclick = methodClosure(this,
		LocalSearch.prototype.goIdle,
		[]);
		// create a table for these to sit within
		var prevNextTable = createTable(css.results_controls);
		var row = createTableRow(prevNextTable);
		var moreTd = createTableCell(row, css.more_results);
		var prevNextTd = createTableCell(row, css.prev_next);
		var clearTd = createTableCell(row, css.clear_results);

		moreTd.appendChild(moreDiv);
		prevNextTd.appendChild(this.prevNext);
		clearTd.appendChild(clearDiv);

		this.resultControls.appendChild(prevNextTable);
	}

	// light up the selected marker
	LocalSearch.prototype.selectMarker = function(index, opt_suppressInfoWindow) {

		// clear info window and reset icon on current marker
		this.gmap.closeInfoWindow();

		// if we have a results list, clear selected
		if (this.markers[this.currentResultIndex].resultsListItem) {
			cssSetClass(this.markers[this.currentResultIndex].resultsListItem,
			css.result_list_item);
		}

		// snap to current
		this.currentResultIndex = index;

		// light up current
		var result = this.markers[this.currentResultIndex];

		if (result.resultsListItem) {
			cssSetClass(result.resultsListItem, css.result_list_item_selected);
		}

		if (opt_suppressInfoWindow == null ||
				opt_suppressInfoWindow == false ) {
			result.marker.openInfoWindow(result.getHtml(), {maxWidth:300});
		}

		// set scroller
		if (index == 0) {
			cssSetClass(this.prev, css.prev_idle);
		} else {
			cssSetClass(this.prev, css.prev_active);
		}

		if (index == this.markers.length - 1) {
			cssSetClass(this.next, css.next_idle);
		} else {
			cssSetClass(this.next, css.next_active);
		}
	}

	// clear current markers and start a new search
	LocalSearch.prototype.formSubmit = function(form) {
		if (form.input.value) {
			this.newSearch(form.input.value);
		}
		return false;
	}

	// clear current markers and start a new search
	LocalSearch.prototype.formClear = function(form) {
		this.onCloseFormCallback();
		this.goIdle()
		return false;
	}

	LocalSearch.prototype.focus = function() {
		this.searchForm.input.focus();
	}

	// clear current markers and start a new search
	LocalSearch.prototype.execute = function(opt_query) {
		// hyperlink friendly...
		this.newSearch(opt_query);
	}

	LocalSearch.prototype.newSearch = function(opt_query) {
		if (opt_query) {
			this.searchForm.input.value  = opt_query;
		}
		if (this.searchForm.input.value) {

			// clear markers, set prev/next
			this.clearMarkers();
			removeChildren(this.attributionDiv);
			this.gs.execute(this.searchForm.input.value);
		}
		return false;
	}

	LocalSearch.prototype.searchComplete = function() {
		var attribution = this.gs.getAttribution();
		if (attribution) {
			this.attributionDiv.appendChild(attribution);
		}

		if (this.inlineAds) {
			this.inlineAds.hide();
		}
		if (this.externalAds) {
			this.externalAds.hide();
		}
		if (this.onSearchCompleteCallback) {
			this.onSearchCompleteCallback(this.gs);
		}
		if (this.setMarkers()) {
			if (this.onMarkersSetCallback) {
				this.onMarkersSetCallback(this.markers);
			}

			// don't show ads for address lookup results
			if (this.gs.results && this.gs.results.length > 0 &&
					this.gs.results[0].addressLookupResult) {
				return;
			}
			if (this.inlineAds) {
				this.inlineAds.fetch(this.searchForm.input.value, this.gs.results);
				this.inlineAds.show();
			}
			if (this.externalAds) {
				this.externalAds.fetch(this.searchForm.input.value, this.gs.results);
				this.externalAds.show();
			}
		}
	}

	// forwards through the search results
	LocalSearch.prototype.onNext = function() {
		if (this.currentResultIndex < this.markers.length - 1) {
			this.selectMarker(this.currentResultIndex+1);
		}
	}

	// backwards through the search results
	LocalSearch.prototype.onPrev = function() {
		if (this.currentResultIndex > 0) {
			this.selectMarker(this.currentResultIndex-1);
		}
	}

	// called onboot complete, and on cancel click
	LocalSearch.prototype.goIdle = function() {
		if (this.inlineAds) {
			this.inlineAds.hide();
		}
		if (this.externalAds) {
			this.externalAds.hide();
		}
		this.searchForm.input.value = this.searchFormHint;
		this.clearMarkers();
		cssSetClass(this.searchFormDiv, css.search_form_idle);
		this.setAppContainerClass(css.app_idle);
		cssSetClass(this.prevNext, css.prev_next_idle);

		this.idle = true;
		this.resetResultsTable();

		if (this.onIdleCallback) {
			this.onIdleCallback();
		}
	}

	// call onfocus/onclick for search input cell
	LocalSearch.prototype.onPreActive = function() {
		if (this.idle) {
			this.searchForm.input.value = "";
			cssSetClass(this.searchFormDiv, css.search_form_active);
		}
	}

	LocalSearch.prototype.onMapClick = function(marker, point) {
		if (marker && marker.__ls__) {
			var localResult = marker.__ls__;
			localResult.onClick();
		}
	}

	LocalSearch.prototype.setAppContainerClass = function(className) {
		cssSetClass(this.appContainer, className);
		if (this.externalResultListContainer) {
			className += " " + css.app_external_results;
			cssSetClass(this.externalResultListContainer, className);
		}
	}

	// A class representing a single Local Search result returned by the
	// Google AJAX Search API.
	function LocalResult(gmls, result, icon, index, buildList) {
		this.gmls = gmls;
		this.result = result;
		this.latLng = new GLatLng(parseFloat(result.lat), parseFloat(result.lng));
		this.index = index;

		this.setMarker(icon);

		if (buildList) {
			var div = createDiv(null, css.result_list_item);
			var row = createTableRow(gmls.resultsTable);
			var tdiv = createDiv(result.title, css.gs_title);
			var resultTd = createTableCell(row, null);

			var resultDiv = createDiv(null, css.result_list_item);

			var key;
			var keyClass = css.result_list_item_key;
			var keyCode = String.fromCharCode(65+index);
			if (true) {
				// go with image based key key codes for now. make it an option later
				keyClass += " " + css.result_list_item_key + "-" + keyCode;
				keyClass += " " + css.result_list_item_key + "-" + "keymode";
				key = createDiv(null, keyClass);
				key.innerHTML = "&nbsp;";
			} else {
				key = createDiv("(" + keyCode + ")", keyClass);
			}
			resultDiv.appendChild(key);

			resultDiv.appendChild(tdiv);

			if (!result.addressLookupResult) {
				if (result.streetAddress && result.streetAddress != "") {
					var str = "&nbsp;-&nbsp;" + result.streetAddress;
					var addrDiv = createDiv(str, css.gs_street);

					resultDiv.appendChild(addrDiv);
				}
			}
			resultTd.appendChild(resultDiv);
			resultDiv.onclick = methodClosure(
			gmls, LocalSearch.prototype.selectMarker, [index]);
			this.resultsListItem = resultDiv;
		}
	}

	LocalResult.prototype.getHtml = function() {
		var result = createDiv(null, css.result_wrapper);
		var node = this.result.html.cloneNode(true);

		if (this.gmls.onGenerateMarkerHtmlCallback) {
			node = this.gmls.onGenerateMarkerHtmlCallback(this.marker, node, this.result);
		}

		result.appendChild(node);
		return result;
	}

	LocalResult.prototype.setMarker = function(icon) {
		this.marker = new GMarker(this.latLng, icon);
		this.marker.__ls__ = this;
		this.gmls.gmap.addOverlay(this.marker);
	}

	LocalResult.prototype.onClick = function() {
		this.gmls.selectMarker(this.index);
	}


	/**
* Various Static DOM Wrappers.
*/
	function methodClosure(object, method, opt_argArray) {
		return function() {
			return method.apply(object, opt_argArray);
		}
	}

	function methodCallback(object, method) {
		return function() {
			return method.apply(object, arguments);
		}
	}

	function createDiv(opt_text, opt_className) {
		var el = document.createElement("div");
		if (opt_text) {
			el.innerHTML = opt_text;
		}
		if (opt_className) { el.className = opt_className; }
		return el;
	}

	function removeChildren(parent) {
		while (parent.firstChild) {
			parent.removeChild(parent.firstChild);
		}
	}

	function cssSetClass(el, className) {
		el.className = className;
	}

	function createTable(opt_className) {
		var el = document.createElement("table");
		if (opt_className) { el.className = opt_className; }
		return el;
	}

	function createTableRow(table) {
		var tr = table.insertRow(-1);
		return tr;
	}

	function createTableCell(tr, opt_className) {
		var td = tr.insertCell(-1);
		if (opt_className) { td.className = opt_className; }
		return td;
	}

	function createLink(href, text, opt_target, opt_className) {
		var el = document.createElement("a");
		el.href = href;
		el.appendChild(document.createTextNode(text));
		if (opt_className) {
			el.className = opt_className;
		}
		if (opt_target) {
			el.target = opt_target;
		}
		return el;
	}

	// from common, user agent detector
	function br_AgentContains_(str) {
		if (str in br_AgentContains_cache_) {
			return br_AgentContains_cache_[str];
		}

		return br_AgentContains_cache_[str] =
		(navigator.userAgent.toLowerCase().indexOf(str) != -1);
	}
	var br_AgentContains_cache_ = {};
	function br_IsIE() {
		return br_AgentContains_('msie');
	}

	function br_IsOpera() {
		return br_AgentContains_('opera');
	}

	FindAndParseScriptArgs = function(){
		//
		// step #1 - find ourselves
		//
		var apiPattern = /gmlocalsearch\.js\?.*/;

		var scripts = document.getElementsByTagName("script");
		if (scripts && scripts.length > 0) {
			for (var i=0; i < scripts.length; i++) {
				var src = scripts[i].src;
				var matches = src.match(apiPattern);
				if (matches) {
					// we found ourselves, WITH Args so crack the args and proceed
					FindAndParseScriptArgs.cgiParams =
					FindAndParseScriptArgs.parseCgiParams(src);
					return;
				}
			}
		}
	}
	FindAndParseScriptArgs.cgiParams = null;

	FindAndParseScriptArgs.parseCgiParams = function(str) {
		var params = {};
		str = str.replace(/#.*$/,"");
		var urlHalves = str.split("?");
		var parts = urlHalves[urlHalves.length - 1].split("&");
		for (var i = 0; i < parts.length; i++) {
			var keyvalue = parts[i].split("=");
			if (keyvalue[0]) {                     // in case parts[i] is empty
				params[keyvalue[0].toLowerCase()] =
				keyvalue.length>1 ? FindAndParseScriptArgs.urlDecode(keyvalue[1]) : "";
			}
		}
		return params;
	}

	FindAndParseScriptArgs.plus_re_ = /\+/g;
	FindAndParseScriptArgs.urlDecode = function(str) {
		return decodeURIComponent(str.replace(FindAndParseScriptArgs.plus_re_, ' '));
	}

	FindAndParseScriptArgs();


	GenerateAds = function(){

		var loadCss = function(css) {
			document.write('<link href="' + css + '" rel="stylesheet" type="text/css"/>');
		}

		if (FindAndParseScriptArgs.cgiParams &&
				FindAndParseScriptArgs.cgiParams["adsense"]) {
			adsenseKeyArg = FindAndParseScriptArgs.cgiParams["adsense"];
		} else {
			return;
		}

		if (adsenseKeyArg) {
			// arg is key[,adstyle[,mode]]
			// ok, now we have the adsense key and optional
			// ad type (inline, links, tower)
			var values=adsenseKeyArg.split(",");
			var adsenseKey = values[0];
			var adStyle = "inline";
			if (values.length > 1) {
				if (values[1] == "inline" ||
						values[1] == "strip" ||
						values[1] == "skyscraper" ||
						values[1] == "wide_skyscraper" ||
						values[1] == "button" ||
						values[1] == "vertical_banner"
						) {
					adStyle = values[1];
				}
			}

			if (values.length > 2) {
				if (values[2] == "afs") {
					afsMode = true;
				} else if (values[2] == "afc") {
					afsMode = false;
				}
			}

			var adsenseChannel = FindAndParseScriptArgs.cgiParams["channel"] || "";

			// having a problem relocating inline on opera
			// so disable until this is fixed
			if (br_IsOpera() && adStyle == "inline") {
				adStyle = "button";
			}

			if (afsMode) {
				if (LocalSearch.adFacts[adStyle].afsIndex == -1) {
					adStyle = "inline";
				}
			}

			var currentStyle = LocalSearch.adFacts[adStyle];

			// record the selected style
			var obj = new Object();
			for (p in LocalSearch.adFacts[adStyle]) {
				obj[p] = LocalSearch.adFacts[adStyle][p];
				obj["adsenseKey"] = adsenseKey;
			}
			LocalSearch.selectedStyle.push(obj);

			// now, write out an adsense unit using the appropriate
			// key and style

			if (afsMode) {
				document.write('<div id="_gmls_inline_ads_div_" class="gmls_inline_ads" style="display:none; position:absolute;"></div>');
				document.write('<script type="text/javascript">');
				document.write('var googleAdIframeTable = [["_gmls_inline_ads_div_",' +
				currentStyle.afsIndex + ']];');
				document.write('var googleAdIE = "UTF8";');
				document.write('var googleAdOE = "UTF8";');
				document.write('var googleAdHL = "' + UDS_CurrentLocale + '";');
				document.write('var googleAdClient = "' + adsenseKey + '";');
				document.write('var googleAdChannel = "' + adsenseChannel + '";');
				if (adsenseKey == "ca-google-ajaxapi") {
					document.write('var googleAdtest = "on";');
				}
				var width = currentStyle.width;
				var height = currentStyle.height;
				var format = currentStyle.format;

				var colors = LocalSearch.colorSchemes[currentStyle.colors];
				var color_border = colors.color_border;
				var color_text = colors.color_text;
				var color_url = colors.color_url;
				var color_line = colors.color_line;
				var color_link = colors.color_link;

				// pull from table indexed by style

				document.write('var googleAdColorDiv = "' + color_border + '";');
				document.write('var googleAdColorText = "' + color_text + '";');
				document.write('var googleAdColorAltText = "' + color_url + '";');
				document.write('var googleAdColorLink = "' + color_link + '";');
				document.write('var googleAdColorALink = "' + color_link + '";');
				document.write('var googleAdColorVLink = "' + color_url + '";');

				document.write('</script>');

				// defer load of afs bits until needed. main point of this is to ensure
				// that when this runs, the dynamicly created ad block is available.
			} else {
				document.write('<div id="_gmls_inline_ads_div_" class="gmls_inline_ads" style="display:none; position:absolute;">');
				document.write('<script type="text/javascript">');
				document.write('google_ad_client = "' + adsenseKey + '";');
				document.write('google_ad_channel = "' + adsenseChannel + '";');
				if (adsenseKey == "ca-google-ajaxapi") {
					document.write('google_adtest = "true";');
				}
				var width = currentStyle.width;
				var height = currentStyle.height;
				var format = currentStyle.format;

				var colors = LocalSearch.colorSchemes[currentStyle.colors];
				var color_border = colors.color_border;
				var color_text = colors.color_text;
				var color_url = colors.color_url;
				var color_line = colors.color_line;
				var color_link = colors.color_link;

				// pull from table indexed by style
				document.write('google_ad_width = ' + width + ';');
				document.write('google_ad_height = ' + height + ';');
				document.write('google_ad_format = "' + format + '";');

				document.write('google_color_border = "' + color_border + '";');
				document.write('google_color_text = "' + color_text + '";');
				document.write('google_color_url = "' + color_url + '";');
				document.write('google_color_line = "' + color_line + '";');
				document.write('google_color_link = "' + color_link + '";');

				document.write('google_dynamic_adsense_id = "_gmls_inline_ads_asid_";');
				document.write('google_dynamic_adsense_no_initial_ads = true;');
				document.write('</script>');
				document.write('<script src="http://pagead2.googlesyndication.com/pagead/show_dynamic_ads.js" type="text/javascript"></script>');
				document.write('</div>');
			}
		}
	}

	function loadAfs(url) {
		var head = document.getElementsByTagName("head")[0];
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.charset = "utf-8";
		script.src = url;

		// add code to notice when this is done and
		// enable gadsense use
		head.appendChild(script);
	}

	LocalSearch.selectedStyle = [];
	LocalSearch.adFacts = {
		"inline" : {
			afsIndex : 12,
			status : "done",
			width : 234,
			height : 60,
			format : "234x60_as",
			colors : "results",
			parent : "results"
		},

		"skyscraper" : {
			afsIndex : 2,
			status : "done",
			width : 120,
			height : 600,
			format : "120x600_as",
			colors : "map",
			parent : "map"
		},

		"wide_skyscraper" : {
			afsIndex : 3,
			status : "done",
			width : 160,
			height : 600,
			format : "160x600_as",
			colors : "map",
			parent : "map"
		},

		"button" : {
			afsIndex : -1,
			status : "done",
			width : 125,
			height : 125,
			format : "125x125_as",
			colors : "map",
			parent : "map"
		},

		"vertical_banner" : {
			afsIndex : -1,
			status : "done",
			width : 120,
			height : 240,
			format : "120x240_as",
			colors : "map",
			parent : "map"
		}
	}

	LocalSearch.colorSchemes = {
		"results" : {
			color_border : "f9f9f9",
			color_text : "878787",
			color_url : "878787",
			color_line : "878787",
			color_link : "7777cc"
		},

		"map" : {
			color_border : "f0f0f0",
			color_text : "878787",
			color_url : "008000",
			color_line : "878787",
			color_link : "7777cc"
		}
	}

	GenerateAds();

	// an inline ads object
	function InlineAds() {
		this.root = null;
		this.adDiv = document.getElementById("_gmls_inline_ads_div_");
		this.facts = LocalSearch.selectedStyle[0];
		this.searcher = null;

		// On IE, we have a little problem... The ad block that
		// we wrote out is not attached in the dom correctly. Its
		// parent is body, not this.adDiv so now we go on a little
		// fishing expedition...
		if (br_IsIE() && !afsMode) {
			var adDiv = this.adDiv;
			this.adDiv = null;
			var asu = window['google_dynamic_adsense_units']['_gmls_inline_ads_asid_'];
			if (asu) {
				// now whip through the object looking for our lost div...
				for (var p in asu) {
					if (asu[p] && asu[p].nodeType && asu[p].nodeType == 1 ) {
						var node = asu[p];
						if (node.parentNode.id != '_gmls_inline_ads_div_') {
							// we have a little problem. The node got
							// attached incorrectly, so now lift it and mode it
							node.parentNode.removeChild(node);
							this.adDiv = adDiv;
							this.adDiv.appendChild(node);
						}
					}
				}
			}
		}

		// detach from the dom. This will be re-attached
		// during super structure build out
		if (this.adDiv != null) {
			this.adDiv.parentNode.removeChild(this.adDiv);
		}
	}

	InlineAds.prototype.show = function() {
		this.root.style.display = "block";
		// only needed once, but we do it here until we decide
		// otherwise
		this.adDiv.style.display = "block";
	}

	InlineAds.prototype.hide = function() {
		this.root.style.display = "none";
	}

	InlineAds.prototype.fetch = function(q, results) {
		var query = q;
		var options = null;
		if (results.length > 1) {

			// if the view port was computed (meaning there is no near city, etc.)
			// in the query, then append on the phrase "near city" in hopes that
			// this will result in better targetting
			if (results[0].viewportmode && results[0].viewportmode == "computed") {
				if (results[0].city) {
					options = {
						google_city : results[0].city
					}
					if (results[0].region) {
						options.google_region = results[0].region;
					}
				}
			}
		}

		if (this.searcher == null) {
			var adId = null;
			if (!afsMode) {
				adId = "_gmls_inline_ads_asid_";
			}
			this.searcher = new GadSenseSearch(adId);
		}
		// todo - pass options when its fully supported
		this.searcher.execute(query);
	}

	function ExternalAds(adBlock) {
		this.adDiv = null;
		//
		// if we have been given a container then
		// we manage the visibility of the ad block. Note,
		// this will cause the ad block to flash
		if (adBlock.container) {
			this.adDiv = adBlock.container;
		}

		// note, no id passed for afs
		var id = null;
		if ( adBlock.adsense_id ) {
			id = adBlock.adsense_id;
		}
		this.searcher = new GadSenseSearch(id);
	}

	ExternalAds.prototype.show = function() {
		if (this.adDiv) {
			this.adDiv.style.display = "block";
		}
	}

	ExternalAds.prototype.hide = function() {
		if (this.adDiv) {
			this.adDiv.style.display = "none";
		}
	}

	ExternalAds.prototype.fetch = function(q, results) {
		var query = q;
		var options = null;
		if (results.length > 1) {

			// if the view port was computed (meaning there is no near city, etc.)
			// in the query, then append on the phrase "near city" in hopes that
			// this will result in better targetting
			if (results[0].viewportmode && results[0].viewportmode == "computed") {
				if (results[0].city) {
					options = {
						google_city : results[0].city,
						google_language : UDS_CurrentLocale
					}
					if (results[0].region) {
						options.google_region = results[0].region;
					}
				}
			}
		}
		// todo - pass options when its fully supported
		this.searcher.execute(query);
	}

	// classes used throughout
	css = {};

	// major states are
	// active: search results are visible
	// idle: search results are not showing, control is idle
	css.control_root = "gmls";
	css.app = "gmls-app";
	css.app_compact_mode = "gmls-app-compact-mode";
	css.app_full_mode = "gmls-app-full-mode";
	css.app_active = "gmls-app gmls-active";
	css.app_no_results = "gmls-app gmls-active gmls-no-results";
	css.app_idle = "gmls-app gmls-idle";
	css.app_external_results = "gmls-external-results";

	// search form contains input box, search button, and branding
	css.search_form_active = "gmls-search-form gmls-search-form-active";
	css.search_form_idle = "gmls-search-form gmls-search-form-idle";
	css.attribution = "gmls-attribution";

	// results
	css.results_popup = "gmls-results-popup";
	css.results_list = "gmls-results-list";
	css.results_table = "gmls-results-table";
	css.results_ads_box = "gmls-results-ads-box";
	css.results_controls = "gmls-results-controls";

	css.result_list_item = "gmls-result-list-item";
	css.result_list_item_selected = "gmls-result-list-item gmls-selected";
	css.result_list_item_key = "gmls-result-list-item-key";
	css.result_wrapper = "gmls-result-wrapper";
	css.gs_title = "gs-title";
	css.gs_street = "gs-street";

	css.result_list_item_warning_symbol = "gmls-result-list-item-warning-symbol";
	css.result_list_item_warning_text = "gmls-result-list-item-warning-text";

	// scroll controls
	css.prev_next = "gmls-prev-next";
	css.prev_next_active = "gmls-prev-next gmls-prev-next-active";
	css.prev_next_idle = "gmls-prev-next gmls-prev-next-idle";
	css.prev_next_center = "gmls-prev-next-center";
	css.prev = "gmls-prev";
	css.prev_active = "gmls-prev gmls-prev-active";
	css.prev_idle = "gmls-prev gmls-prev-idle";
	css.next = "gmls-next";
	css.next_active = "gmls-next gmls-next-active";
	css.next_idle = "gmls-next gmls-next-idle";

	// more/clear
	css.more_results = "gmls-more-results";
	css.clear_results = "gmls-clear-results";


	// ads classes
	css.ads = {
		"234x60_as" : "gmls-ads-box-234x60_as",               // inline
		"468x15_0ads_al" : "gmls-ads-box-468x15_0ads_al",     // strip
		"120x600_as" : "gmls-ads-box-120x600_as",             // skyscraper
		"160x600_as" : "gmls-ads-box-160x600_as",             // wide_skyscraper
		"125x125_as" : "gmls-ads-box-125x125_as",             // button
		"120x240_as" : "gmls-ads-box-120x240_as",             // vertical_banner
		"120x90_0ads_al" : "gmls-ads-box-120x90_0ads_al"      // links_120x90
	}

	// check for callback args
	function AutoLoad() {
		if (FindAndParseScriptArgs.cgiParams &&
				FindAndParseScriptArgs.cgiParams["callback"] &&
				FindAndParseScriptArgs.cgiParams["key"]) {

			var cb = FindAndParseScriptArgs.cgiParams["callback"];
			var validJsPattern = /^[a-zA-Z\._0-9]*$/;
			if (!cb.match(validJsPattern)) {
				throw "invalid callback or context";
			}

			// now we need to look to see if the Search API is Loaded. If so,
			// we are clear to callback. If not, load the css and code. When this
			// completes, then we can call the specified callback
			if (window["GlocalSearch"] == undefined) {
				// search API is missing. Load it...

				var key = FindAndParseScriptArgs.cgiParams["key"];
				var script = document.createElement("script");
				script.type = "text/javascript";
				script.charset = "utf-8";

				var targetUrl = "http://www.google.com/uds/api?file=uds.js&v=1.0&directload=t&key=" + key + "&callback=" + cb;
				script.src = targetUrl;

				// initiate CSS load...
				AutoLoad.LoadCss("http://www.google.com/uds/css/gsearch.css");
				AutoLoad.LoadCss("http://www.google.com/uds/solutions/localsearch/gmlocalsearch.css");

				// finally, attach to trigger the load
				var head = document.getElementsByTagName("head")[0];
				head.appendChild(script);
			} else {
				AutoLoad.LoadCss("http://www.google.com/uds/solutions/localsearch/gmlocalsearch.css");
				cb = cb + "();"
				eval(cb);
			}
		}
	}


	AutoLoad.LoadCss = function(url) {
		var head = document.getElementsByTagName("head")[0];

		// initiate CSS load...
		var styleSheet = document.createElement("link");
		styleSheet.href = url;
		styleSheet.type = "text/css";
		styleSheet.rel = "stylesheet";
		head.appendChild(styleSheet);
	}

	AutoLoad.GetEvent = function(event) {
		return (event) ? event : window.event;
	}
	AutoLoad.GetEventTarget = function(event) {
		return (event.target) ? event.target : event.srcElement;
	}
	AutoLoad();
})();
