if (typeof(Yelp) == 'undefined') var Yelp = new Object();
// Onload handlers that do not need template processing go here
Yelp.extraBizInit = function(){
	if($('rating_graph')){
			// moved Yelp.biz_ratings_graph_init to chart_functions
		Event.observe('rating_graph','click', function(e) {
				var anchor = findEventTarget(e);
				Yelp.Chart.popBizRatings(e);
		});
	}

	Event.observe('bizBookmark','click',function(e){
		Event.stop(e);
		var aLink = findEventTarget(e);
		if(Yelp.loggedin_user){
			Yelp.addBizBookmark(aLink,json_biz.id,null,null);
		}else{
			initBookSignUp(aLink,json_biz.id,json_biz.name.escapeHTML());
		}
	});
	Event.observe('bizShare','click', function(e){
		Event.stop(e);
		var aLink = findEventTarget(e);
		Yelp.send2Friends.open(aLink, json_biz.id, '',Yelp.loggedin_user);
	});

	// If the user came from the login/signup popup, we need to open the popups for them
	if (Yelp.loggedin_user) {
		var hashVars = Yelp.getHashVars(window.location.href);

		if (hashVars['popup'] == 'bookmark') {
			Yelp.addBizBookmark($('bizBookmark'), json_biz.id, null, null);
		} else if (hashVars['popup'] == 'send2phone') {
			initSend2Phone($('bizShare'), false);
		}
	}

	// init show/hide events for archived reviews
	Yelp.archive_snippet.load();
};

Yelp.categoryPickerPopup = Class.create({
	initialize: function(link, biz_id){
		this.biz_id = biz_id;
		this.link = link;
		new Ajax.Request('/pop_fill/catBox', {
			method: 'GET',
			parameters: {biz_id:biz_id, nc: (new Date()).getTime()},
			onComplete: this.displayPicker.bind(this)
		});
		this.callbacks = {};
	},

	displayPicker: function(transport){
		this.coords = findPosition(this.link.parentNode);
		this.catBox = new Element('div', {'class': 'autopop bizDetailsCatBox'}).update(transport.responseText);
		this.catBox.setStyle({top: this.coords[1] + 'px', left: this.coords[0] + 'px'});
		document.body.appendChild(this.catBox);
		this.callbacks.onClickCancel = function(){this.remove();}.bind(this.catBox);
		this.catBox.down('a.cancelLink').observe('click', this.callbacks.onClickCancel);
		this.form = this.catBox.down('FORM');
		this.form.observe('submit', this.formSubmit.bindAsEventListener(this));
		var initCategoryChooser = function(){
			new Yelp.CategoryYelpChooser({
				container: this.catBox.down('.formContent'),
				selected_ids: json_biz.category_yelp_ids,
				parameter_name: 'biz_details_categories',
				max_category_count: 3,
				onBeforeComplete: this.clearLoader.bind(this)
			});
		}.bind(this);
		if( !Yelp.CategoryYelpChooser ) Yelp.addScript(jsPrefix+'category_yelp_chooser.js', 'catchooser', [Yelp, 'CategoryYelpChooser'], initCategoryChooser);
		else initCategoryChooser();
	},
	clearLoader: function(){
		this.form.down('.formContent').innerHTML = '';
	},

	formSubmit: function(evt){
		this.form.select('input[type=submit]').invoke('disable');
		this.form.select('a.cancelLink').invoke('stopObserving', 'click', this.callbacks.onClickCancel);
		var params = this.form.serialize();
		params['nc'] = (new Date()).getTime();
		new Ajax.Request(this.form.action, {
			method: 'POST',
			parameters: params,
			onComplete: this.submitOnComplete.bind(this)
		});
		Event.stop(evt);
	},

	submitOnComplete: function(transport){
		this.catBox.remove();
		Yelp.ajaxStatusMessage(transport.responseJSON.msg, [this.coords[0],this.coords[1]], 2000);
		response = transport.responseJSON;
		if( response.status ) {
			// new categories data
			json_biz.category_yelp_ids = response.category_yelp_ids;
			$('cat_display').innerHTML = response.category_links;
		}
	}
});

Yelp.BizReviewsFetcherQuery = Class.create();
Object.extend(Yelp.BizReviewsFetcherQuery.prototype, {
	initialize: function() {
		// selected review
		this.hrid  = "";
		// featured review
		this.frid  = "";
		this.biz_id = "";
		this.query = "";
	},
	url: function() {
		var p = {};

		// business id.
		if (this.biz_id != "")
			p['biz_id'] = this.biz_id;

		// search query.
		if (this.query != "")
			p['query'] = this.query;

		// select review id.
		if (this.hrid != "")
			p['hrid'] = this.hrid;

		// featured review id, needed so we don't show this review on page 1+N.
		if (this.frid != "")
			p['frid'] = this.frid;

		var encoded_vars = Yelp.encodeQueryComponents(p);
		return '/biz_details/reviews_snippet?' + encoded_vars;
	}
});

