
////////////////////////////////////////////////////////////////////
//
// code is placed in the public domain.
//
////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------
//
// custom (site-specific) globals
// 
//------------------------------------------------------------------

/**
 * direct data stream for votereport.us
 */
var g_strJSONURL = "http://votereport.us/reports.json";

/**
 * local wrapper for the youtube stuff.  
 */
var g_strYTJSONPath = "vyv.php" ;

//------------------------------------------------------------------
//
// constants
// 
//------------------------------------------------------------------

var MAX_MESSAGES = 256; 		

var UPDATE_DATA_SECONDS = 240; 	// (X)-second update on json - too hot?

var DEFAULT_INFO_WIDTH = 275;

var DEFAULT_YT_INFO_WIDTH = 220;

var YT_UPDATE_CYCLES = 6;

var CYCLE_DELAY_SECONDS = 7;

var TYPE =
{
	ACCESS:		0,
	ALERT:		1,
	BUG:		2,
	FORBIDDEN:	3,
	HELP:		4,
	CLOCK:		5,
	ID:			6,
	WARNING:	7,
	FLASH:		8,
	ALARM:		9,
	REDLIGHT:	10,
	WEATHER:	11,
	STOP:		12,
	NOTE:		13,
	CLIPBOARD:	14,
	SUCCESS:	15,
	VIDEO:		16,
	EMPTY:		17
};

//------------------------------------------------------------------
//
// default globals
// 
//------------------------------------------------------------------

/* map of hashtags to message "types".  the first match will
  control, so the order here is relevant (meaning if there's a
  "hava" before a "wait", "hava" will be displayed). */
var g_tagMap =
{
	challenge:		TYPE.REDLIGHT,
	hava:			TYPE.ALERT,
	access:			TYPE.ACCESS,
	registration: 	TYPE.ID,
	machine:		TYPE.BUG,
	good:			TYPE.SUCCESS,
	EP:				TYPE.ALERT,
	wait:			TYPE.CLOCK
};

/* limit to one state */
var g_limitState = "";

/* tag for callback-loading */
var g_JSONScriptTag = null;

/* flag for startup */
var g_firstLoad = true;

/* don't update yt every time; wait 5 or 6 cycles... */
var g_ytUpdateCtr = YT_UPDATE_CYCLES;

/* image filenames */
var g_iconImages = Object();

/* use yt/ryv? */
var g_fIncludeYTData = false;

/* preload image cache */
var g_imageCache = Array();

/* GIcons */
var g_iconTypes = Object();

/* the map */
var g_map = null;

/* show the "resume" control */
var g_autonotice = true;

/* whether to select randomly; this is true on rollover, false when we
  get fresh data */
var g_random = true;

/* where to start, if there's new data */
var g_newStart = -1;

/* cache of currently-displayed messages, including markers and html content */
var g_reports = Array();

/* currently displayed */
var g_currentIndex = -1;

/* the marker manager: handles hiding off-screen markers for performance */
var g_markerManager = null;

/* are we auto-running? */
var g_auto = true;

/* worried about closures & memory */
var g_moveEvtHandler = Object;

/* current window size - in case we need to shrink the info window... */
var g_windowWidth = 0;

/* last fetch time - only saved if we have a successful get */
var g_fetchTime = 0;

/* this is the saved last update, so we can use deltas on the query */
var g_lastUpdate = "19000101T00:00Z";

/* ... */
var g_lastYTYpdate = null;

/* default zoom, for parameter */
var g_defaultZoom = 5;


//------------------------------------------------------------------
//
// types
// 
//------------------------------------------------------------------

function Message()
{
	var type = TYPE.EMPTY;
	var content = "";
	var location = null;
	var marker = null;
	var uniq = "";
	var added = false;
	
	this.destroy = function()
	{
		if( this.location ) delete this.location;
		this.location = null;
		if( this.marker )
		{
			this.marker.parent = null;
			delete this.marker;
		}
		this.marker = null;
		delete this.content;
	};
}

//------------------------------------------------------------------
//
// methods
// 
//------------------------------------------------------------------

