|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"> |
|
<head> |
|
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/> |
|
<meta http-equiv="content-type" content="text/html; charset=utf-8"/> |
|
<title>[agency]</title> |
|
<link href="file/style.css" rel="stylesheet" type="text/css" /> |
|
<style type="text/css"> |
|
v\:* { |
|
behavior:url(#default#VML); |
|
} |
|
</style> |
|
<script src="http://[host]/maps?file=api&v=2&key=[key]" type="text/javascript"></script> |
|
<script src="/file/labeled_marker.js" type="text/javascript"></script> |
|
<script src="/file/calendarpopup.js" type="text/javascript"></script> |
|
<script language="VBScript" src="/file/svgcheck.vbs"></script> |
|
<script type="text/javascript"> |
|
//<![CDATA[ |
|
var map; |
|
// Set to true when debugging for log statements about HTTP requests. |
|
var log = false; |
|
var twelveHourTime = false; // set to true to see AM/PM |
|
var selectedRoute = null; |
|
var forbid_editing = [forbid_editing]; |
|
|
|
function load() { |
|
if (GBrowserIsCompatible()) { |
|
sizeRouteList(); |
|
var map_dom = document.getElementById("map"); |
|
map = new GMap2(map_dom); |
|
map.addControl(new GLargeMapControl()); |
|
map.addControl(new GMapTypeControl()); |
|
map.addControl(new GOverviewMapControl()); |
|
map.enableScrollWheelZoom(); |
|
var bb = new GLatLngBounds(new GLatLng([min_lat], [min_lon]),new GLatLng([max_lat], [max_lon])); |
|
map.setCenter(bb.getCenter(), map.getBoundsZoomLevel(bb)); |
|
map.enableDoubleClickZoom(); |
|
initIcons(); |
|
GEvent.addListener(map, "moveend", callbackMoveEnd); |
|
GEvent.addListener(map, "zoomend", callbackZoomEnd); |
|
callbackMoveEnd(); // Pretend we just moved to current center |
|
fetchRoutes(); |
|
} |
|
} |
|
|
|
function callbackZoomEnd() { |
|
} |
|
|
|
function callbackMoveEnd() { |
|
// Map moved, search for stops near the center |
|
fetchStopsInBounds(map.getBounds()); |
|
} |
|
|
|
/** |
|
* Fetch a sample of stops in the bounding box. |
|
*/ |
|
function fetchStopsInBounds(bounds) { |
|
url = "/json/boundboxstops?n=" + bounds.getNorthEast().lat() |
|
+ "&e=" + bounds.getNorthEast().lng() |
|
+ "&s=" + bounds.getSouthWest().lat() |
|
+ "&w=" + bounds.getSouthWest().lng() |
|
+ "&limit=50"; |
|
if (log) |
|
GLog.writeUrl(url); |
|
GDownloadUrl(url, callbackDisplayStopsBackground); |
|
} |
|
|
|
/** |
|
* Displays stops returned by the server on the map. Expected to be called |
|
* when GDownloadUrl finishes. |
|
* |
|
* @param {String} data JSON encoded list of list, each |
|
* containing a row of stops.txt |
|
* @param {Number} responseCode Response code from server |
|
*/ |
|
function callbackDisplayStops(data, responseCode) { |
|
if (responseCode != 200) { |
|
return; |
|
} |
|
clearMap(); |
|
var stops = eval(data); |
|
if (stops.length == 1) { |
|
var marker = addStopMarkerFromList(stops[0], true); |
|
fetchStopInfoWindow(marker); |
|
} else { |
|
for (var i=0; i<stops.length; ++i) { |
|
addStopMarkerFromList(stops[i], true); |
|
} |
|
} |
|
} |
|
|
|
function stopTextSearchSubmit() { |
|
var text = document.getElementById("stopTextSearchInput").value; |
|
var url = "/json/stopsearch?q=" + text; // TODO URI escape |
|
if (log) |
|
GLog.writeUrl(url); |
|
GDownloadUrl(url, callbackDisplayStops); |
|
} |
|
|
|
function tripTextSearchSubmit() { |
|
var text = document.getElementById("tripTextSearchInput").value; |
|
selectTrip(text); |
|
} |
|
|
|
/** |
|
* Add stops markers to the map and remove stops no longer in the |
|
* background. |
|
*/ |
|
function callbackDisplayStopsBackground(data, responseCode) { |
|
if (responseCode != 200) { |
|
return; |
|
} |
|
var stops = eval(data); |
|
// Make a list of all background markers |
|
var oldStopMarkers = {}; |
|
for (var stopId in stopMarkersBackground) { |
|
oldStopMarkers[stopId] = 1; |
|
} |
|
// Add new markers to the map and remove from oldStopMarkers |
|
for (var i=0; i<stops.length; ++i) { |
|
var marker = addStopMarkerFromList(stops[i], false); |
|
if (oldStopMarkers[marker.stopId]) { |
|
delete oldStopMarkers[marker.stopId]; |
|
} |
|
} |
|
// Delete all markers that remain in oldStopMarkers |
|
for (var stopId in oldStopMarkers) { |
|
GEvent.removeListener(stopMarkersBackground[stopId].clickListener); |
|
map.removeOverlay(stopMarkersBackground[stopId]); |
|
delete stopMarkersBackground[stopId] |
|
} |
|
} |
|
|
|
/** |
|
* Remove all overlays from the map |
|
*/ |
|
function clearMap() { |
|
boundsOfPolyLine = null; |
|
for (var stopId in stopMarkersSelected) { |
|
GEvent.removeListener(stopMarkersSelected[stopId].clickListener); |
|
} |
|
for (var stopId in stopMarkersBackground) { |
|
GEvent.removeListener(stopMarkersBackground[stopId].clickListener); |
|
} |
|
stopMarkersSelected = {}; |
|
stopMarkersBackground = {}; |
|
map.clearOverlays(); |
|
} |
|
|
|
/** |
|
* Return a new GIcon used for stops |
|
*/ |
|
function makeStopIcon() { |
|
var icon = new GIcon(); |
|
icon.iconSize = new GSize(12, 20); |
|
icon.shadowSize = new GSize(22, 20); |
|
icon.iconAnchor = new GPoint(6, 20); |
|
icon.infoWindowAnchor = new GPoint(5, 1); |
|
return icon; |
|
} |
|
|
|
/** |
|
* Initialize icons. Call once during load. |
|
*/ |
|
function initIcons() { |
|
iconSelected = makeStopIcon(); |
|
iconSelected.image = "/file/mm_20_yellow.png"; |
|
iconSelected.shadow = "/file/mm_20_shadow.png"; |
|
iconBackground = makeStopIcon(); |
|
iconBackground.image = "/file/mm_20_blue_trans.png"; |
|
iconBackground.shadow = "/file/mm_20_shadow_trans.png"; |
|
iconBackgroundStation = makeStopIcon(); |
|
iconBackgroundStation.image = "/file/mm_20_red_trans.png"; |
|
iconBackgroundStation.shadow = "/file/mm_20_shadow_trans.png"; |
|
} |
|
|
|
var iconSelected; |
|
var iconBackground; |
|
var iconBackgroundStation; |
|
// Map from stopId to GMarker object for stops selected because they are |
|
// part of a trip, etc |
|
var stopMarkersSelected = {}; |
|
// Map from stopId to GMarker object for stops found by the background |
|
// passive search |
|
var stopMarkersBackground = {}; |
|
/** |
|
* Add a stop to the map, given a row from stops.txt. |
|
*/ |
|
function addStopMarkerFromList(list, selected, text) { |
|
return addStopMarker(list[0], list[1], list[2], list[3], list[4], selected, text); |
|
} |
|
|
|
/** |
|
* Add a stop to the map, returning the new marker |
|
*/ |
|
function addStopMarker(stopId, stopName, stopLat, stopLon, locationType, selected, text) { |
|
if (stopMarkersSelected[stopId]) { |
|
// stop was selected |
|
var marker = stopMarkersSelected[stopId]; |
|
if (text) { |
|
oldText = marker.getText(); |
|
if (oldText) { |
|
oldText = oldText + "<br>"; |
|
} |
|
marker.setText(oldText + text); |
|
} |
|
return marker; |
|
} |
|
if (stopMarkersBackground[stopId]) { |
|
// Stop was in the background. Either delete it from the background or |
|
// leave it where it is. |
|
if (selected) { |
|
map.removeOverlay(stopMarkersBackground[stopId]); |
|
delete stopMarkersBackground[stopId]; |
|
} else { |
|
return stopMarkersBackground[stopId]; |
|
} |
|
} |
|
|
|
var icon; |
|
if (selected) { |
|
icon = iconSelected; |
|
} else if (locationType == 1) { |
|
icon = iconBackgroundStation |
|
} else { |
|
icon = iconBackground; |
|
} |
|
var ll = new GLatLng(stopLat,stopLon); |
|
var marker; |
|
if (selected || text) { |
|
if (!text) { |
|
text = ""; // Make sure every selected icon has a text box, even if empty |
|
} |
|
var markerOpts = new Object(); |
|
markerOpts.icon = icon; |
|
markerOpts.labelText = text; |
|
markerOpts.labelClass = "tooltip"; |
|
markerOpts.labelOffset = new GSize(6, -20); |
|
marker = new LabeledMarker(ll, markerOpts); |
|
} else { |
|
marker = new GMarker(ll, {icon: icon, draggable: !forbid_editing}); |
|
} |
|
marker.stopName = stopName; |
|
marker.stopId = stopId; |
|
if (selected) { |
|
stopMarkersSelected[stopId] = marker; |
|
} else { |
|
stopMarkersBackground[stopId] = marker; |
|
} |
|
map.addOverlay(marker); |
|
marker.clickListener = GEvent.addListener(marker, "click", function() {fetchStopInfoWindow(marker);}); |
|
GEvent.addListener(marker, "dragend", function() { |
|
|
|
document.getElementById("edit").style.visibility = "visible"; |
|
document.getElementById("edit_status").innerHTML = "updating..." |
|
changeStopLocation(marker); |
|
}); |
|
return marker; |
|
} |
|
|
|
/** |
|
* Sends new location of a stop to server. |
|
*/ |
|
function changeStopLocation(marker) { |
|
var url = "/json/setstoplocation?id=" + |
|
encodeURIComponent(marker.stopId) + |
|
"&lat=" + encodeURIComponent(marker.getLatLng().lat()) + |
|
"&lng=" + encodeURIComponent(marker.getLatLng().lng()); |
|
GDownloadUrl(url, function(data, responseCode) { |
|
document.getElementById("edit_status").innerHTML = unescape(data); |
|
} ); |
|
if (log) |
|
GLog.writeUrl(url); |
|
} |
|
|
|
/** |
|
* Saves the current state of the data file opened at server side to file. |
|
*/ |
|
function saveData() { |
|
var url = "/json/savedata"; |
|
GDownloadUrl(url, function(data, responseCode) { |
|
document.getElementById("edit_status").innerHTML = data;} ); |
|
if (log) |
|
GLog.writeUrl(url); |
|
} |
|
|
|
/** |
|
* Fetch the next departing trips from the stop for display in an info |
|
* window. |
|
*/ |
|
function fetchStopInfoWindow(marker) { |
|
var url = "/json/stoptrips?stop=" + encodeURIComponent(marker.stopId) + "&time=" + parseTimeInput() + "&date=" + parseDateInput(); |
|
GDownloadUrl(url, function(data, responseCode) { |
|
callbackDisplayStopInfoWindow(marker, data, responseCode); } ); |
|
if (log) |
|
GLog.writeUrl(url); |
|
} |
|
|
|
function callbackDisplayStopInfoWindow(marker, data, responseCode) { |
|
if (responseCode != 200) { |
|
return; |
|
} |
|
var timeTrips = eval(data); |
|
var html = "<b>" + marker.stopName + "</b> (" + marker.stopId + ")<br>"; |
|
var latLng = marker.getLatLng(); |
|
html = html + "(" + latLng.lat() + ", " + latLng.lng() + ")<br>"; |
|
html = html + "<table><tr><th>service_id<th>time<th>name</tr>"; |
|
for (var i=0; i < timeTrips.length; ++i) { |
|
var time = timeTrips[i][0]; |
|
var tripid = timeTrips[i][1][0]; |
|
var tripname = timeTrips[i][1][1]; |
|
var service_id = timeTrips[i][1][2]; |
|
var timepoint = timeTrips[i][2]; |
|
html = html + "<tr onClick='map.closeInfoWindow();selectTrip(\"" + |
|
tripid + "\")'>" + |
|
"<td>" + service_id + |
|
"<td align='right'>" + (timepoint ? "" : "~") + |
|
formatTime(time) + "<td>" + tripname + "</tr>"; |
|
} |
|
html = html + "</table>"; |
|
marker.openInfoWindowHtml(html); |
|
} |
|
|
|
function leadingZero(digit) { |
|
if (digit < 10) |
|
return "0" + digit; |
|
else |
|
return "" + digit; |
|
} |
|
|
|
function formatTime(secSinceMidnight) { |
|
var hours = Math.floor(secSinceMidnight / 3600); |
|
var suffix = ""; |
|
|
|
if (twelveHourTime) { |
|
suffix = (hours >= 12) ? "p" : "a"; |
|
suffix += (hours >= 24) ? " next day" : ""; |
|
hours = hours % 12; |
|
if (hours == 0) |
|
hours = 12; |
|
} |
|
var minutes = Math.floor(secSinceMidnight / 60) % 60; |
|
var seconds = secSinceMidnight % 60; |
|
if (seconds == 0) { |
|
return hours + ":" + leadingZero(minutes) + suffix; |
|
} else { |
|
return hours + ":" + leadingZero(minutes) + ":" + leadingZero(seconds) + suffix; |
|
} |
|
} |
|
|
|
function parseTimeInput() { |
|
var text = document.getElementById("timeInput").value; |
|
var m = text.match(/([012]?\d):([012345]?\d)(:([012345]?\d))?/); |
|
if (m) { |
|
var seconds = parseInt(m[1], 10) * 3600; |
|
seconds += parseInt(m[2], 10) * 60; |
|
if (m[4]) { |
|
second += parseInt(m[4], 10); |
|
} |
|
return seconds; |
|
} else { |
|
if (log) |
|
GLog.write("Couldn't match " + text + " to time"); |
|
return ""; |
|
} |
|
|