// Query to get the selected review for the page.
Yelp.BizReviewsFetcher = Class.create();
Object.extend(Yelp.BizReviewsFetcher.prototype, {
	initialize: function() {
		this.current_query = new Yelp.BizReviewsFetcherQuery();
		this.query_counter = 0;
		this.default_feedback = {"funny":false,"useful":false,"cool":false};
		this.src = null;
	},
	// Parse the fragment variables.
	_parseHashVars : function() {
		var biz_vars = {};
		if(window.location.hash)
			biz_vars = Yelp.getHashVars(window.location.href);

		// Get the selected review id.
		if (biz_vars.hrid)
			this.current_query.hrid = biz_vars.hrid;

		// Get the search query so we can highlight the selected review
		// on the next page (we send this as part of the xhr request).
		if (biz_vars.query)
			this.current_query.query = biz_vars.query;

		if(biz_vars.src)
			this.src = biz_vars.src;
	},

	// this function can create it if it doesn't exist. will add it as the top review inside $('bizReviewsInner')
	_getHighlightedReviewHeader: function(create){
		var header = $('highlightedReviewHeader');
		if(!header && create){
			header = new Element('h3', {id:'highlightedReviewHeader'}).update('Selected Review');
			$('bizReviewsInner').parentNode.insertBefore(header, $('bizReviewsInner'));
		}
		return header;
	},
	// this function can create it if it doesn't exist. will append it to the end of $('bizReviewsInner')
	_getFollowingReviewHeader: function(create){
		var header = $('followingReviewHeader');
		if(!header && create){
			header = new Element('h3', {id:'followingReviewHeader'}).update('From ');
			header.appendChild(new Element('a', {href:'/user_details_reviews_following'}).update('Reviewers You\'re Following'));
			$('bizReviewsInner').appendChild(header);
		}
		return header;
	},
	// this function can create it if it doesn't exist. will append it to the end of $('bizReviewsInner')
	_getAllReviewHeader: function(create){
		var header = $('allReviewHeader');
		if(!header && create){
			header = new Element('h3', {id:'allReviewHeader'}).update('All Reviews');
			$('bizReviewsInner').appendChild(header);
		}
		return header;
	},

	// don't call this with a review that is already in body.
	// removes review with same id as review in document.
	// review is a prototype Element
	_removeDuplicate: function(review){
		var existing_duplicate_review = $(review.id);
		if(existing_duplicate_review){
			existing_duplicate_review.parentNode.removeChild(existing_duplicate_review);
		}
	},

	// move the currently selected review down
	_moveDownSelectedReview: function(){
		if($('highlightedReviewHeader') && Element.visible($('highlightedReviewHeader'))){
			var highlighted_review = $('highlightedReviewHeader').next();
			if(highlighted_review.hasClassName('review')){
				this._addReviewToInner(highlighted_review);
				toggleElement('highlightedReviewHeader', false);
			}
		}
	},

	// dynamically add the given review
	// retain_featured_review is a boolean: if true, the featured review stays on top. (such as flow from a search)
	// review_to_add is a prototype Element
	_addReview: function(review_to_add, retain_featured_review){
		review_to_add.addClassName('highlighted-review');
		var featured_reviews = $$('.featuredReview');
		var featured_review = null;
		if(featured_reviews.length > 0){
			featured_review = featured_reviews[0];
			if(featured_review.id == review_to_add.id)
				Element.addClassName(review_to_add, 'featuredReview');
		}

		if(retain_featured_review){
			// if the review to add is a featured review AND retain_featured_review, then replace featured review
			// with review_to_add, and get rid of highlighted review if any
			if(Element.hasClassName(review_to_add,'featuredReview')){
				if(featured_review){
					Element.insert(featured_review, {after:review_to_add});
					Element.remove(featured_review);
				}
				this._moveDownSelectedReview();
				this._redoBizReviewsInnerHeaders();
				return;
			}
		}else{
			// if there is a featured review, move it down inside bizReviewsInner and hide the featured review header.
			if(featured_review){
				featured_review.removeClassName('featuredReview');
				this._addReviewToInner(featured_review);
				$('featuredReviewHeader').hide();
			}
		}

		this._removeDuplicate(review_to_add);
		this._moveDownSelectedReview();
		this._redoBizReviewsInnerHeaders();

		// add the selected review, creating a highlighted review header if necessary
		var highlighted_review_header = this._getHighlightedReviewHeader(true);
		//change highlighted review header text if nececssary, depending on src
		switch(this.src){
			case 'self':
				highlighted_review_header.update('Your Review');
				break;
			case 'search':
				if(this.current_query.query)
					highlighted_review_header.update();
					highlighted_review_header.appendChild(document.createTextNode('Review Matching "#{query}"'.interpolate(this.current_query)));
				break;
			default:
				if(review_to_add.down('.reviewer_name'))
					highlighted_review_header.update(review_to_add.down('.reviewer_name').innerHTML+"'s Review");
		}
		toggleElement(highlighted_review_header, true);
		Element.insert(highlighted_review_header, {after:review_to_add});
	},


	// insert review somewhere appropriately inside bizReviewsInner after removing duplicates.
	// review_to_add is a prototype Element
	_addReviewToInner: function(review_to_add){
		this._removeDuplicate(review_to_add);

		var following_reviews     = $('bizReviewsInner').select('.followingReview');
		var non_following_reviews = $('bizReviewsInner').select('.nonfollowingReview');

		if(review_to_add.hasClassName('followingReview')){
			if(following_reviews.length > 0){
				$('bizReviewsInner').insertBefore(review_to_add, following_reviews[0]);
			}else if(non_following_reviews.length != 0){
				$('bizReviewsInner').insertBefore(review_to_add, non_following_reviews[0]);
			}else{
				$('bizReviewsInner').appendChild(review_to_add);
			}
		}else{
			if(non_following_reviews.length > 0){
				$('bizReviewsInner').insertBefore(review_to_add, non_following_reviews[0]);
			}else{
				$('bizReviewsInner').appendChild(review_to_add);
			}
		}
	},

	// look in bizReviewsInner, and reorganize headers according to number of following and nonfollowing
	_redoBizReviewsInnerHeaders: function(){
		var following_reviews = $('bizReviewsInner').select('.followingReview');
		var non_following_reviews = $('bizReviewsInner').select('.nonfollowingReview');
		// create or show following header / all header
		if (following_reviews.length > 0) {
			var following_review_header = this._getFollowingReviewHeader(true);
			$('bizReviewsInner').insertBefore(following_review_header, following_reviews[0]);
			following_review_header.style.display = 'block';
		} else {
			toggleElement('followingReviewHeader', false);
		}
		if (non_following_reviews.length > 0) {
			var all_review_header = this._getAllReviewHeader(true);
			$('bizReviewsInner').insertBefore(all_review_header, non_following_reviews[0]);
			all_review_header.style.display = 'block';
		} else {
			toggleElement('allReviewHeader', false);
		}
	},

	// Fetch a selected review id based on the hrid in the fragment.
	_fetcherSuccess: function(resp) {
		var rXML = resp.responseXML.documentElement;
		var snippets = rXML.getElementsByTagName('snippet');
		for (var i = 0; i < snippets.length; i++) {
			tag_name = snippets[i].getAttribute('name');
			if (tag_name == 'review') {
				review = snippets[i].firstChild.nodeValue;
			} else if (tag_name == 'pager') {
				// Need to replace the pager so we don't show
				// the selected or featured review in page 1+N.
				pager = snippets[i].firstChild.nodeValue;
			} else if (tag_name == 'empty') {
				// if there was no selected review bail out gracefully.
				return;
			} else {
				this.fetcherError(resp);
			}
		}

		if(!review) return;

		// create fetched review
		var fetched_review = new Element("div").update(review);
		fetched_review = $(fetched_review).firstDescendant();

		var rating_container = fetched_review.select('.rateReview')[0];
		var reviews_love = new Yelp.reviewsLove(rating_container);

		var from_search = (this.current_query.query != '');

		this._addReview(fetched_review, from_search);

		// initialize add/remove from following link
		var manage_selected_following = new Yelp.followingModify('.highlighted-review a.manage_following');

		//attach events for show/hide links on archived reviews
		Yelp.archive_snippet.attach_clicks(fetched_review);
		// hack to remove query from future calls to this method
		this.current_query.query = '';

		// replace the pager because we need to tell the next
		// page that we have a selected review (we don't want
		// to show the same review again).
		$('paginationControls').innerHTML = pager;
	},
	getVotes: function(){
		var types = ['useful','funny','cool'];
		var votes = {};
		for(var i=0;i<types.length;i++){
			var val = $('review_feedback_stats.'+this.current_query.hrid+'.'+types[i]).innerHTML.match(/\d+/gi);
			if(val){
				votes[types[i]] = parseInt(val[0]);
			}else{
				votes[types[i]] = 0;
			}
		}
		return votes;
	},
	fetcherSuccess: function(query_number, resp) {
		if (query_number != this.query_counter) {
			return;
		}
		if (resp.responseXML && resp.responseXML.documentElement){
			this._fetcherSuccess(resp);
		} else {
			this.fetcherError(resp);
		}
	},
	fetcherError: function(response) {
		var error_div = '<div id="error_result"><h3>Biz Review Error</h3><p>Whoops, something went wrong. Please try again, or if the problem persists, please email <a href="mailto:feedback@'+'yelp.com">feedback@'+'yelp.com</a> to report the error.</p></div>';
		$('snippetReviews').innerHTML=error_div;
	},
	// Get the selected review via xhr.
	fetchReviews: function(biz_id, featured_review_id, FTRid){
		if(!FTRid){var FTRid=false;}
		this.src = '';
		this.current_query.biz_id = biz_id;
		this.current_query.frid = featured_review_id;

		if(FTRid){ //fetching FTR
			this.current_query.hrid = FTRid;
		}else{ //regular hrid request
			this._parseHashVars();
			if (this.current_query.hrid == "") return;
		}

		query_params = gimmieQuery(window.location.href);
		if(query_params.q || query_params.sort_by){  // If we're on a query or a sort_by, ignore the HRID
			return;
		}else if(json_biz.review_count > 1){
			this.querycounter++;
			(new Ajax.Request(
				this.current_query.url() + "&nocache="+(new Date()).getTime(),
				{on404: this.fetcherError.bind(this),
				onSuccess: this.fetcherSuccess.bind(this, this.query_counter),
				onFailure: this.fetcherError.bind(this),
				method:'get'}
			));
		}
	}
});

// Find highlights for the About This Biz contents and
// a highlighted snippet for the About This Biz tab title
// on biz_details pages.
Yelp.AboutThisBizHighlightFetcher = Class.create();
Object.extend(Yelp.AboutThisBizHighlightFetcher.prototype, {

	// Extrancts the highlighed data from the Ajax call
	// and updates the About This Biz contents & tab with
	// those highlights.
	handleHighlightFetchSuccess: function(response) {
		var response_xml = response.responseXML.documentElement;
		var snippets = response_xml.getElementsByTagName('snippet');
		var highlighted_atb = null;
		var highlighted_shortened_atb = null;

		// Get the highlighting data from the xml returned by the Ajax call
		for(var i = 0; i < snippets.length; ++i) {
			tag_name = snippets[i].getAttribute('name');
			if (tag_name == 'atb_highlighted') {
				highlighted_atb = snippets[i].firstChild.nodeValue;
			} else if (tag_name = 'atb_highlighted_shortened') {
				highlighted_shortened_atb = snippets[i].firstChild.nodeValue;
			}
		}

		// Replace the old About This Biz with the new highlighted version
		var new_atb = document.createElement("div");
		new_atb.innerHTML = highlighted_atb;
		var old_atb = $('about_this_biz');
		if (old_atb && highlighted_atb) {
			old_atb.parentNode.insertBefore(new_atb, old_atb);
			old_atb.remove();
		}

		// Add a highlighted snippet to the About This Biz tab
		var tabs = $('about_reviews_tabs');
		if (tabs && highlighted_shortened_atb) {
			tabs.select('A').each(function(tab_link) {
				var this_rel = tab_link.up('li').readAttribute('rel');
				if (this_rel == 'about') {
					tab_link.innerHTML = tab_link.innerHTML + '<span class="atb_snippet">"' + highlighted_shortened_atb + '"</span>';
				}
			});
		}
	},

	handleHighlightFetchFailure: function(response) {
		// Highlighting failure!
	},

	// Will update the current business page's About This Biz contents
	// with highlighting and the About This Biz tab with a highlighted snippet.
	//  Args:
	//		biz_id - and encrypted business id
	//		snippet_length - the length of the snippet that will be
	//						 added to the About This Biz tab.
	// 	Returns:
	//		None
	fetchHighlights: function(biz_id, snippet_length) {
		var biz_vars = {};
		var curr_url = window.location.href;
		if(curr_url.match('#') && (curr_url.split('#')[1] != '')) {
			biz_vars = Yelp.getHashVars(window.location.href);
		}

		var atb_alias = '';
		if ((biz_vars.atb_alias) && (biz_vars.atb_alias != '')) {
			this.atb_alias = biz_vars.atb_alias;
		} else {
			// Without an atb_alias there is nothing to highlight, so don't bother trying.
			return;
		}

		var query = '';
		if ((biz_vars.query) && (biz_vars.query != '')) {
			query = biz_vars.query;
		} else {
			// Without a query nothing will be highlighted, so don't bother trying.
			return;
		}

		(new Ajax.Request(
			'/biz_details/about_this_biz_highlights', {
				method: 'get',
				parameters: {
					'biz_id': biz_id,
					'snippet_length': snippet_length,
					'atb_alias': this.atb_alias,
					'query': query
				},
				onSuccess: this.handleHighlightFetchSuccess.bind(this),
				onFailure: this.handleHighlightFetchFailure.bind(this)
			}
		));
	}
});

