/**
 * Genereric overlay toolkit class
 */
function Overlay(map, visible)
{
    this.map = map;
    this.visible = visible;
    if (visible)
        this.display();
}

Overlay.prototype = new Object();

// Triggers the displaying.
Overlay.prototype.display = function()
{
    this.visible = true;
    this.addOverlay(); // must be implemented by the super class
}

// Triggers the hiding. If disabled, there will be no visible changes.
Overlay.prototype.hide = function()
{
    this.visible = false;
    this.removeOverlay(); // must be implemented by the super class
}

// Toggles the displaying. If disabled, there will be no visible changes.
Overlay.prototype.toggle = function()
{
    if (this.visible == true) {
        this.hide();
    } else {
        this.display();
    }
}

// Gets the current state of the display/hide.
Overlay.prototype.getToggleState = function()
{
    return this.visible;
}


/**
 * Generic KML overlay class
 */
function KmlOverlay(map, url, visible)
{
    this.geoXml = new GGeoXml(url);
    this.base(map, visible);
}

KmlOverlay.prototype = new Overlay();

KmlOverlay.prototype.base = Overlay;

KmlOverlay.prototype.addOverlay = function()
{
    this.map.addOverlay(this.geoXml);
}

KmlOverlay.prototype.removeOverlay = function()
{
    this.map.removeOverlay(this.geoXml);
}

/**
 * Geometric polygon class (used in performing neighborhood boundary-related calculations)
 */
function Polygon(coordinates)
{
    this.coordinates = coordinates;
    this.centroid = this.getCentroid(coordinates);
    this.overlay = new GPolygon(coordinates, "#004a80", 3, 0.5, "#000000", 0);
    this.bounds = this.overlay.getBounds();
}

/**
 * Performs the geometric operations for determining the center of mass for the ploygon
 * Returns a GLatLng instance with the centroid's coordinates
 */
Polygon.prototype.getCentroid = function(coordinates)
{
    var area = 0;
    for (var i = 0; i < coordinates.length - 1; i++)
    {
        area = area + (coordinates[i].lat() * coordinates[i + 1].lng() - coordinates[i + 1].lat() * coordinates[i].lng());
    }
    area = area / 2;

    var cLat = 0;
    var cLng = 0;
    for (var i = 0; i < coordinates.length - 1; i++)
    {
        var x = (coordinates[i].lat() * coordinates[i + 1].lng() - coordinates[i + 1].lat() * coordinates[i].lng()) / (6 * area);
        cLat = cLat + (coordinates[i].lat() + coordinates[i + 1].lat()) * x;
        cLng = cLng + (coordinates[i].lng() + coordinates[i + 1].lng()) * x;
    }

    if (area == 0)
        return false;
    return new GLatLng(cLat, cLng);
}

/**
 * Checks if the input coordinate (GLatLng instance) falls within the polygon's boundary
 */
Polygon.prototype.contains = function(coordinates)
{
    if (! this.bounds.contains(coordinates))
        return false;

    var j = 0;
    var oddNodes = false;
    var x = coordinates.lng();
    var y = coordinates.lat();
    for (var i=0; i < this.overlay.getVertexCount(); i++) {
      j++;
      if (j == this.coordinates.length) {j = 0;}
      if (((this.coordinates[i].lat() < y) && (this.coordinates[j].lat() >= y))
      || ((this.coordinates[j].lat() < y) && (this.coordinates[i].lat() >= y))) {
        if ( this.coordinates[i].lng() + (y - this.coordinates[i].lat())
        /  (this.coordinates[j].lat() - this.coordinates[i].lat())
        *  (this.coordinates[j].lng() - this.coordinates[i].lng())<x ) {
          oddNodes = !oddNodes
        }
      }
    }
    return oddNodes;
}

/* MISC map related functions */

function view_organization(lat, long, idx){
	myMap.results.popResultAtIndexInfo(idx);
	myMap.map.panTo(new GLatLng(lat, long));
}