/**
 * initialize icons.  this is hardcoded to our selections, which
 * are from the Nuvola library by David Vignoni:
 * http://www.icon-king.com/projects/nuvola/
 */
function initIcons()
{
	var elt, imgroot = "images/tvr/square/";
	
	g_iconImages[TYPE.SUCCESS] = "apply.png";
	g_iconImages[TYPE.ACCESS] = "access.png";
	g_iconImages[TYPE.ALERT] = "bell.png";
	g_iconImages[TYPE.BUG] = "bug.png";
	g_iconImages[TYPE.FORBIDDEN] = "file_locked.png";
	g_iconImages[TYPE.HELP] = "help.png";
	g_iconImages[TYPE.CLOCK] = "history.png";
	g_iconImages[TYPE.ID] = "identity.png";
	g_iconImages[TYPE.WARNING] = "important.png";
	g_iconImages[TYPE.FLASH] = "irkickflash.png";
	g_iconImages[TYPE.ALARM] = "kalarm.png";
	g_iconImages[TYPE.REDLIGHT] = "ksysv.png";
	g_iconImages[TYPE.WEATHER] = "kweather.png";
	g_iconImages[TYPE.STOP] = "no.png";
	g_iconImages[TYPE.NOTE] = "package_editors.png";
	g_iconImages[TYPE.CLIPBOARD] = "tool_clipboard.png";
	g_iconImages[TYPE.VIDEO] = "camera.png";
	g_iconImages[TYPE.EMPTY] = "empty.png";
	
	// preload images
	
	for( elt in g_iconImages )
	{
		var img = new Image();
		img.src = imgroot + g_iconImages[elt];
		g_imageCache.push( img );
	}
	
	// create icons
	
	for( elt in g_iconImages )
	{
		var icon = new GIcon(G_DEFAULT_ICON);
		icon.image = imgroot + g_iconImages[elt];
		icon.iconSize = new GSize( 32, 32 );
		icon.iconAnchor = new GPoint( 16, 16 );
        icon.infoWindowAnchor = new GPoint( 20, 20 );
		icon.shadow = null;
		icon.shadowSize = new GSize( 0, 0 );
		g_iconTypes[elt] = icon;
	}
	
}

/**
 * dev: create random message, icon
 */
function createRandomMessage( map )
{
	var msg = new Message();
	var bounds = map.getBounds();
	var southWest = bounds.getSouthWest();
	var northEast = bounds.getNorthEast();
	var lngSpan = northEast.lng() - southWest.lng();
	var latSpan = northEast.lat() - southWest.lat();
	msg.type = Math.round( Math.random() * TYPE.EMPTY );
	msg.location = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random());
	var markerOptions = { icon:g_iconTypes[ msg.type ] };
	msg.marker = new GMarker( msg.location, markerOptions );
	msg.marker.parent = msg;
	msg.content = "Blah blah blah (" + msg.type + ")";
	return msg;
}

function shuffleArray( arr )
{
	var i, j, o;
	for( i = 0; i < arr.length; i++)
	{
		for( j = i; j == i; )
		{
			j = Math.floor(Math.random() * arr.length);
		}
		o = arr[i];
		arr[i] = arr[j];
		arr[j] = o;
	}
}

/**
 * resize to the iframe; the map div needs a size before we load it
 */
function setEmbeddedSize( name )
{
	var w = document.body.offsetWidth;
	var h = document.body.offsetHeight;
	var o = document.getElementById( "customcontrol" );
	var f = false;

	if( o.style.display == "block" )
	{
		o.style.display = "none";
		f = true;
	}
	
	o = document.getElementById( name );
	o.style.width = w + "px";
	o.style.height = h + "px";

	g_windowWidth = w;

	if( f )
	{
		o = document.getElementById( "customcontrol" );
		o.style.display = "block";
		o.style.top = ( document.body.offsetHeight - o.offsetHeight - 2 ) + "px";
		o.style.left = (( document.body.offsetWidth - o.offsetWidth ) / 2 ) + "px";
	}
		
}