Yelp.biz_announce_track = function(click, announcement_id) {
	// Log announcement view
	var clickVal = "0";
	if (click) clickVal = "1";
	new Ajax.Request("/biz_details/track_announce", {parameters: "bid="+json_biz.id+"&aid="+announcement_id+"&click=" + clickVal});
};
Yelp.biz_announce = function(id_str, announcement_id, desc_str){
	this.id_str = id_str;
	this.el = $(id_str);
	this.desc_str = desc_str;
	this.announcement_id = announcement_id;
	this.announce_snip = this.el.innerHTML;
	this.start = this.el.getHeight();
	this._announce_tracked = false;
	Event.observe($(id_str + '_more'), 'click', this.show_ad.bind(this));
	Event.observe($(id_str + '_less'), 'click', this.hide_ad.bind(this));
	Yelp.biz_announce_track(false, announcement_id);
};
Yelp.biz_announce.prototype.show_ad = function(){
	this.el.innerHTML = this.desc_str;
	var total_height = this.el.scrollHeight;
	this.el.morph("height:"+total_height+"px",{duration:.3})
	$(this.id_str + '_more').hide();
	$(this.id_str + '_less').show();

	if (!this._announce_tracked) {
		// Log announcement click
		Yelp.biz_announce_track(true, this.announcement_id);
		this._announce_tracked = true;
	}
};
Yelp.biz_announce.prototype.hide_ad = function(){
	this.el.morph("height:"+this.start+"px",{duration:.3});
	this.reset.bind(this).delay(.2);
};
Yelp.biz_announce.prototype.reset = function(){
	this.el.innerHTML = this.announce_snip;
	$(this.id_str + '_less').hide();
	$(this.id_str + '_more').show();
};

// new less functional biz details map
Yelp.biz_map_init = function(){
	var marker = $('biz_marker');
	var zin = $('zoom_in');
	var zout = $('zoom_out');
	var likesDXFilters = false;
	if(Prototype.Browser.IE){
		var ua = navigator.userAgent;
		var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
		if (re.exec(ua) != null) {
			var rv = parseFloat( RegExp.$1 );
			if ((rv >= 6) && ((new RegExp("SV1")).exec(ua) != null)) {
				likesDXFilters = true;
			}
		}
	}
	[marker,zin,zout].each(function(el){
		if(el && likesDXFilters){
			var oSrc = el.src;
			el.src = imagesPrefix+"p.gif";
			el.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+oSrc+"')";
		}
	});
	Event.observe(zin,'click',Yelp.biz_map_activate);
	Event.observe(zout,'click',Yelp.biz_map_activate);
};
Yelp.biz_map_activate = function(e){
	var target = findEventTarget(e);
	Yelp.addScript(gmapsUrl+'&async=2', 'gmaps', [window,'GMap2'], function(){
		Yelp.addScript(jsPrefix+'maputil.js', 'maputil', [window,'GxMarker'],function(){if(!bizMap){bizMap = new Yelp.biz_details_map(target);}});
	});
};
var bizMap;
Yelp.biz_details_map = Class.create();
Object.extend(Yelp.biz_details_map.prototype, {
	initialize:function(target) {
		var clickURL = $('biz_marker').parentNode.href;
		$('biz_marker', 'static_map').invoke('remove');
		Event.stopObserving($('zoom_in'),'click',Yelp.biz_map_activate);
		Event.stopObserving($('zoom_out'),'click',Yelp.biz_map_activate);
		var zin = $('zoom_in').remove();
		var zout = $('zoom_out').remove();

		map = new GMap2($("mapdiv"));

		$('mapdiv').appendChild(zin);
		$('mapdiv').appendChild(zout);
		GEvent.addDomListener(zin, "click", function(){map.zoomIn();});
		GEvent.addDomListener(zout, "click", function(){map.zoomOut();});

		map.setCenter(new GLatLng(parseFloat(json_biz.latitude), parseFloat(json_biz.longitude)), 15);

		var m = createBizMarker(json_biz, -1, false, clickURL);
		map.addOverlay(m);

		copyrightTimer = window.setInterval('makeCopyrightSmaller()', 150);
		if(target.id=='zoom_in'){
			map.zoomIn();
		}else if(target.id=='zoom_out'){
			map.zoomOut();
		}
		Event.observe(window, 'unload', this.unload);
	},
	unload:function(){
        if(map){
            map.clearOverlays.call(map);
            GUnload();
        }
        map = null;
	}
});




// START ADVERTISER SLIDESHOW

function initss() {
	slideshow = new Yelp.slideshow('slideViewer', slides, { onStateChange:updateSlideStatus.bind(this) });
	slideshow.start();

	//preload mouseover stuff
	var ssimages = ['slideshow/reverseOn.gif', 'slideshow/forwardOn.gif', 'slideshow/playOn.gif', 'slideshow/pauseOn.gif', 'slideshow/pauseOn.gif'];
	for (var i = 0; i < ssimages.length; i++){
		Yelp.preloadImage(imagesPrefix + ssimages[i]);
	}

	Event.observe('reverseSSBtn', 'mouseover', function(event){ Event.element(event).style.cursor = 'pointer'; Event.element(event).src = imagesPrefix + 'slideshow/reverseOn.gif';});
	Event.observe('reverseSSBtn', 'mouseout', function(event){ Event.element(event).style.cursor = 'default'; Event.element(event).src = imagesPrefix + 'slideshow/reverseOff.gif';});
	Event.observe('reverseSSBtn', 'click', function(event) { slideshow.prev(); return false;});

	Event.observe('forwardSSBtn', 'mouseover', function(event){ Event.element(event).style.cursor = 'pointer'; Event.element(event).src = imagesPrefix + 'slideshow/forwardOn.gif';});
	Event.observe('forwardSSBtn', 'mouseout', function(event){ Event.element(event).style.cursor = 'default'; Event.element(event).src = imagesPrefix + 'slideshow/forwardOff.gif';});
	Event.observe('forwardSSBtn', 'click', function(event) { slideshow.next(); return false;});

	Event.observe('playSSBtn', 'mouseover', function(event){ Event.element(event).style.cursor = 'pointer'; playMouseover = true;	var playstate = paused ? 'pause':'play'; Event.element(event).src = imagesPrefix + 'slideshow/' + playstate + 'On.gif';}.bindAsEventListener(this));
	Event.observe('playSSBtn', 'mouseout', function(event){ Event.element(event).style.cursor = 'default'; playMouseover = false;	var playstate = paused ? 'pause':'play'; Event.element(event).src = imagesPrefix + 'slideshow/' + playstate + 'Off.gif';}.bindAsEventListener(this));
	Event.observe('playSSBtn', 'click', function(event) { paused?slideshow.unpause():slideshow.pause(); return false;});
}

function updateSlideStatus(playing, slideNum, total) {
	var rawhtml;
	if (total > 9){
		rawhtml = '<spa' +'n style="font-size: 9px;"><stron'+'g>' + slideNum + '</'+'strong> of <str'+'ong>' + total + '</'+'strong></'+'span>';
	} else	{
		rawhtml = '<stron'+'g>' + slideNum + '</'+'strong> of <str'+'ong>' + total + '</'+'strong>';
	}
	$('slidestatus').innerHTML = rawhtml;
	paused = !playing;
	var playstate = playing ? 'play':'pause';
	var mostate = playMouseover ? 'On':'Off';
	$('playSSBtn').src = imagesPrefix + 'slideshow/' + playstate + mostate + '.gif';
}

Yelp.slideshow = Class.create();
Yelp.slideshow.guid = 0;