function update_nearby_info(request){
	var response = request.responseText;
	var xmlDoc;
	if (window.ActiveXObject){
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async = 'false';
		xmlDoc.loadXML(response);
	} else {
		var parser = new DOMParser();
		xmlDoc = parser.parseFromString(response, "text/xml");
	}
	
	var places = xmlDoc.documentElement.getElementsByTagName('place');
	var nbOfResults = places.length;
	
	if (!nbOfResults){
		// no results
		$('afta_find_nearby_error').show();
		$('afta_did_you_mean').hide();
		
	} else if (nbOfResults == 1){
		// direct hit
		$('afta_find_nearby_error').hide();
		$('afta_did_you_mean').hide();
		var lat = places[0].getElementsByTagName('lat')[0].childNodes[0].nodeValue;
		var long = places[0].getElementsByTagName('long')[0].childNodes[0].nodeValue;
		myMap.clearResults();
    setResultsNamespace("borough");
    if (myMap.limitResultsToVenueType) {
	    myMap.limitResultsToVenueType = false;
	    myMap.limitResultsToVenueId = false;
	  }
		myMap.nearbyMarker.setDirectHit(true);
		myMap.nearbyMarker.setCoords(new GLatLng(lat, long));
		myMap.nearbyMarker.show();
		myMap.map.setCenter(new GLatLng(lat, long));
		highlightNavBtn(false);
		myMap.reloadResults();
		setTimeout("myMap.map.setZoom(13)",1000);
		window.location = "#afta_content_head";
	} else {
		// did you mean?
		$('afta_find_nearby_error').hide();
		$('afta_did_you_mean').show();
		var suggestions = '';
		for (var c = 0; c < nbOfResults; c++){
			var lat = places[c].getElementsByTagName('lat')[0].childNodes[0].nodeValue;
			var long = places[c].getElementsByTagName('long')[0].childNodes[0].nodeValue;
			var address = places[c].getElementsByTagName('addr')[0].childNodes[0].nodeValue;
			suggestions += "<li><a href='#afta_content_head' onclick='show_nearby_suggestion(" + lat + ", " + long + "); return false;'>" + address + "</a></li>";
		}
		$('afta_did_you_mean_list').innerHTML = suggestions;
	}
}

function show_nearby_suggestion(lat, long){
	$('afta_find_nearby_error').hide();
	$('afta_did_you_mean').hide();
	
	myMap.nearbyMarker.setDirectHit(false);
	myMap.nearbyMarker.setCoords(new GLatLng(lat, long));
	myMap.nearbyMarker.show();
	myMap.map.panTo(new GLatLng(lat, long));
	myMap.map.setZoom(17);
	window.location = "#afta_content_head";
}

/**
 * Cross-browser script to an object. Make sure id and name are set.
 * DOES NOT ALWAYS WORK WELL WITH LAYERS
 * found at apple developer site... author unknown
 **/
function getAnObject(objectId) {
    // cross-browser function to get an object's style object given its id
    if (document.getElementById && document.getElementById(objectId)) {
        // W3C DOM
        return document.getElementById(objectId);
    } else if (document.all && document.all(objectId)) {
        // MSIE 4 DOM
        return document.all(objectId);
    } else if (document.layers && document.layers[objectId]) {
        // NN 4 DOM.. note: this won't find nested layers
        return document.layers[objectId];
    } else {
        return false;
    }
} // getAnObject


function highlight(id) {
    
    hide = getAnObject('afta_map_subways');
    hide.className = 'afta_search_links';
    hide = getAnObject('afta_map_lib');
    hide.className = 'afta_search_links';
    hide = getAnObject('afta_map_ps');
    hide.className = 'afta_search_links';

    hide = getAnObject(id);
    if (hide) {
        hide.className = 'afta_nav_select';
    }
    
}

// add waiting...
function showWaiting() {
    //MyMap.createBusy();
    myMap.busyDiv.style.visibility = 'visible';
    return;
    latlng = myMap.getCenter();
    waitingPoint = new PMarker(new PLatLng(latlng.lat(),latlng.lng()),sicon);
    waitingMarker = createMarker(waitingPoint);
    
}

function hideWaiting() {
    myMap.busyDiv.style.visibility = 'hidden';
}

function setupMapForLibSchoolMarkers() {
  if (myMap.map.getZoom() < 15) {
    myMap.map.setZoom(15);
  }
}