/**
 * for automatic update/scrolling, move to the next marker and show it
 */
function showNextMarker()
{
	var msg, oldindex;
	
	clearEvtHandler();
	if( !g_auto ) return;

	// do we have any to show?
	
	if( g_reports.length <= 0 )
	{
		// keep waiting
		setTimeout( "showNextMarker()", 200 );
		return;
	}
	
	oldindex = g_currentIndex ;
	
	// this will either force down z-order, or (if the one we were displaying
	// was removed), do nothing.  handy.
	
	if( oldindex >= 0 && oldindex < g_reports.length )
	{
		g_markerManager.removeMarker( g_reports[oldindex].marker );
		g_reports[oldindex].marker.parent = null;
		delete g_reports[oldindex].marker ;
		var markerOptions = { icon:g_iconTypes[ g_reports[oldindex].type ], zIndexProcess:updateZIndexLow };
		g_reports[oldindex].marker = new GMarker( g_reports[oldindex].location, markerOptions );
		g_reports[oldindex].marker.parent = g_reports[oldindex];
		g_markerManager.addMarker( g_reports[oldindex].marker, 0 );

		// later // g_markerManager.refresh();
	}
	
	// ok, show next.  roll over.  don't worry about array size here,
	// that'll be handled in the insert function.

	if( ++g_currentIndex >= g_reports.length )
	{
		g_currentIndex = 0;
		g_random = true;
	}
	
	if( g_newStart >= 0 )
	{
		g_currentIndex = g_newStart;
		g_newStart = -1;
	}
	else if( g_random )
	{
		g_currentIndex = Math.floor( Math.random() * g_reports.length );

	}
	
	// actually, it's easier to clean up here since we don't have to worry
	// about the index being in the wrong spot...

	if( g_reports.length > MAX_MESSAGES && g_currentIndex > 0 )
	{
		var i, rcount = g_reports.length - MAX_MESSAGES ; 
		for( i = 0; i< rcount; i++ )
		{
			g_markerManager.removeMarker( g_reports[i].marker );
			g_reports[i].destroy();
			delete g_reports[i];
		}
		g_reports.splice( 0, rcount );
		g_currentIndex -= rcount;
	}
	
	/*
	if( g_currentIndex == 0 )
	{
		shuffleArray( g_reports );
	}
	*/
	
	// add to the marker manager, if necessary
	
	if( !g_reports[g_currentIndex].added )
	{
		g_reports[g_currentIndex].added = true;
		g_markerManager.addMarker( g_reports[g_currentIndex].marker, 0 );
	}
	
	// trying to force z-ordering... maybe if we pop and push back
	// we can get that to happen.
	{
		g_markerManager.removeMarker( g_reports[g_currentIndex].marker );
		g_reports[g_currentIndex].marker.parent = null;
		delete g_reports[g_currentIndex].marker ;
		var markerOptions = { icon:g_iconTypes[ g_reports[g_currentIndex].type ], zIndexProcess:updateZIndexHigh };
		g_reports[g_currentIndex].marker = new GMarker( g_reports[g_currentIndex].location, markerOptions );
		g_reports[g_currentIndex].marker.parent = g_reports[g_currentIndex];
		g_markerManager.addMarker( g_reports[g_currentIndex].marker, 0 );
		g_markerManager.refresh();
	}
	
	// now either pan or recenter, depending on the current view.  in
	// either case, we want to center the new thing.

	var bounds = g_map.getBounds();
	var southWest = bounds.getSouthWest();
	var northEast = bounds.getNorthEast();
	var lngSpan = northEast.lng() - southWest.lng();
	var latSpan = northEast.lat() - southWest.lat();

	g_map.closeInfoWindow();

	// note that with marker manager, we can't rely on the marker being
	// in the map's memory; so we need to pop info windows based on
	// the map, not the marker.  

	// don't scroll for videos...

	if( g_reports[g_currentIndex].type == TYPE.VIDEO )
	{
		// g_map.setCenter( g_reports[g_currentIndex].location );
		g_map.openInfoWindow( g_reports[g_currentIndex].location, g_reports[g_currentIndex].content );
	}
	else if( g_reports[g_currentIndex].location.lng() >= northEast.lng() - lngSpan
	   && g_reports[g_currentIndex].location.lng() <= southWest.lng() + lngSpan
	   && g_reports[g_currentIndex].location.lat() >= northEast.lat() - latSpan
	   && g_reports[g_currentIndex].location.lat() <= southWest.lat() + latSpan )
	{
		g_map.panTo( g_reports[g_currentIndex].location );
		g_moveEvtHandler.marker = g_reports[g_currentIndex].marker;
		g_moveEvtHandler.handle = GEvent.addListener( g_map, "moveend", function(){
			if( g_moveEvtHandler.marker && g_moveEvtHandler.marker.parent )
			{
				g_map.openInfoWindow( g_moveEvtHandler.marker.parent.location, g_moveEvtHandler.marker.parent.content );
			}
			clearEvtHandler();
		});
	}
	else
	{
		g_map.setCenter( g_reports[g_currentIndex].location );
		g_map.openInfoWindow( g_reports[g_currentIndex].location, g_reports[g_currentIndex].content );
	}
	// longer delay
	
	setTimeout( "showNextMarker()", CYCLE_DELAY_SECONDS * 1000 );

}