Object.extend(Yelp.slideshow.prototype, {
	initialize: function(id, slides) {
		this.slideviewer = $(id);
		this.slides = slides;
		defaultOptions = {	width:			248,
							height:			186,
							fadeDuration:	500,
							looped:			true,
							slideperiod:	5000,
							onStateChange:	Prototype.emptyFunction
						 };
		this.options = Object.extend(defaultOptions, arguments[2] || {});
		this.mouseOver = false;
		this.lightboxArray = new Array();
		this.guid = new Date().getTime() + Yelp.slideshow.guid++;
		this._onStateChange = this.options.onStateChange;
		this.loading = false;
		this.rel_position = 1;
		this.animating = 0;
		this.slidetimer;
		this.playing = false;
		this.currentSlide = -1;
		this.captionHeight = 0;
		this.bylineHeight = 0;

		this.slideviewer.setStyle({position:'relative', margin:'0px', padding:'0px', width: parseInt(this.options.width) + 'px', height: parseInt(this.options.height) + 'px', backgroundColor:'#CCCCCC'});

		this._createHoverBox();
		this._setupSlideArray();
		this.lightbox = null;
		this.showinglbox = false;
	},

	start: function() {
		Event.observe(this.slideviewer, 'mouseout', this._handleMouseOut.bindAsEventListener(this));
		Event.observe(this.slideviewer, 'mouseover', this._handleMouseOver.bindAsEventListener(this));
		Event.observe(this.slideviewer, 'click', this._handleClick.bindAsEventListener(this));
		this.playing = true;

		if (this._preLoad(0)){
			this._updateSlide();
		}
	},

	pause: function() {
		if (this.playing){
			this._clearSlidetimer();
			this.playing = false;
			this._updateState();
		}
	},

	unpause: function() {
		if (!this.playing && !this.showinglbox) {
			this.playing = true;
			this._startSlidetimer();
			this._updateState();
		}
	},

	isPlaying: function() {
		return this.playing;
	},

	_updateState: function() {
		this._onStateChange(this.playing, this.currentSlide+1, this.slides.length);
	},

	_updateHover: function() {
		if (this.mouseOver) {
			if (this.hover.childNodes[1].style.visibility == 'visible') {
				if (this.hover.offsetHeight != this.captionHeight) {
					new Effect.Height(this.hover, {duration: this.options.fadeDuration/1000,startHeight: this.hover.offsetHeight, endHeight: this.captionHeight});
				}
			} else if (this.hover.childNodes[0].style.visibility == 'visible') {
				if (this.hover.offsetHeight != this.bylineHeight)	{
					new Effect.Height(this.hover, {duration: this.options.fadeDuration/1000,startHeight: this.hover.offsetHeight, endHeight: this.bylineHeight});
				}
			} else if (this.hover.offsetHeight != 0) {
				new Effect.Height(this.hover, {duration: this.options.fadeDuration/1000,startHeight: this.hover.offsetHeight, endHeight: 0});
			}
		} else {
			if (this.hover.childNodes[0].style.visibility == 'visible') {
				if (this.hover.offsetHeight != this.bylineHeight) {
					new Effect.Height(this.hover, {duration: this.options.fadeDuration/1000,startHeight: this.hover.offsetHeight, endHeight: this.bylineHeight});
				}
			} else if (this.hover.offsetHeight != 0) {
				new Effect.Height(this.hover, {duration: this.options.fadeDuration/1000,startHeight: this.hover.offsetHeight, endHeight: 0});
			}
		}
	},

	_updateSlide: function() {
		var nextSlide = 0;
		if (this.currentSlide + this.rel_position >= this.slides.length) {
			nextSlide = this.currentSlide + this.rel_position - this.slides.length;
		} else if  (this.currentSlide + this.rel_position < 0) {
			nextSlide = this.currentSlide + this.rel_position + slides.length;
		} else {
   			nextSlide = this.currentSlide + this.rel_position;
		}


		if (this.currentSlide > -1 && !this._preLoad(nextSlide)) return;

		// first fade out previous image
		if (this.currentSlide > -1 && this.slides.length > 1) {
			this.lastSlideEl = slides[this.currentSlide].slide;
			this.lastSlideEl.setStyle({zIndex:2});
			new Effect.Fade(this.lastSlideEl, {duration: this.options.fadeDuration/1000, afterFinish: this._doneFading.bind(this, this.lastSlideEl)});
			this.animating++;
		}

		this.nextSlideEl = this.slides[nextSlide].slide;
		this.nextSlideEl.setStyle({zIndex:3});

		var byline = this._getByLine(nextSlide);
		var caption = this._getCaption(nextSlide);

		if (byline != '') {
			this.hover.childNodes[0].innerHTML = byline;
			this.hover.childNodes[0].style.visibility = 'visible';
			if (caption != '') {
				this.hover.childNodes[1].innerHTML = caption;
				this.hover.childNodes[1].style.visibility = 'visible';
			} else {
				this.hover.childNodes[1].innerHTML = '';
				this.hover.childNodes[1].style.visibility = 'hidden';
			}
		} else {
			this.hover.childNodes[0].innerHTML = '';
			this.hover.childNodes[0].style.visibility = 'hidden';
			this.hover.childNodes[1].innerHTML = '';
			this.hover.childNodes[1].style.visibility = 'hidden';
		}

		this.animating++;
		this.nextSlideEl.setStyle({visibility:'visible'});
		new Effect.Appear(this.nextSlideEl, {duration: this.options.fadeDuration/1000, afterFinish: this._doneAppearing.bind(this)} );

		this.currentSlide = nextSlide;
		this._updateHover();
		this._updateState();

		//if (looped && playing && !paused) {
		if (this.slides.length == 1) return;
		this._startSlidetimer();
		//}
		this.rel_position = 1;
	},

	_doneAppearing: function() {
		this.animating--;
	},

	_doneFading: function(slide) {
		this.animating--;
	},

	next: function() {
		if (this.slides.length == 1) return;
		if (this.animating <= 0)
		{
			this._clearSlidetimer();
			this._updateSlide();
		}
	},

	prev: function() {
		if (this.slides.length == 1) return;
		if (this.animating <= 0)
		{
			this._clearSlidetimer();
			this.rel_position = -1;
			this._updateSlide();
		}
	},

	_createImage: function(photoNum, onloadFunc) {
		var newImage = document.createElement('img');
		newImage.setAttribute('id', 'slide_'+this.guid+'_'+photoNum);
		newImage = $(newImage);
		this.slideviewer.appendChild(newImage);
		newImage.photoNum = photoNum;
		newImage.setStyle( {position:'absolute', top:'0px', left:'0px', border:'0px', margin:'0px', zIndex:2 } );
		Element.setOpacity(newImage, 0);
		Event.observe(newImage, "load", onloadFunc);
		newImage.src = imagesHostUrl + this.slides[photoNum]['image_url'] + 'sl';
		return newImage;
	},

	_preLoad: function(photoNum) {
		if (this.slides[photoNum].loaded){
			return true;
		} else	{
			this.loading = true;
			this._createImage(photoNum, this._preloadHandler.bindAsEventListener(this));
			return false;
		}
	},

	_preloadHandler: function(event) {
		var slide = event.currentTarget || event.srcElement;
		var photoNum = slide.photoNum;
		this.slides[photoNum].loaded = true;
		this.slides[photoNum].slide = $(slide);
		this.loading = false;

		this.slides[photoNum].slide.style.left = parseInt(Math.floor(this.options.width/2.0 - this.slides[photoNum].slide.width / 2.0)) + 'px';
		this.slides[photoNum].slide.style.top = parseInt(Math.floor(this.options.height/2.0 - this.slides[photoNum].slide.height / 2.0)) + 'px';
		this._updateSlide();
		//Event.stopObserving(this.slides[photoNum].slide, 'load', this._preloadHandler);
	},

	getGuid: function() {
		return this.guid;
	},

	_clearSlidetimer: function() {
		window.clearTimeout(this.slidetimer);
		this.slidetimer = null;
	},

	_startSlidetimer: function(period) {
		this._clearSlidetimer();
		this.slidetimer = window.setTimeout(this._updateSlide.bind(this), period || this.options.slideperiod);
	},

	_createHoverBox: function() {
		this.hover = document.createElement('div');
		this.hover.setAttribute('id', 'hover_'+this.guid);

		this.hover = $(this.hover);
		Element.setOpacity(this.hover, 0);
		this.hover.setStyle({zIndex:4, position: 'absolute', bottom:'0px', left:'0px', width: this.options.width + 'px', height: '30px', backgroundColor: '#FFF', margin: '0px', padding: '0px', overflow:'hidden'});

		this.slideviewer.appendChild(this.hover);

		var byLine = document.createElement('div');
		byLine = $(byLine);
		byLine.setStyle({width:this.options.width, height:'11px', color:'#000', margin:'0px',border:'0px',padding:'2px',fontSize:'11px',visibility:'hidden'});
		byLine.innerHTML = '|';
		this.hover.appendChild(byLine);
		this.bylineHeight = byLine.offsetHeight;

		var caption = document.createElement('div');
		caption = $(caption);
		caption.setStyle({width:this.options.width, height:'11px', color:'#000', margin:'0px',border:'0px',padding:'2px',fontSize:'11px',visibility:'hidden'});
		caption.innerHTML = '|';
		this.hover.appendChild(caption);
		this.captionHeight = this.bylineHeight + caption.offsetHeight;

		Element.setOpacity(this.hover, .8);
		this.hover.setStyle({height: '0px', visibility:'visible'});
		byLine.setStyle({visibility:'hidden'});
		caption.setStyle({visibility:'hidden'});
	},

	_getByLine: function(photoNum) {
		if (this.slides[photoNum]['user']['exists'] && this.slides[photoNum]['user']['display_name']) return 'Photo by ' + this.slides[photoNum]['user']['display_name'];
		return '';
	},

	_getCaption: function(photoNum) {
		if (this.slides[photoNum]['caption']) return this.slides[photoNum]['caption'];
		return '';
	},

	_getFullCaption: function (photoNum){
		var fullCaption = this._getByLine(photoNum);
		var caption = this._getCaption(photoNum);
		if (caption != '') {
			fullCaption += '<br />' + caption;
		}
		return fullCaption;
	},

	_setupSlideArray: function() {
		for (var i = 0; i < this.slides.length; i++) {
			this.slides[i].loaded = false;
			this.lightboxArray.push(new Array(this.slides[i]['image_url'] + 'l', this._getFullCaption(i), false));
		}
	},

	_handleMouseOver: function(event) {
		this.pause();
		if (this.currentSlide >= 0 && this.slides[this.currentSlide]['image_url'] != null)
		{
			Event.element(event).style.cursor = 'pointer';
		}
		this.mouseOver = true;
		this._updateHover();
	},

	_handleMouseOut: function(event) {
		var target = event.relatedTarget || event.toElement;
		if (!target) return;
		if (target.nodeType==3) target=target.parentNode;
		if (this.slideviewer != target && !Element.descendantOf(target, this.slideviewer)) {
			this.unpause();
			Event.element(event).style.cursor = 'default';
			this.mouseOver = false;
			this._updateHover();
		}
	},

	_handleClick: function(event) {
		if (this.currentSlide >= 0 && this.slides[this.currentSlide]['type'] == 'photo'){
			this._prepareForSlideshow();
			if(!Yelp.lightbox){
				Yelp.addScript(jsPrefix+'lightbox.js', 'lightboxjs', [Yelp,'lightbox'], function(){this._initLightbox();}.bind(this));
			}else{
				this._initLightbox();
			}
		}
	},

	_initLightbox: function(){
		if(this.lightbox == null){
			this.lightbox = new Yelp.lightbox();
		}

		this.lightbox.start(this.lightboxArray, this.currentSlide, this._returnFromSlideshow.bind(this));
	},

	_prepareForSlideshow: function() {
		this.showinglbox = true;
		this.pause();
	},

	_returnFromSlideshow: function() {
		this.showinglbox = false;
		this.unpause();
	}

});
//TODO: make this a proper class
Yelp.directions = {
	dObj:null,//GDirections object
	flag:null,//you are here flag
	ev:null,//onLoad event for dObj
	path:null,//directions string
	gsvClient: null,//streetview client
	marker:null,//biz marker
	pano:null,//streetview panorama
	extraFlags : [],//array of replacement markers for start and end points
	initialize:function() {
		Event.observe('get_directions_button', 'click', Yelp.directions.getDirections);
		Event.observe('swicther', 'click', Yelp.directions.switchDirections);
		Event.observe('printBtn', 'click', Yelp.directions.printPage);
		Event.observe('start_point', 'keydown', Yelp.directions.keywatcher);

		//set up autocompleters
		Yelp.directions.start_ac = new Yelp.autocomplete('start_point', Yelp.country_name_list);
		Yelp.directions.end_ac = new Yelp.autocomplete('end_point', Yelp.country_name_list);

		var directionsPanel = document.getElementById("directions_results");
		map = new GMap2(document.getElementById("directions_map"));
		map.setCenter(Yelp.directions.bizPoint.latlng, 14);
		map.addControl(new GMapTypeControl());

		Yelp.directions.dObj = new GDirections(map, directionsPanel);
		GEvent.addListener(Yelp.directions.dObj,"error",Yelp.directions.errorHandler);

		copyrightTimer = window.setInterval('makeCopyrightSmaller()', 150);

		var zin = document.createElement('img');
		var zout = document.createElement('img');
		zin.src = (imagesPrefix+'map/zoom_in.png');
		zout.src = (imagesPrefix+'map/zoom_out.png');
		zin.id = 'zoom_in';
		zout.id = 'zoom_out';
		$('directions_map').appendChild(zin);
		$('directions_map').appendChild(zout);
		GEvent.addDomListener(zin, "click", function(){map.zoomIn();});
		GEvent.addDomListener(zout, "click", function(){map.zoomOut();});

		Yelp.directions.gsvClient = new GStreetviewClient();
		Yelp.directions.gsvClient.getNearestPanoramaLatLng(Yelp.directions.bizPoint.latlng, Yelp.directions.initGSV);

		var to = $('end_point').value.strip();
		var hash = Yelp.getHashVars(window.location.href);
		if(hash.start && hash.start != to){
			var path = Yelp.directions.makePath(hash.start, to);
			Yelp.directions.handleDirections(path);
			Yelp.directions.path = path;
			$('start_point').value = hash.start;
		}else{
			Yelp.directions.bizPoint.marker = new GxMarker(Yelp.directions.bizPoint.latlng, createSliceIcon(-1, SLICE_STYLE_NORMAL), "",'');
			map.addOverlay(Yelp.directions.bizPoint.marker);
		}
	},
	initGSV : function(latlng){ //see if there is a google streeview pano nearby
		if(latlng){
			var bounds = map.getBounds();
			//is the nearest gstreetview in the map bounds?
			if(bounds.containsLatLng(latlng)){
				Yelp.directions.gsvOverlay = new GStreetviewOverlay();
				map.addOverlay(Yelp.directions.gsvOverlay);
				Yelp.directions.gsvOverlay.hide();//start hidden

				map.getContainer().appendChild(Yelp.DOM.div({'id':'gsvToggle', 'class':'off'},Yelp.DOM.div('Street View')));
				//handler for turning streetview on and off
				Event.observe('gsvToggle','click',function(){
					if(Yelp.directions.gsvOverlay.isHidden()){
						Yelp.directions.gsvOverlay.show();
						this.removeClassName('off');
					}else{
						Yelp.directions.gsvOverlay.hide();
						this.addClassName('off');
					}
				});

				//create marker and pano
				Yelp.directions.marker = new GMarker(latlng);

				Yelp.directions.marker.bindInfoWindowHtml('<div id="pano"></div>');
				map.addOverlay(Yelp.directions.marker);
				Yelp.directions.marker.hide();

				//add pano show/hide events
				GEvent.addListener(Yelp.directions.marker,'infowindowclose', function(m){m.hide();Yelp.directions.pano = null;});
				GEvent.addListener(Yelp.directions.marker,'infowindowopen',function(m){Yelp.directions.pano = new GStreetviewPanorama(document.getElementById('pano'),{'latlng':m.getLatLng()});});

				//show streetview pano onclick
				GEvent.addListener(map,"click", Yelp.directions.gsvOverlayClick);
			}
		}
	},
	gsvOverlayClick: function(overlay, point) { //click on a gstreetview poly and get a infowindow w/ the pano in it
		var mapclick = !overlay; //if overlay is null, click is on map itself
		if(!Yelp.directions.gsvOverlay.isHidden()){//if no gsvOverlay visible, ignore clicks
			if(mapclick && point){
				//check to see if click location is valid
				Yelp.directions.gsvClient.getNearestPanoramaLatLng(point, function(latlng){
					if(latlng){
						Yelp.directions.showPano(latlng, true);
					}
				});

			}else{//click is on an overlay
				if(overlay == Yelp.directions.bizPoint.marker){
					Yelp.directions.showPano(Yelp.directions.bizPoint.latlng, false);
				}
			}
		}
	},
	showPano: function(latlng, showMarker){
		Yelp.directions.marker.setLatLng(latlng);
		if(showMarker)
			Yelp.directions.marker.show();
		GEvent.trigger(Yelp.directions.marker,'click');
	},
	errorHandler: function() {
		var code = Yelp.directions.dObj.getStatus().code;
		if(code == 604 || code == 602){
			var start = $('start_point').value.strip();
			var end = $('end_point').value.strip();
			if(start.indexOf(',') < 0){
				var corrected_path = start+', '+current_location;
				$('start_point').value = corrected_path;
				Yelp.directions.getDirections();
			}else if(end.indexOf(',') < 0){
				var corrected_path = end+', '+current_location;
				$('end_point').value = corrected_path;
				Yelp.directions.getDirections();
			}else{
				if (code == 604){
					alert("Sorry, we can't find a route between those two places.");
				} else{
					alert(Yelp._("We didn't recognize one of your addresses. Please enter at least a city and state or a ZIP code."));
				}
			}
		}else{
			alert("Error: code"+ code);
		}
	},
	getDirections:function(){
		var from = $('start_point').value.strip();
		var to = $('end_point').value.strip();
		if(from!='' && to!=''){
			var path = Yelp.directions.makePath(from, to);
			//don't clear all overlays, just direction points
			Yelp.directions.dObj.clear();
			Yelp.directions.bizPoint.marker.hide();
			if(Yelp.directions.extraFlags){
				Yelp.directions.extraFlags.each(function(flag){map.removeOverlay(flag);});
				Yelp.directions.extraFlags = [];
			}

			if(Yelp.directions.marker && Yelp.directions.gsvOverlay){
				Yelp.directions.marker.hide();
				Yelp.directions.marker.closeInfoWindow();
				map.removeOverlay(Yelp.directions.gsvOverlay);
				$('gsvToggle').addClassName('off');
			}

			Yelp.directions.handleDirections(path);
			Yelp.directions.path = path;
			window.location.hash = Yelp.writeHashVars({'start':from});
			//update cookie
			var typed_address = (from.strip() != Yelp.directions.bizPoint.address) ? from : (to != Yelp.directions.bizPoint.address) ? to: false;
			if(typed_address && Yelp.directions.start_ac.recent_locations.indexOf(typed_address) < 0){
				Yelp.directions.start_ac.recent_locations.unshift(typed_address);
				while(Yelp.directions.start_ac.recent_locations.size() > 50){
					Yelp.directions.start_ac.recent_locations.pop();
				}
				var expireDate = new Date();
				expireDate.setYear(expireDate.getFullYear()+(20/1));
				document.cookie = "recentlocations=" + escape(Yelp.directions.start_ac.recent_locations.join(';;')) + "; expires="+expireDate.toGMTString() + "; path=/;domain="+Yelp.cookie_domain;
			}
		}else{
			alert('Please enter both a start and end address.');
		}
	},
	handleDirections: function(path){
		if(!$('directions_map').hasClassName('withDirections')){
			$('directions_map').addClassName('withDirections');
			$('directions_results').show();
			map.checkResize();
		}
		Yelp.directions.dObj.load(path, {locale: Yelp.lang_pref[0]});
		Yelp.directions.ev = GEvent.addListener(Yelp.directions.dObj,'load',Yelp.directions.resetIcons);
	},
	hideMarker:function(marker){
		var startIcon = marker.getIcon();
		startIcon.image = (imagesPrefix+'p.gif');
		startIcon.iconSize = new GSize(1,1);
		startIcon.iconAnchor = new GPoint(1, 1);
		startIcon.shadow = (imagesPrefix+'p.gif');
		startIcon.shadowSize = new GSize(1, 1);
		marker.redraw();
	},
	resetIcons:function(){
		// this gets called immediately after the plotting of the directions, so we can
		// redraw the start marker in our own style

		// we will replace the start and end icon with our own markers later.
		var start = Yelp.directions.dObj.getMarker(0);
		Yelp.directions.hideMarker(start);
		var end = Yelp.directions.dObj.getMarker(1);
		Yelp.directions.hideMarker(end);

		var endIcon = new GIcon();
		endIcon.image = (imagesPrefix+'map/marker_star_blue.png');
		endIcon.iconSize = new GSize(20,29);
		endIcon.iconAnchor = new GPoint(15, 29);
		endIcon.shadow = (imagesPrefix+'map/marker_shadow.png');
		endIcon.shadowSize = new GSize(38, 29);
		endIcon.infoWindowAnchor = new GPoint(15, 3);
		var newEnd = new GMarker(end.getLatLng(), {'icon':endIcon});
		GEvent.addListener(newEnd,'click', function(overlay){
			if(Yelp.directions.gsvOverlay.isHidden()){
				this.showMapBlowup();
			}else{
				Yelp.directions.showPano(this.getLatLng(), false);
			}
		});
		map.addOverlay(newEnd);
		Yelp.directions.extraFlags.push(newEnd);

		var html = '<img src="'+imagesPrefix+'map/loc_pin_flag.gif" id="loc_pin_flag" width="41" height="48">';
		var startMarker = new GxMarker(start.getLatLng(), createSliceIcon(-1, SLICE_STYLE_NORMAL), html,'');
		startMarker.onClick = function(){//need to override GxMarker's onClick
			if(!Yelp.directions.gsvOverlay.isHidden()){
				Yelp.directions.clearFlag();
				Yelp.directions.showPano(start.getLatLng(), false);
			}
		};
		map.addOverlay(startMarker);
		Yelp.directions.flag = startMarker;
		Yelp.directions.extraFlags.push(startMarker);


		if(Yelp.directions.ev){
			GEvent.removeListener(Yelp.directions.ev);
			Yelp.directions.ev = null;
		}
		//step icons must be delayed
		window.setTimeout(function(){Yelp.directions.resetStepIcons();}, 500);
	},
	clearFlag: function(){
		if(Yelp.directions.flag){
			if(Yelp.directions.flag.tooltipObject){
				Yelp.directions.flag.tooltipObject.style.display = 'none';
			}
			GEvent.clearListeners(map, 'moveend');
			Yelp.directions.flag = null;
		}
	},
	resetStepIcons: function(){
		if(Yelp.directions.flag){
			//this shows the "you are here" flag
			Yelp.directions.flag.showMouseOver(false);
			Yelp.directions.flag.removeHighlight();
			if(Yelp.directions.flag.tooltipObject){
				var left = Yelp.directions.flag.tooltipObject.style.left.match(/\d+/);
				var top = Yelp.directions.flag.tooltipObject.style.top.match(/\d+/);
				Yelp.directions.flag.tooltipObject.style.left = (parseInt(left)-5)+'px';
				Yelp.directions.flag.tooltipObject.style.top = (parseInt(top)+3)+'px';
			}
			GEvent.addListener(map, 'moveend', Yelp.directions.clearFlag);
		}
		var flags = Yelp.child_elms('directions_results', 'img');
		flags.each(function(el){
			var iconPath = imagesPrefix + 'ico/map_marker_med' + ((el == flags.last()) ? '_blue.gif':'.gif');
			el.src = iconPath;
		});
		//don't really want to put this here, but gmaps won't hide the gsvOverlay properly without a timeout
		if(Yelp.directions.gsvOverlay){
			map.addOverlay(Yelp.directions.gsvOverlay);
			Yelp.directions.gsvOverlay.hide(); //hide it until button is clicked
		}
	},
	keywatcher:function(e){
		//start_ac also watches this form input, avoid conflicts
		if(Yelp.directions.start_ac && Yelp.directions.start_ac.autocompleter && Yelp.directions.start_ac.autocompleter.active){
			//autocompleter active, pass
		}else{
			if(e.keyCode==Event.KEY_RETURN){
				Yelp.directions.getDirections();
			}
		}
	},
	switchDirections: function(){
		var start = $('start_point');
		var end = $('end_point');
		var from = start.value.strip();
		var to = end.value.strip();
		start.value = to;
		end.value = from;
		if(end.value == Yelp.directions.bizPoint.address){
			$('to_address').show();
		}else{
			$('to_address').hide();
		}

		if($('dropperBtn_mapstart')){
			var dpoints = $$('#directions .direction_point');
			if(dpoints.first().down('#dropperBtn_mapstart')){
				var btn = $('dropperBtn_mapstart').remove();
				if(!end.hasClassName('short')){
					end.addClassName('short');
					start.addClassName('long');
				}
				//replace click handler
				btn.onclick = function(){Yelp.init_loc_picker($('dropperBtn_mapstart'), 'end_point');};
				//move button
				dpoints.last().style.width = "385px";
				dpoints.last().insertBefore(btn, end);
			}else if(dpoints.last().down('#dropperBtn_mapstart')){
				var btn = $('dropperBtn_mapstart').remove();
				if(end.hasClassName('short')){
					end.removeClassName('short');
					start.removeClassName('long');
				}
				//replace click handler
				btn.onclick = function(){Yelp.init_loc_picker($('dropperBtn_mapstart'), 'start_point');};
				//move button
				dpoints.first().insertBefore(btn, start);
				dpoints.last().style.width = "380px";
			}
		}

		if(from!='' && to!=''){
			Yelp.directions.getDirections();
		}
	},
	initPrint: function(){
		var directionsPanel = document.getElementById("directions_results");
		map = new GMap2(document.getElementById("directions_map"));
		map.addControl(new GSmallZoomControl());
		map.setCenter(Yelp.directions.bizPoint.latlng, 14);
		Yelp.directions.dObj = new GDirections(map, directionsPanel);
		if(window.path){
			Yelp.directions.getPrintDirections(window.path);
			Yelp.directions.path = window.path;
		}else if(Yelp.directions.startPoint && Yelp.directions.endPoint){
			var path = Yelp.directions.makePath(Yelp.directions.startPoint, Yelp.directions.endPoint);
			Yelp.directions.getPrintDirections(path);
			Yelp.directions.path = path;
		}else{
			$('directions_map').setStyle({width:'675px',height:'400px',position:'absolute',left:'10px',top:'100px'});
			map.checkResize();
			map.setCenter(Yelp.directions.bizPoint.latlng);
			var start = new GxMarker(Yelp.directions.bizPoint.latlng, createSliceIcon(-1, SLICE_STYLE_NORMAL), "",'');
			map.addOverlay(start);
		}

	},
	showPrintMaps: function(){
		var points = Yelp.directions.dObj.getNumGeocodes();
		var maps = $$('.dPointMap');
		Yelp.directions.pMaps = [];
		for(var i=0;i<points;i++){
			var marker = Yelp.directions.dObj.getMarker(i);
			var c = marker.getLatLng();
			Yelp.toggleVisible(maps[i]);
			var m = new GMap2(document.getElementById('map'+(i+1)));
			m.setCenter(c,15);
			m.addOverlay(new GMarker(c));
			m.addControl(new GSmallZoomControl());
			Yelp.directions.pMaps.push(m);
		}
	},
	getPrintDirections: function(path){
		Yelp.directions.dObj.load(path);
		GEvent.addListener(Yelp.directions.dObj, 'load',Yelp.directions.showPrintMaps);
		window.setTimeout(function(){ $$('.googledir td').each(function(el){
			if(el.readAttribute('jscontent')=='address'){
				el.style.fontSize = '14px';
			}
			});
			window.setTimeout(function(){
				window.print();
			}, 600);
		},500);
	},
	printPage: function(){
		var alias = window.location.pathname.split('/').last();
		var path = '/map/print_layout/'+alias;
		var hash = Yelp.getHashVars(window.location.href);
		if(hash.start){
			path += '?start_point='+encodeURIComponent(hash.start);
			if(Yelp.directions.bizPoint.address != $('end_point').value){
				path += '&end_point='+ encodeURIComponent($('end_point').value);
			}
		}
		var pWindow = window.open(path, "printWindow", "height=600,width=720,status=yes,toolbar=no,menubar=yes,location=no,scrollbars=yes");
		pWindow.path = Yelp.directions.path;
	},
	makePath: function(start, end){
		// silently add ', uk' when we're in the UK
		// This is an imperfect solution; it'd be better
		// if GDirections had had a setBaseCountryCode()
		// method, like GClientGeocoder
		if (typeof(explicit_country_name) == 'string'&&explicit_country_name) {
			if (start){
				start = start + ', ' + explicit_country_name;
			}
			if (end){
				end = end + ', ' + explicit_country_name;
			}
		}
		return start + ' to ' + end;
		//return 'from: ' + start + ' to: ' + end;
	}
};