function setSubwayMarkers(){
    if (!subwayMarkersApplied){
        showWaiting();
        myMap.clearSchoolMarkers();
        myMap.clearLibraryMarkers();
        myMap.addSubwayMarkers();
        highlight('afta_map_subways');
        hideWaiting();
    } else {
        
        showWaiting();
        myMap.clearSubwayMarkers();
        hideWaiting();
        highlight('xxx');
    }
}

function setLibraryMarkers(){
    if (!libraryMarkersApplied){
        showWaiting();
        setupMapForLibSchoolMarkers();
        myMap.clearSchoolMarkers();
        myMap.clearSubwayMarkers();
        myMap.addLibraryMarkers();
        highlight('afta_map_lib');
        hideWaiting();
    } else { 
        showWaiting();
        myMap.clearLibraryMarkers();
        highlight('');
        hideWaiting();
        highlight('xxx');
    }
}

function setSchoolMarkers(){
    if (!schoolMarkersApplied){
        showWaiting();
        setupMapForLibSchoolMarkers();
        myMap.clearSubwayMarkers();
        myMap.clearLibraryMarkers();
        myMap.addSchoolMarkers();
        highlight('afta_map_ps');
        hideWaiting();
    } else { 
        showWaiting();
        myMap.clearSchoolMarkers();
        highlight('');
        hideWaiting();
        highlight('xxx');
    }
}

/***************************************************************
/**   LEGACY FUNCTIONS
/***************************************************************

/**
Function to create a div for school tool tip
**/
function buildToolTip(target) {
    text = "<div class='fav_tip'><strong>";
    text += target.getAttribute('name');
    text += "</strong><br />\n";
    text += target.getAttribute('address');
    text += "<br />\nNew York, NY ";
    text += target.getAttribute('zipcode');
    text += "</div>";
    return text;
}

/** 
 * Function to create a div for school popup-html
 * @param target - the PMarker with the data
 **/
function buildSchoolPophtml(target) {
    return '<div class="info_wrapper"><strong>' + target.getAttribute('name') + "</strong></div>";
    /* -- keep only the school's name
    text = "<strong>";
    text += target.getAttribute('name');
    text += "</strong><br />\n";
    text += target.getAttribute('address');
    text += "<br />\nNew York, NY ";
    text += target.getAttribute('zipcode');
    text += "<br />\n9th to 12 th grade enrollment: ";
    text += target.getAttribute('9th to 12 th grade enrollment');
    text += "<br />\npre-k to 8th enrollment: ";
    text += target.getAttribute('pre-k to 8th enrollment');
    text += "<br />\ncapacity: ";
    text += target.getAttribute('capacity');
    text += "<br />\nfacility type: ";
    text += target.getAttribute('facility type');
    text += "<br />\n";
    return text;
    */
}

/** 
 * Function to create a div for school popup-html
 * @param target - the PMarker that has the data
 **/
function buildLibraryPophtml(target) {
    text = '<div class="info_wrapper"><strong>';
    text += target.getAttribute('name');
    text += "</strong><br />\n";
    text += target.getAttribute('street');
    text += "<br />\nNew York, NY ";
    text += target.getAttribute('zipcode');
    /*text += "<br />\nCapacity: "; // -- removed capacity info
    text += target.getAttribute('capacity');*/ 
    text += "</div>\n";
    return text;
}

/**
 * This function creates a pretty looking marker based on the work done at
 * onnyturf.
 * This DOES NOT use the Point class created above. It uses a simple array
 * structure. You can delete this if you move all data to the library and
 * school data format, where you use the Point class
 * @param - point an instance of simple array
 * @param - pointdata an Array of PointData()
 * @return - an instance of GxMarker 
 * @see - gxmarker.2.js
 * @see - subwaydata.js
 **/