/**
 * remove the scroll event handler.  again watching out
 * for closures.
 */
function clearEvtHandler()
{
	if( g_moveEvtHandler.handle )
	{
		GEvent.removeListener( g_moveEvtHandler.handle );
	}
	g_moveEvtHandler.handle = null;
	g_moveEvtHandler.ref = null;
}

/**
 * z-index function for markers.  our aim here is to ensure that
 * the current "hot" marker, if we're animating, is on top of
 * the z-order.
 */
function updateZIndexLow( a )
{
	return 100;
}

/**
 * maybe we can force this...
 */
function updateZIndexHigh( a )
{
	return 999;
}

/**
 * create a youtube link
 */
function createYTMessage( data )
{
	var msg = new Message();
	var w = DEFAULT_YT_INFO_WIDTH;

	msg.type = TYPE.VIDEO;
	
	msg.location = new GLatLng( data.lat, data.lng );

	/*
	msg.content = "<div><div>" + data.title + "</div><div class='videoembed'>" 
		+ "<object type='application/x-shockwave-flash' data='http://www.youtube.com/v/" + data.ytid + "&autoplay=0&border=0&rel=0' width='256' height='234'>"
		+ "<param name='movie' value='http://www.youtube.com/v/" + data.ytid + "&autoplay=0&border=0&rel=0'>"
		+ "<param name='wmode' value='transparent'>"
		+ "<param name='loop' value='false'>"
		+ "<embed src='http://www.youtube.com/v/" + data.ytid + "&autoplay=0&border=0&rel=0' type='application/x-shockwave-flash' wmode='transparent' loop='false' width='256' height='234'></embed>"
		+ "</object>"
		+ "</div></div>";
	*/

	w = Math.min( w, g_windowWidth - 20 );

	msg.content = "<div class='info' style='width:" + w + "px;'>"
		+ "<div class='videotitle'>" + data.title + "</div><div class='videolink'>"
		+ "<a target='_blank' href='http://www.youtube.com/watch?v="
		+ data.ytid + "'><img src='http://i3.ytimg.com/vi/"
		+ data.ytid + "/default.jpg'/></a></div></div>";


	// {"ytid":"91aSXVLcQtk","title":"Have your say! Vote absentee!","user":"crncin","cat":"Early Voting","lat":"40.551216","lng":"-85.602364"},
	
	var markerOptions = { icon:g_iconTypes[ msg.type ], zIndexProcess:updateZIndexLow };
	msg.marker = new GMarker( msg.location, markerOptions );
	msg.marker.parent = msg;
	
	return msg;
}	

/**
 * turn one of the json reports into some html and a marker
 * for the map.  if there's no location, this should return
 * null (since we can't use it on the map)
 */