Yelp.ReviewSummaryMechatron = Class.create({
	initialize: function(options){
		this.container = options['container'];                           // "Review Snapshot" box
		this.main_reviews_elements = options['main_reviews_elements'];   // things to hide when showing summaries
		this.main_reviews_container = options['main_reviews_container']; // box containing the main reviews
		this.scroll_to_element = options['scroll_to_element'];           // a viewport placeholder
		this.graphs_header = options['graphs_header'];
		this.graphs_content = options['graphs_content'];
		this.graphs_urls = options['graphs_urls'];
		this.graphs_loaded = [];

		this.callbacks = {};
		this.callbacks.onClickNgramLink = this.replaceNgramLinksAndRequestSummaryReviews.bindAsEventListener(this);
		this.callbacks.onSuccessReviewFetch = this.handleReviewFetchSuccess.bindAsEventListener(this);
		this.callbacks.onFailureReviewFetch = this.handleReviewFetchFailure.bindAsEventListener(this);
		this.callbacks.onClickReturnToAllReviews = this.returnToAllReviews.bindAsEventListener(this);
		this.callbacks.onClickReviewGraphs = this.initReviewGraphSwitch.bindAsEventListener(this);
		this.callbacks.onLoadReviewGraphImage = this.displayLoadedReviewGraph.bind(this);
		this.callbacks.onClickLinkToThis = this.linkToThisReplacementAction.bindAsEventListener(this);

		this.created_elements = { 'burst': [], 'ngram_reviews': [], 'saved_ngram_links': [], 'preloader': [] };
		this.loaded_ngram = null;
		this.burst_start_time = null // minimum-period timer for burst

		// create/init the "whats's this" link
		var whats_this = new Element('a', {
			href: '#',
			title: Yelp._("In their reviews, Yelpers mentioned the linked phrases below a lot. And these aren't any old common phrases, they're also the ones that our Yelp Robots have determined are unique and good, quick ways to describe this business. Click any of the phrases to see all the reviews that mention it.<br><br>To the right are two graphs. First is the distribution of 1, 2, 3, 4 and 5-star ratings. Click \"Trend\" to see how the average rating has changed over time.<br><br>For now, you'll only see these fun highlights on restaurant and nightlife pages with more than 15 reviews.")
		}).update(Yelp._("What's this?"));
		new Yelp.tooltipz(whats_this, {doClick: true});
		this.container.down('H3').appendChild(whats_this);

		// get the graphs going
		this.initReviewGraphSwitch(null, 'dist');

		// register onclicks for the ngram links - ngram text, "in x reviews", and user photos
		this.container.select('a.ngram').invoke('observe', 'click', this.callbacks.onClickNgramLink);
	},

	registerCreatedElement: function(bucket, elt){ return this.created_elements[bucket].push(elt); },
	getCreatedElements: function(bucket){ return this.created_elements[bucket]; },
	clearCreatedElements: function(bucket, do_not_remove){
		var created_elements = this.getCreatedElements(bucket);
		var elt = null;
		while( elt = created_elements.pop() ) {
			if( !do_not_remove && elt.nodeName ) elt.remove();
		}
	},

	hideMainReviews: function(){ this.main_reviews_elements.invoke('hide'); },
	showMainReviews: function(){ this.main_reviews_elements.invoke('show'); },

	removeSummaryReviews: function(){ this.clearCreatedElements('ngram_reviews'); },

	getBurstContainer: function(){
		var ngram_reviews = this.getCreatedElements('ngram_reviews');
		return (ngram_reviews.length > 0) ? ngram_reviews[0] : this.main_reviews_container;
	},

	showLoadingBurst: function(){
		var burst_container = this.getBurstContainer();
		burst_container.makePositioned();
		var burst_container_dimensions = burst_container.getDimensions();
		var opacity_layer = new Element('div', {'class':'summaries_loader'});
		opacity_layer.setStyle({
			width: burst_container_dimensions.width+'px',
			height: burst_container_dimensions.height+'px',
			opacity: 0.6,
			zIndex: 1
		});
		burst_container.appendChild(opacity_layer);
		this.registerCreatedElement('burst', opacity_layer);

		var burst_div = new Element('div', {'class':'summaries_loader_burst'});
		MIN_HEIGHT_BURST = 300;
		burst_div.setStyle({
			'top': '20px',
			'left': Math.floor(burst_container_dimensions.width / 2) - 37 + "px" //37 is half of the width of burst_loader_big.gif
		});

		burst_container.appendChild(burst_div);
		this.registerCreatedElement('burst', burst_div);
		burst_div.innerHTML = '<img src="'+imagesPrefix+'search/burst_loader_big.gif"><h3>Loading...</h3>';
		this.burst_start_time = (new Date()).getTime();
	},

	clearLoadingBurst: function(){
		var now = (new Date()).getTime();
		if( (now - this.burst_start_time) < 1000 ) {
			setTimeout(this.clearLoadingBurst.bind(this), (now - this.burst_start_time) + 50);
			return;
		}
		this.burst_start_time = null;
		this.clearCreatedElements('burst');
	},

	showingNgramReviews: function(){ return this.getCreatedElements('ngram_reviews').length > 0; },

	getCurrentReviewsContainer: function(){
		var ngram_reviews = this.getCreatedElements('ngram_reviews');
		return ngram_reviews.length > 0 ? ngram_reviews[0] : this.main_reviews_container;
	},

	createNgramLinkSubstitutes: function(ngram){
		// sub out the ngram A tags, store them in the object and replace with equivalent SPANs
		this.container.select('A.ngram[ngram="'+ngram+'"]').each(function(ngram_link){
			var replacement_span = new Element('span', {
				'class':ngram_link.className,
				'ngram':ngram
			}).update(ngram_link.innerHTML);
			ngram_link.parentNode.insertBefore(replacement_span, ngram_link);
			// cloneNode to work around an IE issue with retaining the :hover styling when reattaching
			var removed_link = ngram_link.cloneNode(true);
			removed_link.stopObserving('click', this.callbacks.onClickNgramLink);
			ngram_link.remove();
			this.registerCreatedElement('saved_ngram_links', removed_link);
		}, this);
	},

	revertSavedNgramLinks: function(){
		// sub the stored ngram A's back in for the placeholder SPANs
		var existing_spans = this.container.select('SPAN.ngram[ngram="'+ this.loaded_ngram +'"]');
		this.getCreatedElements('saved_ngram_links').each(function(link, index){
			link.observe('click', this.callbacks.onClickNgramLink);
			existing_spans[index].parentNode.insertBefore(link, existing_spans[index]);
			existing_spans[index].remove();
		}, this);
		this.clearCreatedElements('saved_ngram_links', true);
	},

	replaceNgramLinksAndRequestSummaryReviews: function(evt){
		var clicked_link = Event.element(evt);
		if( clicked_link.nodeName == "IMG" ) clicked_link = clicked_link.up('A');
		var ngram = clicked_link.readAttribute('ngram');
		var sentence_review_id = clicked_link.readAttribute('sentence-review-id');
		var start = clicked_link.readAttribute('start');

		// clean up the UI and show the burst loader graphic
		clicked_link.blur();
		this.showLoadingBurst();

		// unless a reasonable amount of the reviews section is showing, scroll it into view
		var scroll_offsets = document.viewport.getScrollOffsets();
		var container_offsets = this.container.cumulativeOffset();
		var absolute_bottom_of_snapshot_box = container_offsets.top + this.container.getHeight();
		var absolute_bottom_of_window = scroll_offsets.top + document.viewport.getHeight();
		if(
				(absolute_bottom_of_window - absolute_bottom_of_snapshot_box < 150) ||
				(scroll_offsets.top - container_offsets.top > 300)
		) {
			this.scroll_to_element.scrollTo();
		}

		// if we haven't called this before, or we're clicking on a "new" ngram
		// (read: as long as we're not just paginating), we want to do change the link styles
		if( !this.loaded_ngram || (this.loaded_ngram && (this.loaded_ngram != ngram)) ) {

			// if loading a new ngram to replace an old one, swap out stand-in SPANs back to As
			if( this.loaded_ngram && (this.loaded_ngram != ngram) ) this.revertSavedNgramLinks();

			// create the new set of stand-in SPANs and sub out the As
			this.createNgramLinkSubstitutes(ngram);
		}

		// fire off the AJAX request to get the reviews
		(new Ajax.Request(
			'/biz_details/snapshot_reviews', {
				method: 'get',
				parameters: {
					'biz_id': json_biz.id,
					'ngram': unescape(ngram),
					'sentence_review_id': sentence_review_id,
					'start': start,
					'nc': (new Date()).getTime()
				},
				onSuccess: this.callbacks.onSuccessReviewFetch,
				onFailure: this.callbacks.onFailureReviewFetch
			}
		));

		this.loaded_ngram = ngram;
		Event.stop(evt);
	},

	handleReviewFetchSuccess: function(transport){
		var response_xml = transport.responseXML.documentElement;
		var snippets = response_xml.getElementsByTagName('snippet');
		var ngram = response_xml.getAttribute('ngram');
		var reviews = null;
		var pager = null;
		var header = null;

		// copy out the rendered reviews and updated pager
		for( var i = 0; i < snippets.length; i++ ){
			tag_name = snippets[i].getAttribute('name');
			if( tag_name == 'reviews' ){
				reviews = snippets[i].firstChild.nodeValue;
			} else if( tag_name == 'pager' ){
				pager = snippets[i].firstChild.nodeValue;
			} else if( tag_name == 'header' ){
				header = snippets[i].firstChild.nodeValue;
			}
		}

		// if we're clicking a new link but already have ngrams loaded, do it different
		if( this.showingNgramReviews() ) {
			var old_ngram_reviews = this.getCreatedElements('ngram_reviews')[0];
			var replacing_loaded_reviews = true;
		} else {
			var replacing_loaded_reviews = false;
		}

		var ngram_reviews = new Element('div', {'class':'ngram_reviews'});
		this.clearCreatedElements('ngram_reviews', true);
		this.registerCreatedElement('ngram_reviews', ngram_reviews);

		// reviews header and handlers
		var ngram_reviews_header = new Element('div', {'class':'header'}).update(header);
		ngram_reviews_header.down('.returnToAllReviews').observe('click', this.callbacks.onClickReturnToAllReviews);
		ngram_reviews.appendChild(ngram_reviews_header);

		// actual reviews and associated event handlers
		var reviews_container = new Element('div').update(reviews);
		reviews_container.select('.review').each(function(review){
			var review_love_container = review.getElementsByClassName('rateReview')[0];
			if (review_love_container) {
				var reviews_love = new Yelp.reviewsLove(review_love_container);
			}
			Yelp.archive_snippet.attach_clicks(review);
			var link_to_this = review.down('.linkToThis');
			var prev_onclick = link_to_this.readAttribute('onclick');
			link_to_this.setAttribute('onclick', '');
			link_to_this.observe('click', this.callbacks.onClickLinkToThis.curry(prev_onclick));
		}, this);
		ngram_reviews.appendChild(reviews_container);

		// pager stuff - container and handlers
		var pager_container = new Element('div', {'class':'pagination_controls'}).update(pager);
		pager_container.select('.ngram').invoke('observe', 'click', this.callbacks.onClickNgramLink);
		ngram_reviews.appendChild(pager_container);

		// throw it on the page
		this.hideMainReviews();
		this.clearLoadingBurst();

		if( replacing_loaded_reviews ) {
			old_ngram_reviews.parentNode.insertBefore(ngram_reviews, old_ngram_reviews);
			old_ngram_reviews.remove();
		} else {
			this.main_reviews_container.parentNode.insertBefore(ngram_reviews, this.main_reviews_container);
		}
	},

	handleReviewFetchFailure: function(transport){
		this.clearLoadingBurst();
		this.removeSummaryReviews();
		this.showMainReviews();
		alert("Sorry, this feature is not working right now.  Please try again in a few minutes");
	},

	returnToAllReviews: function(evt){
		this.revertSavedNgramLinks();
		this.removeSummaryReviews();
		this.loaded_ngram = null;
		this.showMainReviews();
		Event.stop(evt);
	},

	initReviewGraphSwitch: function(evt, graph){
		if( evt ) Event.stop(evt);

		// switch header links
		if( graph == 'dist' ){
			this.graphs_header.update(Yelp._("Rating Distribution") + '&nbsp;|&nbsp;');
			var link = new Element('a', {href:'#'}).update(Yelp._("Trend"));
			link.observe('click', this.initReviewGraphSwitch.bindAsEventListener(this, 'trend'));
			this.graphs_header.appendChild(link);
		} else if( graph == 'trend' ) {
			this.graphs_header.update('&nbsp;|' + Yelp._("Trend"));
			var link = new Element('a', {href:'#', 'class':'dist_link'}).update(Yelp._("Rating Distribution"));
			link.observe('click', this.initReviewGraphSwitch.bindAsEventListener(this, 'dist'));
			this.graphs_header.insert({top: link});
		}

		// add loading graph
		if( this.graphs_loaded.indexOf(graph) < 0 ) {
			this.graphs_content.removeClassName('trend');
			this.graphs_content.removeClassName('dist');
			this.graphs_content.innerHTML = '<div class="loader"><img src="' +imagesHostUrl+"static/"+Yelp.images_serial+'/i/new/gfx/burst_loader_ani.gif" alt="loading" class="loader" />&nbsp;&nbsp;' + Yelp._("Loading...") + '</div>';

			var preloader = new Element('img');
			this.registerCreatedElement('preloader', preloader);
			document.body.appendChild(preloader);
			preloader.observe('load', this.callbacks.onLoadReviewGraphImage.curry(graph));
			preloader.src = this.graphs_urls[graph];
		} else {
			this.displayLoadedReviewGraph(graph);
		}
	},

	displayLoadedReviewGraph: function(graph){
		if( this.graphs_loaded.indexOf(graph) < 0 ) {
			this.clearCreatedElements('preloader');
			this.graphs_loaded.push(graph);
		}
		this.graphs_content.addClassName(graph);
		var graph_image = new Element('img', {height:'100', width:'145', src:this.graphs_urls[graph]});
		this.graphs_content.innerHTML = '';
		this.graphs_content.appendChild(graph_image);
	},

	linkToThisReplacementAction: function(evt, onclick){
		this.callbacks.onClickReturnToAllReviews.bind(this)(evt);
		eval(onclick);
	}
});

Yelp.BizDetailsTabMechatron = Class.create({
	initialize: function(container){
		this.container = container;
		this.callbacks = {};
		this.callbacks.onClickTab = this.switchToTab.bindAsEventListener(this);
		this.container.select('A').each(function(tab_link){
			var tab_name = tab_link.readAttribute('rel');
			tab_link.observe('click', this.callbacks.onClickTab);
		}, this);
	},

	switchToTab: function(evt){
		evt.stop();
		var selected_tab = Event.element(evt).up('li').readAttribute('rel');
		this.container.select('A').each(function(tab_link){
			var this_rel = tab_link.up('li').readAttribute('rel');
			if( this_rel == selected_tab ) {
				tab_link.up('li').addClassName('active');
				$('biz_tab_' + this_rel).show();
			} else {
				tab_link.up('li').removeClassName('active');
			  $('biz_tab_' + this_rel).hide();
			}
		});
	}
});