function createMarker(point, pointdata, hideTip) {
    var noTip = hideTip || false;
    // Create an icon, set the image correctly
    var theIcon = new GIcon(baseIcon);
    //theIcon.image = "http://67.192.109.227/map_resources/img/" + pointdata.lines[0].id + ".gif";       
    theIcon.image='';
    // This tip will be on mouse roll over. 
    var tip = "<div class='fav_tip'><strong>" +pointdata.address+"</strong><br />";
    
    // The popupText will display when they click on the icon
    var popupText = "<b>"+pointdata.address+"</b><br />";

    // Add all point data text
    for (a = 0; a < pointdata.lines.length; ++a) {
        var p = pointdata.lines[a].id.indexOf(',');
        var images = new Array();
        if (p>0) {
            images = pointdata.lines[a].id.split(',');
        } else {
            images[0] = pointdata.lines[a].id;
        }
        p = images.length;
        for (i=0;i<p;++i) {
            images[i] = images[i].replace(" ", "");
            tip += "<img src='/map_resources/" + images[i].toLowerCase() +".gif' width='23' height='24' alt='' />";
            popupText += "<img src='/map_resources/" + images[i].toLowerCase() +".gif' width='45' height='44' alt='' />";
        }
    }
    tip +="</div>";

    // create the marker
    if (noTip){
      var marker = new GxMarker(point, blankIcon, false);
    } else {
      var marker = new GxMarker(point, blankIcon, tip);
    }
    
    // add the click event
    GEvent.addListener(marker, "click", function() {
						               myMap.eW.openOnMarker(marker, popupText);
                           //marker.openInfoWindowHtml( popupText );
                           // setTimeout
    });
    return marker;
}

/**
 * Create markers from pinpoint data
 * @param point - instance of Point()
 * @param isSchool - there are currently 2 different functions for creating html popup
 * shool and library. - if we add more then this function call will have to change
 * 
 * This was created to encapsulate the logic we needed to create our makers with
 * tool tips. If you need to add other functions to the markers, like a different
 * on click, this is the place to do it.
 **/
function createGxMarker(point,isSchool) {
    var theIcon = new GIcon(baseIcon);
    
    // set the image based on the data in the point
    theIcon.image = point.icon.image;
    
    // This tip will be on mouse roll over. The popupText will be there when they click
    var tip = buildToolTip(point);
    
    // create the pop-up html text
    var popupText = "";
    if (isSchool) {
        popupText = buildSchoolPophtml(point);
    } else {
        popupText = buildLibraryPophtml(point);
    }
    
    // the point
    var apoint = new GLatLng(point.latlon.lat,point.latlon.lon);
    
    // the marker
    var marker = new GxMarker(apoint, theIcon);
    
    // open a popup if clicked.
    GEvent.addListener(marker, "click", function() {
                //marker.openInfoWindowHtml( popupText );
				myMap.eW.openOnMarker(marker, popupText);
                //setTimeout
    });
    
    return marker;
}
/**
 * Create a blank marker to use for center and zoom
 */
function createBlankPdMarker(lat, lon) {
    var apoint = new GLatLng(lat,lon);
    var imarker = new PdMarker(apoint, blankIcon, "");
    return imarker;
}

/**
 * Create a set of blank markers
 * @param array of Point()
 * @return array of PdMarker()
 **/
function createBlankMarkers(points) {
    markers = [];
    for (index = 0; index < points.length; ++index) {
        marker = createBlankPdMarker(points[index].latlon.lat,points[index].latlon.lon);
        markers[index]=marker;
        myMap.map.addOverlay(marker);
    }
    return markers;
}
/**
 * Create a set of blank markers
 * @param array of Point()
 * @return array of PdMarker()
 **/
function createBlankMarkersFromArray(points) {
    markers = [];
    for (index = 0; index < points.length; ++index) {
        marker = createBlankPdMarker(points[index].lat,points[index].lon);
        markers[index]=marker;
        myMap.map.addOverlay(marker);
    }
    return markers;
}
/**
 * Function to remove blank markers from a map
 *
 * @param array of PdMarker()
 * @return undefined
 */
function removeMarkers(markers) {
    for (index = 0; index < markers.length; ++index) {
        myMap.map.removeOverlay(markers[index]);
        markers[index]=null;
    }
}
/**
 * A function to center and zoom on an array of points
 * @param array of Point()
 */
function centerAndZoom(points) {
    markers = createBlankMarkers(points);
    myMap.map.zoomToMarkers();
    removeMarkers(markers);
    markers = null;
}
function defaultCenterAndZoom() {
  myMap.map.setCenter(new GLatLng(40.75467, -73.98382));
  myMap.map.setZoom(10);
  markers = null;
}
/**
 * Updates the results namespace for the map (all, borough, neighborhood)
 */