function createReportMessage( report )
{
	var msg;
	var r, t, w = DEFAULT_INFO_WIDTH;
	var str = "";
	var name = "";
	
	// nutty
	
	if( !report.location
	   || !report.location.location
	   || !report.location.location.point
	   || !report.location.location.point.coordinates ) return null;
	
	msg = new Message();
	
	// type, from the content of the message?  we're aware of a couple
	// of possible tags... default is "note"
	
	msg.type = TYPE.NOTE ;
	if( report.text )
	{
		for( t in g_tagMap )
		{
			var s = "#" + t;
			r = new RegExp( "#" + t );
			if( report.text.match( r ))
			{
				msg.type = g_tagMap[t];
				break;
			}
		}
	}
	
	// location, if available.  what's up with this structure?
	
	if( report.location )
	{
		if( report.location.location && report.location.location.point && report.location.location.point.coordinates )
		{
			msg.location = new GLatLng(report.location.location.point.coordinates[1], report.location.location.point.coordinates[0]);
		}
		// else alert( "No point? name is " + report.name );
	}
	
	var markerOptions = { icon:g_iconTypes[ msg.type ], zIndexProcess:updateZIndexLow };
	msg.marker = new GMarker( msg.location, markerOptions );
	msg.marker.parent = msg;
	
	w = Math.min( w, g_windowWidth - 20 );
	
	if( report.icon ) str += "<img align='left' class='embeddedimg' src='" + report.icon + "'/>";
	if( report.text ) str += report.text;
	
	if( report.name ) name += report.name;
	if( report.location && report.location.location && report.location.location.address )
	{
		if( name == "" ) name = report.location.location.address;
		else name += " in " + report.location.location.address;
	}
	
	msg.content = "<div class='info' style='width:" + w + "px;'>"
		+ "<div class='infomessage'>" + str + "</div>"
		+ "<div class='infoname'>" + name + "</div></div>";

	return msg;
	
}

/**
 * parse the youtube data
 */
function parseYTData( content, requestStatus )
{
	var m = false;
	if( requestStatus == 200 )
	{
		var i, j, o, uniq, data = JSON.parse( content );
		var exists;
		
		//if( data.length > 0 ) shuffleArray( data );
		
		for( i = 0; i< data.length; i++ )
		{
			// we have to construct a unique ID here (FIXME: add unique IDs to reports)
			uniq = data[i].ytid;
			
			// this is an array to maintain ordering, so the check is a bit rough
			exists = false;
			for( j = 0; !exists && j< g_reports.length; j++ )
			{
				if( g_reports[j].uniq == uniq ) exists = true;
			}
			
			// add if necessary
			if( !exists )
			{
				var msg = createYTMessage( data[i] );
				if( msg )
				{
					msg.uniq = uniq;
					g_reports.push( msg );
				}
			}
				
		}
		
		// new: we're adding markers immediately, even though scrollaround
		// might take a while.  this is to dress up the map.
		
		for( i = 0; i< g_reports.length; i++ )
		{
			if( !g_reports[i].added )
			{
				g_reports[i].added = true;
				g_markerManager.addMarker( g_reports[i].marker, 0 );
			}
		}
		g_markerManager.refresh();
		

	}

	// step 2...

	fetchTVRData();

}

/**
 * parse the json source.  this is the callback for the fetch method.
 *
 * update: switched to the callback method
 */