function setResultsNamespace(ns){
  myMap.resultsNamespace = ns || 'borough';
}
/**
 * Limit the results set to a single venue
 */
function limitResultsSet(venue_type, venue_id){
  myMap.limitResultsToVenueType = venue_type;
  myMap.limitResultsToVenueId = venue_id;
}
/**
 * Limit the results set to a collection
 */
function setCollectionResults(collection_id){
  myMap.limitResultsToCollectionId = collection_id;
}
/**
 * Updates the map's behaviour (default | search)
 */
function setMapBehaviour(type){
  myMap.mapBehaviour = type || 'default';
}

function highlightNavBtn(navBtn) {
  document.getElementById('all_nav_btn').setAttribute('class', '');
  document.getElementById('bronx_nav_btn').setAttribute('class', '');
  document.getElementById('brooklyn_nav_btn').setAttribute('class', '');
  document.getElementById('manhattan_nav_btn').setAttribute('class', '');
  document.getElementById('queens_nav_btn').setAttribute('class', '');
  document.getElementById('staten_island_nav_btn').setAttribute('class', '');
  if (navBtn) {
    document.getElementById(navBtn).setAttribute('class', 'afta_nav_select');      
  }
}

function highlightSidebarLink(lid) {
  if ($('side_borough_all')) {
    $('side_borough_all').removeClassName('afta_nav_select');
    $('side_borough_bronx').removeClassName('afta_nav_select');
    $('side_borough_brooklyn').removeClassName('afta_nav_select');
    $('side_borough_manhattan').removeClassName('afta_nav_select');
    $('side_borough_queens').removeClassName('afta_nav_select');
    $('side_borough_staten_island').removeClassName('afta_nav_select');
  }
  if (lid) {
    $(lid).addClassName('afta_nav_select');
  }
}

function switchMapStateToBorough(b) {
  if (myMap.limitResultsToVenueType) {
    myMap.limitResultsToVenueType = false;
    myMap.limitResultsToVenueId = false;
  }
  myMap.clearResults();
  if (b == 'all'){
    setResultsNamespace('all');
    defaultCenterAndZoom();
  } else {
    setResultsNamespace('borough');
    centerAndZoom(eval(b + '_boundaries_array'));
  }
  highlightNavBtn(b + '_nav_btn');
  highlightSidebarLink('side_borough_' + b);
  myMap.reloadResults();
  if (schoolMarkersApplied) {
    myMap.clearSchoolMarkers();
    setSchoolMarkers();
  } else if (libraryMarkersApplied) {
    myMap.clearLibraryMarkers();
    setLibraryMarkers();
  } else if (subwayMarkersApplied) {
    myMap.clearSubwayMarkers();
    setSubwayMarkers();
  }
}

/**
 * A function to center and zoom on array of array data
 */
function centerAndZoomSubway() {
    markers = createBlankMarkersFromArray(sbwy);
    myMap.map.zoomToMarkers();
    removeMarkers(markers);
    markers = null;
}
/**
 * Here just to be consistent
 */
function centerAndZoomSchools() {
    centerAndZoom(smarkers);
}
/**
 * Here just to be consistent
 */
function centerAndZoomLibraries() {
    centerAndZoom(lmarkers);
}

/***************************************************************
BEGIN FUNCTIONS
****************************************************************
***************************************************************
BEGIN SETUP AND START
****************************************************************/


/**
 * to do overlays, you MUST have your kml file avaiable to Google's servers
 * it won't work if the data is only local.
 **/
var geoXml = null; //new GGeoXml("http://www.myphonediet.com/data/manhattanneighborhoods.kml?x="+dt.getMilliseconds());

/**
 * This function is called when the browser loads
 **/