function parseSourceData( content )
{
	var e;
	var nctr = 0;
	
	// catch bad data...
	
	try{ 
	
	//if( requestStatus == 200 )
	{
		var i, j, o, uniq, data = content ; // = JSON.parse( content );
		var exists;
		var newdata = false;
		
		// alert( "pulled in " + data.length );
		
		// data is newest-first, so we should invert (because we're looping
		// and pushing).  that should get the oldest _update_ to the bottom
		// of the current stack...
				
		data.reverse();
				
		// we want to set the pointer so new data gets displayed sooner
		// rather than later.  hold on to this for a moment...
				
		var arrayTop = g_reports.length;
				
		for( i = 0; i< data.length; i++ )
		{
			if( data[i].report && data[i].report.location )
			{
				// we have to construct a unique ID here (FIXME: add unique IDs to reports)
				uniq = MD5( JSON.stringify( data[i].report ));
				
				// this is an array to maintain ordering, so the check is a bit rough
				exists = false;
				for( j = 0; !exists && j< g_reports.length; j++ )
				{
					if( g_reports[j].uniq == uniq ) exists = true;
				}
				
				// add if necessary
				if( !exists )
				{
					var msg = createReportMessage( data[i].report );
					if( msg )
					{
						msg.uniq = uniq;
						g_reports.push( msg );
					}
					nctr++;
					newdata = true; 

				}
				
			}
		}
		
		// new: we're adding markers immediately, even though scrollaround
		// might take a while.  this is to dress up the map.
		
		for( i = 0; i< g_reports.length; i++ )
		{
			if( g_reports[i] && !g_reports[i].added )
			{
				g_reports[i].added = true;
				g_markerManager.addMarker( g_reports[i].marker, 0 );
			}
		}
		g_markerManager.refresh();
		
		// set the pointer - if we're not walking through new data already
		
		//if( newdata && g_random )
		if( newdata )
		{
			g_random = false;
			g_newStart = arrayTop - 1;
		}
		
		g_lastUpdate = g_fetchTime ;
		
	}
	
	}
	catch( e ){}
	
	// update in (X) seconds
	
	// changed to fail-many
	// setTimeout( "updateSourceData()", UPDATE_DATA_SECONDS * 1000 );
	if( nctr > 0 )
	{
		showLoading( nctr + " update" + ( nctr > 1 ? "s" : "" ));
		setTimeout( "hideLoading()", 1000 );
	}
	else
	{
		hideLoading();
	}

}

function showLoading()
{
	var o = document.getElementById( "preload" );
	o.style.display = "block";
	
	if( arguments.length == 1 )
	{
		o.innerHTML = arguments[0];
		o.style.top = (( document.body.offsetHeight - o.offsetHeight ) - 2 ) + "px";
		o.style.left = ( 2 ) + "px";
	}
	else
	{
		o.style.top = (( document.body.offsetHeight - o.offsetHeight ) / 2 ) + "px";
		o.style.left = (( document.body.offsetWidth - o.offsetWidth ) / 2 ) + "px";
	}
}

function hideLoading()
{
	document.getElementById( "preload" ).style.display = "none";
}

/**
 * create an iso8601 date string.  there's got to be a better way
 * of doing this, right?
 */
function createISODate()
{
	var t, s, d = new Date();
	s = d.getFullYear() + "-" ;
	t = d.getUTCMonth() + 1;
	s += ( t < 10 ? "0" + t : t ) + "-";
	t = d.getUTCDate();
	s += ( t < 10 ? "0" + t : t ) + "T";
	t = d.getUTCHours();
	s += ( t < 10 ? "0" + t : t ) + ":";
	t = d.getUTCMinutes();
	s += ( t < 10 ? "0" + t : t ) + "Z";
	return s;
}

/**
 * update zoom
 */
function updateZoom( to )
{
	g_map.closeInfoWindow();
	g_map.setZoom( Number(to) );
}

/**
 * reset; mostly for changing parameters on the fly
 */
function resetAllData()
{
	g_auto = false;
	clearEvtHandler();
	g_map.closeInfoWindow();
	g_markerManager.clearMarkers();
	
	var i, rcount = g_reports.length ; 
	for( i = 0; i< rcount; i++ )
	{
		g_markerManager.removeMarker( g_reports[i].marker );
		g_reports[i].destroy();
		delete g_reports[i];
	}
	g_reports = Array();

	g_ytUpdateCtr = YT_UPDATE_CYCLES;
	g_random = true;
	g_newStart = -1;
	g_currentIndex = -1;
	g_fetchTime = 0;
	g_lastUpdate = "19000101T00:00Z";
	g_lastYTYpdate = null;
	g_firstLoad = true;
	
	updateSourceData();
	
}

/**
 * start the auto-update cycle.
 */