/**
* Javascript code to store data as JSON strings in cookies. 
* It uses prototype.js 1.5.1 (http://www.prototypejs.org)
* 
* Author : Lalit Patel
* Website: http://www.lalit.org/lab/jsoncookies
* License: Creative Commons Attribution-ShareAlike 2.5
*          http://creativecommons.org/licenses/by-sa/2.5/
* Version: 0.4
* Updated: Aug 11, 2007 10:09am
* 
* Chnage Log:
*   v 0.4
*   -  Removed a extra comma in options (was breaking in IE and Opera). (Thanks Jason)
*   -  Removed the parameter name from the initialize function
*   -  Changed the way expires date was being calculated. (Thanks David)
*   v 0.3
*   -  Removed dependancy on json.js (http://www.json.org/json.js)
*   -  empty() function only deletes the cookies set by CookieJar
*/
 
var CookieJar = Class.create();

CookieJar.prototype = {

 	/**
 	 * Append before all cookie names to differntiate them.
 	 */
 	appendString: "__CJ_",

 	/**
 	 * Initializes the cookie jar with the options.
 	 */
 	initialize: function(options) {
 		this.options = {
 			expires: 3600,		// seconds (1 hr)
 			path: '',			// cookie path
 			domain: '',			// cookie domain
 			secure: ''			// secure ?
 		};
 		Object.extend(this.options, options || {});

 		if (this.options.expires != '') {
 			var date = new Date();
 			date = new Date(date.getTime() + (this.options.expires * 1000));
 			this.options.expires = '; expires=' + date.toGMTString();
 		}
 		if (this.options.path != '') {
 			this.options.path = '; path=' + escape(this.options.path);
 		}
 		if (this.options.domain != '') {
 			this.options.domain = '; domain=' + escape(this.options.domain);
 		}
 		if (this.options.secure == 'secure') {
 			this.options.secure = '; secure';
 		} else {
 			this.options.secure = '';
 		}
 	},

 	/**
 	 * Adds a name values pair.
 	 */
 	put: function(name, value) {
 		name = this.appendString + name;
 		cookie = this.options;
 		var type = typeof value;
 		switch(type) {
 		  case 'undefined':
 		  case 'function' :
 		  case 'unknown'  : return false;
 		  case 'boolean'  : 
 		  case 'string'   : 
 		  case 'number'   : value = String(value.toString());
 		}
 		var cookie_str = name + "=" + escape(Object.toJSON(value));
 		try {
 			document.cookie = cookie_str + cookie.expires + cookie.path + cookie.domain + cookie.secure;
 		} catch (e) {
 			return false;
 		}
 		return true;
 	},

 	/**
 	 * Removes a particular cookie (name value pair) form the Cookie Jar.
 	 */
 	remove: function(name) {
 		name = this.appendString + name;
 		cookie = this.options;
 		try {
 			var date = new Date();
 			date.setTime(date.getTime() - (3600 * 1000));
 			var expires = '; expires=' + date.toGMTString();
 			document.cookie = name + "=" + expires + cookie.path + cookie.domain + cookie.secure;
 		} catch (e) {
 			return false;
 		}
 		return true;
 	},

 	/**
 	 * Return a particular cookie by name;
 	 */
 	get: function(name) {
 		name = this.appendString + name;
 		var cookies = document.cookie.match(name + '=(.*?)(;|$)');
 		if (cookies) {
 			return (unescape(cookies[1])).evalJSON();
 		} else {
 			return null;
 		}
 	},

 	/**
 	 * Empties the Cookie Jar. Deletes all the cookies.
 	 */
 	empty: function() {
 		keys = this.getKeys();
 		size = keys.size();
 		for(i=0; i<size; i++) {
 			this.remove(keys[i]);
 		}
 	},

 	/**
 	 * Returns all cookies as a single object
 	 */
 	getPack: function() {
 		pack = {};
 		keys = this.getKeys();

 		size = keys.size();
 		for(i=0; i<size; i++) {
 			pack[keys[i]] = this.get(keys[i]);
 		}
 		return pack;
 	},

 	/**
 	 * Returns all keys.
 	 */
 	getKeys: function() {
 		keys = $A();
 		keyRe= /[^=; ]+(?=\=)/g;
 		str  = document.cookie;
 		CJRe = new RegExp("^" + this.appendString);
 		while((match = keyRe.exec(str)) != undefined) {
 			if (CJRe.test(match[0].strip())) {
 				keys.push(match[0].strip().gsub("^" + this.appendString,""));
 			}
 		}
 		return keys;
 	}
};