function startAuto()
{
	g_auto = true;
	setTimeout( "showNextMarker()", 1000 ); 
}

function loadScript( url )
{
	if( g_JSONScriptTag )
	{
		g_JSONScriptTag.parentNode.removeChild( g_JSONScriptTag );
	}
	g_JSONScriptTag = document.createElement( "script" );
	g_JSONScriptTag.src = url;
	document.body.parentNode.appendChild( g_JSONScriptTag );
}

function fetchTVRData()
{
	var url = g_strJSONURL + "?callback=parseSourceData";
	
	if( g_firstLoad )
	{
		g_firstLoad = false;
		url += "&per_page=200";
	}
	else
	{
		showLoading( "Updating data..." );
		url += "&per_page=25";
	}
	
	if( g_limitState != "" )
	{
		url += "&state=" + g_limitState ;
	}
	
	g_fetchTime = createISODate();

	// is this working?  looks like no...
	//if( g_lastUpdate ) url += "&dtstart=" + g_lastUpdate ;
	
	url += "&r=" + Math.random();
	
	loadScript( url );
	// GDownloadUrl( url, parseSourceData );
}

function updateSourceData()
{
	var url = g_strYTJSONPath ;
	if( g_fIncludeYTData )
	{
		if( g_ytUpdateCtr++ >= YT_UPDATE_CYCLES )
		{
			g_ytUpdateCtr = 0;
			GDownloadUrl( url, parseYTData );
			setTimeout( "updateSourceData()", UPDATE_DATA_SECONDS * 1000 );
			return;
		}
	}
	fetchTVRData();
	setTimeout( "updateSourceData()", UPDATE_DATA_SECONDS * 1000 );
}

/**
 * turn on automatic updates if they've been disabled.  also
 * hides the "restart" control.
 */
function restartAuto()
{
	var o = document.getElementById( "customcontrol" );
	o.style.display = "none";
	startAuto();
}

/**
 * stop automatic updating, and then add a custom control to
 * restart it
 */
function disableAutoUpdates()
{
	if( g_autonotice )
	{
		var o = document.getElementById( "customcontrol" );
		o.style.display = "block";
		o.style.top = ( document.body.offsetHeight - o.offsetHeight - 2 ) + "px";
		o.style.left = (( document.body.offsetWidth - o.offsetWidth ) / 2 ) + "px";
	}
	g_auto = false;
}

/**
 * quick arguments, we only need a couple
 */
function parseArgs()
{
	var q, p, a, i, h = document.location.href;
	q = h.replace( /^.*?\?/, "" );
	a = q.split( "&" );
	for( i = 0; i< a.length; i++ )
	{
		p = a[i].split( "=" );
		if( p.length == 2 )
		{
			if( p[0] == "z" ) g_defaultZoom = p[1];
			else if( p[0] == "auto" )
			{
				if( p[1] == "off" )
				{
					g_auto = false;
					g_autonotice = false;
				}
			}
			else if( p[0] == "state" ) g_limitState = p[1];
		}
	}
}

/**
 * callback init function
 */
function initialize()
{
	setEmbeddedSize( "map" );
	initIcons();
	parseArgs();

	showLoading();

	// init map, marker manager.  (mm doesn't work with new
	// namespaced apis?)
	
    g_map = new GMap2(document.getElementById("map"));
	g_map.setCenter(new GLatLng(37.4419, -122.1419), g_defaultZoom );
	g_markerManager = new MarkerManager( g_map );

	// clicking pauses automatic updates...

	GEvent.addListener( g_map, "click", function( marker ){
		if( marker && marker.parent ) marker.openInfoWindow( marker.parent.content );
		if( g_auto )
		{
			disableAutoUpdates();
			clearEvtHandler();
		}
	});
	
	// also dragging - should consolidate...
	
	GEvent.addListener( g_map, "dragstart", function(){
		if( g_auto )
		{
			disableAutoUpdates();
			clearEvtHandler();
		}
	});

	// first-time load

	setTimeout( "updateSourceData()", 1000 );
	if( g_auto ) startAuto();
	
}


