From 57c00dfceb3547e7fbc9fffd8e85d7942d557bb5 Mon Sep 17 00:00:00 2001
From: Anton Sarukhanov <code@ant.sr>
Date: Tue, 14 Jul 2015 13:30:22 -0400
Subject: [PATCH] Moved agency-specific stuff out of code and into config.

---
 ajax.php      | 229 +++++++++---------
 css/style.css |  29 +--
 functions.php |   8 +-
 index.php     | 105 ++++----
 js/script.js  | 650 +++++++++++++++++++++++---------------------------
 5 files changed, 482 insertions(+), 539 deletions(-)

diff --git a/ajax.php b/ajax.php
index 718b89b..0540de8 100644
--- a/ajax.php
+++ b/ajax.php
@@ -1,134 +1,127 @@
 <?php
 /*
-	This file serves up data for asynchronous requests (usually as JSON).
+    This file serves up data for asynchronous requests (usually as JSON).
 */
 
+require_once('config.php');
+require_once('functions.php');
+
 if (!isset($_GET['command'])) die();
 
 $agency = "rutgers";
 
 if ($_GET['command'] == "vehicleLocations") {
-	// return a JSON array of vehicles and their locations.
-	
-	require_once('config.php');
-	require_once('functions.php');
-	
-	$db = dbc();
-
-	// MAX age of a bus location to return
-	$maxAge = 120;
-	
-	$time = 0;
-	if (isset($_GET['t'])) $time = $db->real_escape_string((int)$_GET['t']);
-	if ($time < $maxAge) $time = time() - $maxAge;
-	$sql = <<< V_LOC
-		SELECT
-		`id`, `title` AS `route`, `routeTag`, `dirTag`, `lat`, `lon`, `heading`, `speedKmHr`
-		FROM `vehicleLocations`
-		LEFT JOIN `routes` ON `vehicleLocations`.`routeTag` = `routes`.`tag`
-		WHERE `time` > '{$time}'
+    // return a JSON array of vehicles and their locations.
+
+    $db = dbc();
+
+    // MAX age of a bus location to return
+    $maxAge = 120;
+
+    $time = 0;
+    if (isset($_GET['t'])) $time = $db->real_escape_string((int)$_GET['t']);
+    if ($time < $maxAge) $time = time() - $maxAge;
+    $sql = <<< V_LOC
+        SELECT
+        `id`, `title` AS `route`, `routeTag`, `dirTag`, `lat`, `lon`, `heading`, `speedKmHr`
+        FROM `vehicleLocations`
+        LEFT JOIN `routes` ON `vehicleLocations`.`routeTag` = `routes`.`tag`
+        WHERE `time` > '{$time}'
 V_LOC;
-	$result = dbq($db, $sql);
-
-	$locations = array();
-	while($row = $result->fetch_assoc()) $locations[$row['id']] = $row;
-	
-	// predictions
-	$sql = <<< V_LOC
-		SELECT
-		`predictions`.`vehicle`, `stops`.`title`, `predictions`.`epochTime`
-		FROM `predictions`
-		LEFT JOIN `stops` ON `predictions`.`stopTag` = `stops`.`tag`
-		ORDER BY `epochTime` ASC
+    $result = dbq($db, $sql);
+
+    $locations = array();
+    while($row = $result->fetch_assoc()) $locations[$row['id']] = $row;
+
+    // predictions
+    $sql = <<< V_LOC
+        SELECT
+        `predictions`.`vehicle`, `stops`.`title`, `predictions`.`epochTime`
+        FROM `predictions`
+        LEFT JOIN `stops` ON `predictions`.`stopTag` = `stops`.`tag`
+        ORDER BY `epochTime` ASC
 V_LOC;
-	$result = dbq($db, $sql);
-	
-	while($row = $result->fetch_assoc()) if (isset($locations[$row['vehicle']])) $locations[$row['vehicle']]['stops'][] = $row;
-	
-	foreach ($locations as $key => $location) {
-		$locations[$key]['route'] = cleanRouteName($location['route']);
-		$locations[$key]['stops'] = array_slice($location['stops'], 0, 8);
-	}
-	
-	$lastFetched = 0;
-	$result = dbq($db, "SELECT `time` FROM `lastChecked` WHERE `selector` = 'vehicleLocations' LIMIT 1", true);
-	if ($result->num_rows == 1) {
-		$arr = $result->fetch_array();
-		$lastFetched = $arr[0];
-	}
-	
-	// also return lastTime!
-	header("Content-type: application/json");
-	echo json_encode(array(
-		"vehicles" => $locations,
-		"lastTime" => $lastFetched,
-	));
-	
+    $result = dbq($db, $sql);
+
+    while($row = $result->fetch_assoc()) if (isset($locations[$row['vehicle']])) $locations[$row['vehicle']]['stops'][] = $row;
+
+    foreach ($locations as $key => $location) {
+        $locations[$key]['route'] = cleanRouteName($location['route']);
+        $locations[$key]['stops'] = array_slice($location['stops'], 0, 8);
+    }
+
+    $lastFetched = 0;
+    $result = dbq($db, "SELECT `time` FROM `lastChecked` WHERE `selector` = 'vehicleLocations' LIMIT 1", true);
+    if ($result->num_rows == 1) {
+        $arr = $result->fetch_array();
+        $lastFetched = $arr[0];
+    }
+
+    // also return lastTime!
+    header("Content-type: application/json");
+    echo json_encode(array(
+        "vehicles" => $locations,
+        "lastTime" => $lastFetched,
+    ));
+
 } elseif ($_GET['command'] == "routeConfig") {
-	// return a JSON array of routes and stops.
-	require_once('config.php');
-	require_once('functions.php');
-	
-	$db = dbc();
-
-	$sql = "SELECT `tag`, `title` FROM `routes`";
-	$result = dbq($db, $sql);
-	$routes = array();
-	while($row = $result->fetch_assoc()) $routes[$row['tag']] = $row;
-	
-	$sql = "SELECT `tag`, `title`, `lat`, `lon`, `stopId` FROM `stops`";
-	$result = dbq($db, $sql);
-	$stops = array();
-	while($row = $result->fetch_assoc()) {
-		$stops[$row['tag']] = $row;
-		$stops[$row['tag']]['routes'] = array();
-	}
-	
-	$sql = "SELECT `route`, `stop` FROM `route_stops`";
-	$result = dbq($db, $sql);
-	while($row = $result->fetch_assoc()) $stops[$row['stop']]['routes'][$row['route']] = array();
-	
-	$sql = "SELECT `routes`.`tag`, `routes`.`title`, `stopTag`, `epochTime`, `vehicle` FROM `predictions` LEFT JOIN `routes` ON `predictions`.`routeTag` = `routes`.`tag` WHERE `routes`.`agency` = '{$agency}'";
-	$result = dbq($db, $sql);
-	while($row = $result->fetch_assoc()) $stops[$row['stopTag']]['routes'][$row['tag']][] = array("epochTime" => $row['epochTime'], "vehicle" => $row['vehicle']);
-	
-	// post proc
-	foreach ($routes as $key => $route) {
-		$routes[$key]['title'] = cleanRouteName($route['title']);
-	}
-	$unset = array();
-	foreach ($stops as $key => $stop) {
-		if (!in_array($key, $unset)) {
-			foreach ($stops as $key2 => $stop2) {
-				if ($key !== $key2 && $stop['title'] == $stop2['title']) {
-					$stops[$key]['routes'] = array_merge($stop['routes'], $stop2['routes']);
-					$unset[] = $key2;
-				}
-			}
-		}
-	}
-	foreach ($unset as $un) unset($stops[$un]);
-	
-	$lastFetched = 0;
-	$result = dbq($db, "SELECT `time` FROM `lastChecked` WHERE `selector` = 'routeConfig' LIMIT 1", true);
-	if ($result->num_rows == 1) {
-		$arr = $result->fetch_array();
-		$lastFetched = $arr[0];
-	}
-	
-	// also return lastTime!
-	header("Content-type: application/json");
-	echo json_encode(array(
-		"routes" => $routes,
-		"stops" => $stops,
-		"lastTime" => $lastFetched,
-	));
-}
+    // return a JSON array of routes and stops.
+
+    $db = dbc();
+
+    $sql = "SELECT `tag`, `title` FROM `routes`";
+    $result = dbq($db, $sql);
+    $routes = array();
+    while($row = $result->fetch_assoc()) $routes[$row['tag']] = $row;
+
+    $sql = "SELECT `tag`, `title`, `lat`, `lon`, `stopId` FROM `stops`";
+    $result = dbq($db, $sql);
+    $stops = array();
+    while($row = $result->fetch_assoc()) {
+        $stops[$row['tag']] = $row;
+        $stops[$row['tag']]['routes'] = array();
+    }
 
-function cleanRouteName($name) {
-	if ($name == "New Brunsquick 1 Shuttle") $name = "NB 1";
-	else if ($name == "New Brunsquick 2 Shuttle") $name = "NB 2";
-	return $name;
+    $sql = "SELECT `route`, `stop` FROM `route_stops`";
+    $result = dbq($db, $sql);
+    while($row = $result->fetch_assoc()) $stops[$row['stop']]['routes'][$row['route']] = array();
+
+    $sql = "SELECT `routes`.`tag`, `routes`.`title`, `stopTag`, `epochTime`, `vehicle` FROM `predictions` LEFT JOIN `routes` ON `predictions`.`routeTag` = `routes`.`tag` WHERE `routes`.`agency` = '{$agency}'";
+    $result = dbq($db, $sql);
+    while($row = $result->fetch_assoc()) $stops[$row['stopTag']]['routes'][$row['tag']][] = array("epochTime" => $row['epochTime'], "vehicle" => $row['vehicle']);
+
+    // post proc
+    foreach ($routes as $key => $route) {
+        $routes[$key]['title'] = cleanRouteName($route['title']);
+    }
+    $unset = array();
+    foreach ($stops as $key => $stop) {
+        if (!in_array($key, $unset)) {
+            foreach ($stops as $key2 => $stop2) {
+                if ($key !== $key2 && $stop['title'] == $stop2['title']) {
+                    $stops[$key]['routes'] = array_merge($stop['routes'], $stop2['routes']);
+                    $unset[] = $key2;
+                }
+            }
+        }
+    }
+    foreach ($unset as $un) unset($stops[$un]);
+
+    $lastFetched = 0;
+    $result = dbq($db, "SELECT `time` FROM `lastChecked` WHERE `selector` = 'routeConfig' LIMIT 1", true);
+    if ($result->num_rows == 1) {
+        $arr = $result->fetch_array();
+        $lastFetched = $arr[0];
+    }
+
+    // also return lastTime!
+    header("Content-type: application/json");
+    echo json_encode(array(
+        "routes" => $routes,
+        "stops" => $stops,
+        "lastTime" => $lastFetched,
+    ));
 }
 
+
 ?>
diff --git a/css/style.css b/css/style.css
index de51d53..c47f4db 100644
--- a/css/style.css
+++ b/css/style.css
@@ -17,8 +17,6 @@ div#topBar {
 	display: table;
 	width: 100%;
 	z-index: 1;
-	
-	
 	background-color: #222;
 	border-top: 2px solid #555;
 	border-bottom: 2px solid #555;
@@ -240,14 +238,15 @@ a.tooltip {
 screen and (-webkit-min-device-pixel-ratio: 1.5),
 screen and (-moz-min-device-pixel-ratio: 1.5),
 screen and (min-device-pixel-ratio: 1.5) {
+    html {
+        font-size: 0.5em;
+        line-height: 0.5em;
+    }
 	div#routeselector {
 		width: 100%;
-		min-height: 56px;
-		line-height:56px;
 	}
-	
+
 	div#routeselector .bar {
-		font-size: 20px; /* 1em */
 		width: 10em;
 		height: 1em;
 		position: relative;
@@ -255,22 +254,12 @@ screen and (min-device-pixel-ratio: 1.5) {
 	}
 
 	div#routeselector .routeBtn {
-		font-size: 1.5em;
 		margin-bottom: 0px;
 	}
-	
+
 	div#lastUpdate {
 		display:none !important;
 	}
-	
-	div#locationButton {
-		width: 64px;
-		height: 64px;
-		background-size: 64px 64px;
-	}
-	
-	.leaflet-popup-content {
-		font-size: 2em;
-	}
-	
-}
\ No newline at end of file
+
+
+}
diff --git a/functions.php b/functions.php
index 3c8ec2a..cc4ae5e 100644
--- a/functions.php
+++ b/functions.php
@@ -25,9 +25,15 @@ function dbq($db, $query, $dieOnError = false) {
 	} else return $result;
 }
 
+function cleanRouteName($name) {
+	if ($name == "New Brunsquick 1 Shuttle") $name = "NB 1";
+	else if ($name == "New Brunsquick 2 Shuttle") $name = "NB 2";
+	return $name;
+}
+
 function sanitize($string) {
 	$pattern = "/[^\w.-\s]/";
 	return preg_replace($pattern,"",$string);
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/index.php b/index.php
index b7fa9bb..742f4a8 100644
--- a/index.php
+++ b/index.php
@@ -1,56 +1,63 @@
+<?php
+require_once('config.php');
+?>
 <!doctype html>
 <html lang="en">
 <head>
-	<title>RU Bus Map</title>
-	<meta name="description" content="Real-time animated map of the bus system at Rutgers University New Brunswick.">
-	<meta charset="utf-8">
-	<meta name="viewport" content="width=device-width; initial-scale=0.5; maximum-scale=0.5; user-scalable=no;" />
-	<meta name="apple-mobile-web-app-capable" content="yes" />
-	<meta name="apple-mobile-web-app-status-bar-style" content="black" />
-	<link href="css/leaflet.css" rel="stylesheet" />
-	<link href="css/leaflet.label.css" rel="stylesheet" />
-	<link href="css/style.css" rel="stylesheet" />
-	<?php if (isset($_GET['embed'])) { ?>
-		<link href="css/embed.css" rel="stylesheet" />
-	<?php } ?>
-	<script src="js/fastclick.js"></script>
-	<script src="js/jquery.min.js"></script>
-	<script src="js/leaflet.js"></script>
-	<script src="js/leaflet.marker.rotate.js"></script>
-	<script src="js/leaflet.label.js"></script>
-	<script src="js/jquery.raptorize.js"></script>
-	<script src="js/script.js"></script>
-	<?php
-		if (isset($_GET['centerLat']) && isset($_GET['centerLon'])) {
-			$lat = doubleval($_GET['centerLat']);
-			$lon = doubleval($_GET['centerLon']);
-			if (isset($_GET['zoom'])) $zoom = intval($_GET['zoom']);
-			else $zoom = 'undefined';
-			echo '<script>$(document).ready(function(){ window.map.setView(['.$lat.','.$lon.'], '.$zoom.'); });</script>';
-		}
-	?>
-	
+    <title><?php echo $cfg['agency']['name']; ?></title>
+    <meta name="description" content=<?php echo json_encode($cfg['agency']['description']); ?>>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; user-scalable=no;" />
+    <meta name="apple-mobile-web-app-capable" content="yes" />
+    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+    <link href="css/leaflet.css" rel="stylesheet" />
+    <link href="css/leaflet.label.css" rel="stylesheet" />
+    <link href="css/style.css" rel="stylesheet" />
+<?php if (isset($_GET['embed'])): ?>
+    <link href="css/embed.css" rel="stylesheet" />
+<?php endif; ?>
+    <script>
+        window.agency = <?php echo json_encode($cfg['agency']['name']); ?>;
+        window.busIconUrl = <?php echo json_encode($cfg['agency']['busIconUrl'], JSON_UNESCAPED_SLASHES); ?>;
+    </script>
+    <script src="js/fastclick.js"></script>
+    <script src="js/jquery.min.js"></script>
+    <script src="js/leaflet.js"></script>
+    <script src="js/leaflet.marker.rotate.js"></script>
+    <script src="js/leaflet.label.js"></script>
+    <script src="js/jquery.raptorize.js"></script>
+    <script src="js/script.js"></script>
+    <script>
+    $(document).ready(function() {
+        // setup and create tile layer on map
+        L.tileLayer(<?php echo json_encode($cfg['agency']['tileUrl'], JSON_UNESCAPED_SLASHES); ?>, {
+            subdomains: <?php echo json_encode($cfg['agency']['tileSubdomains']); ?>,
+            tileset: <?php echo json_encode($cfg['agency']['tileset']); ?>,
+            errorTileUrl: <?php echo json_encode($cfg['agency']['errorTileUrl'], JSON_UNESCAPED_SLASHES); ?>,
+            attribution: <?php echo json_encode($cfg['agency']['attribution'], JSON_UNESCAPED_SLASHES); ?>,
+            detectRetina: true,
+        }).addTo(window.map);
+    });
+    </script>
+    <?php
+        // Set initial lat/lon if requested.
+        if (isset($_GET['centerLat']) && isset($_GET['centerLon'])) {
+            // sanitize user inputs by converting them to doubles/ints.
+            $lat = doubleval($_GET['centerLat']);
+            $lon = doubleval($_GET['centerLon']);
+            if (isset($_GET['zoom'])) $zoom = intval($_GET['zoom']);
+            else $zoom = 'undefined';
+            echo '<script>$(document).ready(function(){ window.map.setView(['.$lat.','.$lon.'], '.$zoom.'); });</script>';
+        }
+    ?>
 </head>
 <body>
-	<div id="container">
-		<div id="topBar">
-			<div id="routeselector">Please enable Javascript to use this map.</div>
-		</div>
-		<div id="map"></div>
-		<div id="locationButton"></div>
-	</div>
-	<script>
-		var _paq = _paq || [];
-		_paq.push(["trackPageView"]);
-		_paq.push(["enableLinkTracking"]);
-
-		(function() {
-			var u=(("https:" == document.location.protocol) ? "https" : "http") + "://analytics.ant.sr/";
-			_paq.push(["setTrackerUrl", u+"piwik.php"]);
-			_paq.push(["setSiteId", "3"]);
-			var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
-			g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
-		})();
-	</script>
+    <div id="container">
+        <div id="topBar">
+            <div id="routeselector">Please enable Javascript to use this map.</div>
+        </div>
+        <div id="map"></div>
+        <div id="locationButton"></div>
+    </div>
 </body>
 </html>
diff --git a/js/script.js b/js/script.js
index 67d4989..5121120 100644
--- a/js/script.js
+++ b/js/script.js
@@ -1,390 +1,338 @@
 $(document).ready(function() {
-	
-	// loading animation
-	$("#routeselector").html('<div class="loading bar"><i class="sphere"></i></div>');
-	$("#routeselector .loading.bar").attr("data-text","Loading map");
 
-	// options
-	var updateDelay = 10776;
-	
-	// setup map
-	var zoom = 14;
-	var center = [40.5, -74.45];
-	var bounds = [
-		[40.400, -74.65], // sw
-		[40.600, -74.25], // ne
-	];
-	var mapDivID = "map";
-	var mapOptions = {
-		minZoom: 14,
-		maxZoom: 18,
-		zoomControl: false,
-	};
-	var boundsOptions = {
-		'animate': false,
-		'reset': true,
-	};
-	
-	// fastclick
-	$(function() {
-		FastClick.attach(document.body);
-	});
-	
-	// create map
-	window.map = L.map(mapDivID, mapOptions).setView(center, zoom).setMaxBounds(bounds, boundsOptions);
+    // loading animation
+    $("#routeselector").html('<div class="loading bar"><i class="sphere"></i></div>');
+    $("#routeselector .loading.bar").attr("data-text","Loading map");
 
-	// setup and create tile layer on map
-	var text = '<a href="https://ant.sr">ant.sr</a> | <div id="raptor"></div><br>'
-		+ '<strong>Not affiliated with <a href="http://www.rutgers.edu">Rutgers</a>.</strong><br>'
-		+ 'Bus data &copy; <a href="http://www.rutgers.edu/">Rutgers University</a><br>'
-		+ 'Map data &copy; <a href="http://osm.org/copyright">OSM contributors</a>';
-	
-	L.tileLayer('http://{s}.tiles.antsar-static.com/{tileset}/{z}/{x}/{y}.png', {
-		subdomains: ['a', 'b', 'c', 'd', 'e'],
-		tileset: 'rutgers-black',
-		errorTileUrl: 'http://tiles.antsar-static.com/generic/tile-blank-black.png',
-		attribution: text,
-	}).addTo(window.map);
+    // options
+    var updateDelay = 10776;
 
-	// init UI elements
-	window.busMarkers = {};
-	window.stopMarkers = {};
-	window.polylines = [];
-	
-	// init data storage vars
-	window.userLocation = false;
-	
-	// event listener for successful gps geolocation
-	window.map.on('locationfound', function(e){
-		var dotsize = 20;
-		if (window.devicePixelRatio > 1) dotsize = 35; // retina
-		showBlueDot(e.latlng, e.accuracy / 2, dotsize);
-	});
-	
-	// event listener for gps geolocation failure
-	window.map.on('locationerror', function(e){
-		alert("Access to your current location was refused. To use the Current Location feature in the future, you may need to reset your browser's location warnings.");
-	});
-	
-	// event listener for beginning of dragging.
-	window.map.on('dragstart', function(e){
-		// cancel geolocation on drag.
-		if (window.userLocation == true) stopUserLocation();
-	});
-	
-	// event listener for opening popup./
-	/*
-	window.map.on('popupopen', function(e){
-		window.popup = e.popup.options;
-		var zoom = window.map.getZoom;
-		if (zoom < 16) zoom = 16
-		//window.map.setView(e.popup._latlng, zoom);
-	});
-	*/
-	
-	
-	// Bind user location getting to the user location getting button
-	$("#locationButton").click(function() {
-		if (window.userLocation == false) {
-			startUserLocation();
-		} else {
-			stopUserLocation();
-		}
-	});	
-	
-	$("#routeselector .loading.bar").attr("data-text","Loading routes");
-	
-	// get routes and initialize route buttons. this is awkward; should refactor.
-	getRouteConfig(function(routes, stops) {
-		makeRouteButtons(routes);
-	});
-	
-	// main app loop
-	function mainLoop() {
-		getRouteConfig(function(routes, stops) {
-			updateMapBusStops(routes,stops);
-			getVehicles(function(vehicles) {
-				updateMapVehicles(routes, vehicles);
-				removeStaleMarkers();
-				updateRouteButtons(vehicles);
-			});
-		});
-	}
-	mainLoop();
-	window.setInterval(mainLoop, updateDelay);
-	
-	$('#raptor').raptorize();
+    // setup map
+    var zoom = 14;
+    var center = [40.5, -74.45];
+    var bounds = [
+        [40.400, -74.65], // sw
+        [40.600, -74.25], // ne
+    ];
+    var mapDivID = "map";
+    var mapOptions = {
+        minZoom: 14,
+        maxZoom: 18,
+        zoomControl: false,
+    };
+    var boundsOptions = {
+        'animate': false,
+        'reset': true,
+    };
+
+    // fastclick
+    $(function() {
+        FastClick.attach(document.body);
+    });
+
+    // create map
+    window.map = L.map(mapDivID, mapOptions).setView(center, zoom).setMaxBounds(bounds, boundsOptions);
+
+    // init UI elements
+    window.busMarkers = {};
+    window.stopMarkers = {};
+    window.polylines = [];
+
+    // init data storage vars
+    window.userLocation = false;
+
+    // event listener for successful gps geolocation
+    window.map.on('locationfound', function(e){
+        var dotsize = 20;
+        if (window.devicePixelRatio > 1) dotsize = 35; // retina
+        showBlueDot(e.latlng, e.accuracy / 2, dotsize);
+    });
+
+    // event listener for gps geolocation failure
+    window.map.on('locationerror', function(e){
+        alert("Access to your current location was refused by your browser. Please reset your location warnings to use this feature.");
+    });
+
+    // event listener for beginning of dragging.
+    window.map.on('dragstart', function(e){
+        // cancel geolocation on drag.
+        if (window.userLocation == true) stopUserLocation();
+    });
+
+    // Bind user location getting to the user location getting button
+    $("#locationButton").click(function() {
+        if (window.userLocation == false) {
+            startUserLocation();
+        } else {
+            stopUserLocation();
+        }
+    });
+
+    $("#routeselector .loading.bar").attr("data-text","Loading routes");
+
+    // get routes and initialize route buttons. this is awkward; should refactor.
+    getRouteConfig(window.agency, function(routes, stops) {
+        makeRouteButtons(routes);
+    });
+
+    // main app loop
+    function mainLoop() {
+        getRouteConfig(window.agency, function(routes, stops) {
+            updateMapBusStops(routes,stops);
+            getVehicles(window.agency, function(vehicles) {
+                updateMapVehicles(routes, vehicles, window.busIconUrl);
+                removeStaleMarkers();
+                updateRouteButtons(vehicles);
+            });
+        });
+    }
+    mainLoop();
+    window.setInterval(mainLoop, updateDelay);
+
+    $('#raptor').raptorize();
 });
 
 function startUserLocation() {
-	$("#locationButton").css("background-image", "url(img/crosshair-blue.png)");
-	window.map.locate({
-		watch: true,
-		setView: true,
-		maxZoom: 18,
-	});
-	window.userLocation = true;
+    $("#locationButton").css("background-image", "url(img/crosshair-blue.png)");
+    window.map.locate({
+        watch: true,
+        setView: true,
+        maxZoom: 18,
+    });
+    window.userLocation = true;
 }
 
 function stopUserLocation() {
-	var blueDotFadeTimeSeconds = 15;
-	window.map.stopLocate();
-	window.userLocation = false;
-	hideBlueDot(15);
-	$("#locationButton").css("background-image", "url(img/crosshair.png)");
+    var blueDotFadeTimeSeconds = 15;
+    window.map.stopLocate();
+    window.userLocation = false;
+    hideBlueDot(15);
+    $("#locationButton").css("background-image", "url(img/crosshair.png)");
 }
 
 function showBlueDot(latlng, radius, size) {
-	var markerIcon = L.icon({
-		iconUrl: 'img/bluedot.png',
-		iconSize: [size,size],
-	});
-	if (window.userLocationMarker == undefined) {
-		window.userLocationMarker = L.marker(latlng, {icon: markerIcon}).addTo(map);
-	} else {
-		window.userLocationMarker.setLatLng(latlng);
-	}
+    var markerIcon = L.icon({
+        iconUrl: 'img/bluedot.png',
+        iconSize: [size,size],
+    });
+    if (window.userLocationMarker == undefined) {
+        window.userLocationMarker = L.marker(latlng, {icon: markerIcon}).addTo(map);
+    } else {
+        window.userLocationMarker.setLatLng(latlng);
+    }
 
-	if (radius < 25) radius = 1; // no tiny circles
+    if (radius < 25) radius = 1; // no tiny circles
 
-	if (window.userLocationCircle == undefined) {
-		window.userLocationCircle = L.circle(latlng, radius).addTo(map);
-	} else {
-		window.userLocationCircle.setLatLng(latlng).setRadius(radius);
-	}
+    if (window.userLocationCircle == undefined) {
+        window.userLocationCircle = L.circle(latlng, radius).addTo(map);
+    } else {
+        window.userLocationCircle.setLatLng(latlng).setRadius(radius);
+    }
 }
 
 function hideBlueDot(userLocationFadeTime) {
-	var fps = 30;
-	var hideBlueDotFrames = userLocationFadeTime * fps;
-	var delay = Math.round(1000 / fps);
-	window.hideBlueDotTimers = [];
-	var frame = 0;
-	function drawFrames( frame, delay, totalFrames ) {
-		var opacity = 1 - frame/totalFrames;
-		window.userLocationMarker.setOpacity(opacity);
-		if (totalFrames > frame) {
-			window.hideBlueDotTimers.push(setTimeout(function() {
-				drawFrames(frame+1, delay, totalFrames);
-			}, delay));
-		} else {
-			if (window.userLocationMarker != undefined) {
-				window.map.removeLayer(window.userLocationMarker);
-				delete window.userLocationMarker;
-			}
-		}
-	}
-	if (window.userLocationCircle != undefined) window.map.removeLayer(window.userLocationCircle);
-	delete window.userLocationCircle;
-	if (userLocationMarker) drawFrames(frame, delay, hideBlueDotFrames);
+    var fps = 60;
+    var hideBlueDotFrames = userLocationFadeTime * fps;
+    var delay = Math.round(1000 / fps);
+    window.hideBlueDotTimers = [];
+    var frame = 0;
+    function drawFrames( frame, delay, totalFrames ) {
+        var opacity = 1 - frame/totalFrames;
+        window.userLocationMarker.setOpacity(opacity);
+        if (totalFrames > frame) {
+            window.hideBlueDotTimers.push(setTimeout(function() {
+                drawFrames(frame+1, delay, totalFrames);
+            }, delay));
+        } else {
+            if (window.userLocationMarker != undefined) {
+                window.map.removeLayer(window.userLocationMarker);
+                delete window.userLocationMarker;
+            }
+        }
+    }
+    if (window.userLocationCircle != undefined) window.map.removeLayer(window.userLocationCircle);
+    delete window.userLocationCircle;
+    if (userLocationMarker) drawFrames(frame, delay, hideBlueDotFrames);
 }
 
-function getRouteConfig(grc_callback) {
-	$.ajax({
-		url: "ajax.php?command=routeConfig&a=rutgers",
-		type: 'GET',
-		success: function(data) {
-			if (grc_callback != undefined) grc_callback(data['routes'], data['stops']);
-		},
-		fail: function() {
-			alert("Could not get routes from NextBus. Please try again later.");
-		}
-	});
+function getRouteConfig(agency, grc_callback) {
+    $.ajax({
+        url: "ajax.php?command=routeConfig&a=" + agency,
+        type: 'GET',
+        success: function(data) {
+            if (grc_callback != undefined) grc_callback(data['routes'], data['stops']);
+        },
+        fail: function() {
+            alert("Could not get routes. Please try again later.");
+        }
+    });
 }
 
-function getVehicles(gv_callback) {	
-	var time = 0;
-	if (window.getVehicles_lastTime != undefined) time = window.getVehicles_lastTime;
-	$.ajax({
-		url: "ajax.php?command=vehicleLocations&a=rutgers&t=" + time,
-		type: 'GET',
-		success: function(data) {
-			window.getVehicles_lastTime = data['lastTime'];
-			// window.vehicles = data['vehicles'];
-			if (gv_callback != undefined) gv_callback(data['vehicles']);
-		},
-		fail: function() {
-			alert("Could not get vehicle locations from NextBus. Please try again later.");
-		}
-	});
+function getVehicles(agency, gv_callback) {
+    var time = 0;
+    if (window.getVehicles_lastTime != undefined) time = window.getVehicles_lastTime;
+    $.ajax({
+        url: "ajax.php?command=vehicleLocations&a=" + agency + "&t=" + time,
+        type: 'GET',
+        success: function(data) {
+            window.getVehicles_lastTime = data['lastTime'];
+            // window.vehicles = data['vehicles'];
+            if (gv_callback != undefined) gv_callback(data['vehicles']);
+        },
+        fail: function() {
+            alert("Could not get vehicle locations. Please try again later.");
+        }
+    });
 }
 
-function updateMapBusStops(routesIn,stops,maxPredictions) {	
-	if (window.map != undefined && stops != undefined) {
-		for (i in stops) {
-			if (stops[i]['lat'] != undefined && stops[i]['lon'] != undefined) {
-				var markerIcon = L.icon({
-					iconUrl: 'img/star.png',
-					iconSize: [25,24],
-				});
-				var routes = [];
-				for (j in stops[i]['routes']) {
-					var string = "<strong>"+routesIn[j]['title']+"</strong>" + ": ";
-					for (k in stops[i]['routes'][j]) {
-						var sec = Math.floor((stops[i]['routes'][j][k]['epochTime'] - (new Date).getTime()) / 1000);
-						var min = Math.floor(sec / 60);
-						var sec = sec % 60;
-						string += '<a class="tooltip prediction" title="Bus #'+stops[i]['routes'][j][k]['vehicle']+'">' + min + "</a>, ";
-					}
-					if (string != "<strong>"+routesIn[j]['title']+"</strong>" + ": ") {
-						string = string.substring(0, string.length-2);
-						routes.push(string);
-					}
-				}
-				if (routes.length == 0) var routeList = "<div class='centered em'>no predictions</div>";
-				else var routeList = routes.sort().join("<br>")
-				var popup = '<div class="title">'+stops[i]['title']+'</div>' + routeList;
-				if (window.stopMarkers[stops[i]['tag']] == undefined) {
-					var markerOpts = {
-						icon: markerIcon,
-						title: stops[i]['title'],
-						opacity: 1,
-						riseOnHover: true,
-						zIndexOffset: -1000,
-						riseOffset: 1500,
-					};
-					var popupOpts = {
-						closeButton: false,
-						keepInView: true,
-					};
-					window.stopMarkers[stops[i]['tag']] = L.marker([stops[i]['lat'], stops[i]['lon']], markerOpts).addTo(window.map).bindPopup(popup, popupOpts);
-				} else {
-					window.stopMarkers[stops[i]['tag']].setLatLng([stops[i]['lat'], stops[i]['lon']]).setPopupContent(popup);
-				}
-			}
-		}
-	}
+function updateMapBusStops(routesIn,stops,maxPredictions) {
+    if (window.map != undefined && stops != undefined) {
+        for (i in stops) {
+            if (stops[i]['lat'] != undefined && stops[i]['lon'] != undefined) {
+                var markerIcon = L.icon({
+                    iconUrl: 'img/star.png',
+                    iconRetinaUrl: 'img/star@2x.png',
+                    iconSize: [25,24],
+                });
+                var routes = [];
+                for (j in stops[i]['routes']) {
+                    var string = "<strong>"+routesIn[j]['title']+"</strong>" + ": ";
+                    for (k in stops[i]['routes'][j]) {
+                        var sec = Math.floor((stops[i]['routes'][j][k]['epochTime'] - (new Date).getTime()) / 1000);
+                        var min = Math.floor(sec / 60);
+                        var sec = sec % 60;
+                        string += '<a class="tooltip prediction" title="Bus #'+stops[i]['routes'][j][k]['vehicle']+'">' + min + "</a>, ";
+                    }
+                    if (string != "<strong>"+routesIn[j]['title']+"</strong>" + ": ") {
+                        string = string.substring(0, string.length-2);
+                        routes.push(string);
+                    }
+                }
+                if (routes.length == 0) var routeList = "<div class='centered em'>no predictions</div>";
+                else var routeList = routes.sort().join("<br>")
+                var popup = '<div class="title">'+stops[i]['title']+'</div>' + routeList;
+                if (window.stopMarkers[stops[i]['tag']] == undefined) {
+                    var markerOpts = {
+                        icon: markerIcon,
+                        title: stops[i]['title'],
+                        opacity: 1,
+                        riseOnHover: true,
+                        zIndexOffset: -1000,
+                        riseOffset: 1500,
+                    };
+                    var popupOpts = {
+                        closeButton: false,
+                        keepInView: true,
+                    };
+                    window.stopMarkers[stops[i]['tag']] = L.marker([stops[i]['lat'], stops[i]['lon']], markerOpts).addTo(window.map).bindPopup(popup, popupOpts);
+                } else {
+                    window.stopMarkers[stops[i]['tag']].setLatLng([stops[i]['lat'], stops[i]['lon']]).setPopupContent(popup);
+                }
+            }
+        }
+    }
 }
 
 function makeRouteButtons(routes) {
-	if (window.map != undefined && routes != undefined) {
-		$("#routeselector").append('<button class="routeBtn routeBtn-=allRoutes" data-selected="false" data-route="allroutes">All</button>');
-		for (i in routes) {
-			$("#routeselector").append('<button class="routeBtn" data-selected="true" data-route="'+i+'">'+routes[i]['title']+'</button>');
-		}
-		$("#routeselector .routeBtn").hide();
-		$('#routeselector').on("click", ".routeBtn", function(e) {
-			if ($(this).attr('data-route') == "allroutes") {
-				$(this).siblings().click();
-			} else {
-				if ($(this).attr('data-selected') == 'true') {
-					// hide this route
-					for (i in window.busMarkers[$(this).attr('data-route')]) {
-						window.busMarkers[$(this).attr('data-route')][i].setOpacity(0);
-					}
-					$(this).attr('data-selected', 'false');
-				} else {
-					// show this route
-					for (i in window.busMarkers[$(this).attr('data-route')]) {
-						window.busMarkers[$(this).attr('data-route')][i].setOpacity(1);
-					}
-					$(this).attr('data-selected', 'true');
-				}
-			}
-		});
-	}
+    if (window.map != undefined && routes != undefined) {
+        $("#routeselector").append('<button class="routeBtn routeBtn-=allRoutes" data-selected="false" data-route="allroutes">All</button>');
+        for (i in routes) {
+            $("#routeselector").append('<button class="routeBtn" data-selected="true" data-route="'+i+'">'+routes[i]['title']+'</button>');
+        }
+        $("#routeselector .routeBtn").hide();
+        $('#routeselector').on("click", ".routeBtn", function(e) {
+            if ($(this).attr('data-route') == "allroutes") {
+                $(this).siblings().click();
+            } else {
+                if ($(this).attr('data-selected') == 'true') {
+                    // hide this route
+                    for (i in window.busMarkers[$(this).attr('data-route')]) {
+                        window.busMarkers[$(this).attr('data-route')][i].setOpacity(0);
+                    }
+                    $(this).attr('data-selected', 'false');
+                } else {
+                    // show this route
+                    for (i in window.busMarkers[$(this).attr('data-route')]) {
+                        window.busMarkers[$(this).attr('data-route')][i].setOpacity(1);
+                    }
+                    $(this).attr('data-selected', 'true');
+                }
+            }
+        });
+    }
 }
 
 function updateRouteButtons(vehicles) {
-	if (window.map != undefined) {
-		visibleRoutes = {};
-		for (i in window.busMarkers) {
-			for (j in window.busMarkers[i]) {
-				visibleRoutes[i] = true;
-			}
-		}
-		if ($("#routeselector .loading").length > 0) $("#routeselector .loading").remove();
-		$("#routeselector .routeBtn").not("[data-route='allroutes']").each(function() {
-			if (visibleRoutes[$(this).attr('data-route')] == undefined && $(this).is(":visible") == true) $(this).hide();
-			else if (visibleRoutes[$(this).attr('data-route')] == true && $(this).is(":visible") == false) $(this).show();
-		});
-		if ($("#routeselector .routeBtn:visible").length > 0 && $("#routeselector .routeBtn[data-route='allroutes']").is(":visible") == false) {
-			$("#routeselector .routeBtn[data-route='allroutes']").show();
-		}
-	}
+    if (window.map != undefined) {
+        visibleRoutes = {};
+        for (i in window.busMarkers) {
+            for (j in window.busMarkers[i]) {
+                visibleRoutes[i] = true;
+            }
+        }
+        if ($("#routeselector .loading").length > 0) $("#routeselector .loading").remove();
+        $("#routeselector .routeBtn").not("[data-route='allroutes']").each(function() {
+            if (visibleRoutes[$(this).attr('data-route')] == undefined && $(this).is(":visible") == true) $(this).hide();
+            else if (visibleRoutes[$(this).attr('data-route')] == true && $(this).is(":visible") == false) $(this).show();
+        });
+        if ($("#routeselector .routeBtn:visible").length > 0 && $("#routeselector .routeBtn[data-route='allroutes']").is(":visible") == false) {
+            $("#routeselector .routeBtn[data-route='allroutes']").show();
+        }
+    }
 }
 
-function updateMapVehicles(routes,vehicles) {
-	if (window.map != undefined) {
-		for (i in vehicles) {
-			
-			if (window.busMarkers[vehicles[i]['routeTag']] == undefined) {
-				window.busMarkers[vehicles[i]['routeTag']] = {};
-			}
+function updateMapVehicles(routes, vehicles, busIconUrl) {
+    if (window.map != undefined) {
+        for (i in vehicles) {
+            if (window.busMarkers[vehicles[i]['routeTag']] == undefined) {
+                window.busMarkers[vehicles[i]['routeTag']] = {};
+            }
 
-			var stops = [];
-			for (j in vehicles[i]['stops']) {
-				var sec = Math.floor((vehicles[i]['stops'][j]['epochTime'] - (new Date).getTime()) / 1000);
-				var min = Math.floor(sec / 60);
-				var sec = sec % 60;
-				stops.push("<span class='prediction'>" + min + '</span> ' + vehicles[i]['stops'][j]['title'] );
-			}
-			var stopList = stops.join("<br>")
-			label = vehicles[i]['route'];
-			popup = "";
-			popup += "<div class='title'>" + label + "</div>";
-			popup += stopList;
-			popup += "<div class='footer'>Bus #" + vehicles[i]['id'] + "</div>";
-			popup += "";
-			if (window.busMarkers[vehicles[i]['routeTag']][i] == undefined) {
+            var stops = [];
+            for (j in vehicles[i]['stops']) {
+                var sec = Math.floor((vehicles[i]['stops'][j]['epochTime'] - (new Date).getTime()) / 1000);
+                var min = Math.floor(sec / 60);
+                var sec = sec % 60;
+                stops.push("<span class='prediction'>" + min + '</span> ' + vehicles[i]['stops'][j]['title'] );
+            }
+            var stopList = stops.join("<br>")
+            label = vehicles[i]['route'];
+            popup = "";
+            popup += "<div class='title'>" + label + "</div>";
+            popup += stopList;
+            popup += "<div class='footer'>Bus #" + vehicles[i]['id'] + "</div>";
+            popup += "";
+            if (window.busMarkers[vehicles[i]['routeTag']][i] == undefined) {
 
-				window.busMarkers[vehicles[i]['routeTag']][i] = L.marker([vehicles[i]['lat'], vehicles[i]['lon']], {
-					icon: L.icon({
-						iconUrl: 'https://rutgers.antsar-static.com/img/bus.png',
-						iconSize: [21,25],
-						iconAnchor: [10,12],
-					}),
-					iconAngle: vehicles[i]['heading'],
-				}).bindLabel(label, {
-					noHide: true,
-					direction: 'right',
-					className: 'busLabel',
-				}).bindPopup(popup).addTo(window.map);
-			} else {
-				window.busMarkers[vehicles[i]['routeTag']][i].setLatLng([vehicles[i]['lat'], vehicles[i]['lon']]).setPopupContent(popup).setIconAngle(vehicles[i]['heading']);
-			}
-			window.busMarkers[vehicles[i]['routeTag']][i].lastUpdated = (new Date).getTime();
-			
-		}
-	}
+                window.busMarkers[vehicles[i]['routeTag']][i] = L.marker([vehicles[i]['lat'], vehicles[i]['lon']], {
+                    icon: L.icon({
+                        iconUrl: busIconUrl,
+                        iconSize: [21,25],
+                        iconAnchor: [10,12],
+                    }),
+                    iconAngle: vehicles[i]['heading'],
+                }).bindLabel(label, {
+                    noHide: true,
+                    direction: 'right',
+                    className: 'busLabel',
+                }).bindPopup(popup).addTo(window.map);
+            } else {
+                window.busMarkers[vehicles[i]['routeTag']][i].setLatLng([vehicles[i]['lat'], vehicles[i]['lon']]).setPopupContent(popup).setIconAngle(vehicles[i]['heading']);
+            }
+            window.busMarkers[vehicles[i]['routeTag']][i].lastUpdated = (new Date).getTime();
+        }
+    }
 }
 
 function removeStaleMarkers(staleTime) {
-	if (staleTime == undefined) staleTime = 60 * 1000; // 60 seconds = dead bus
-	for (i in window.busMarkers) {
-		for (j in window.busMarkers[i]) {
-			if (window.busMarkers[i][j].lastUpdated < (new Date).getTime() - staleTime) {
-				window.map.removeLayer(window.busMarkers[i][j]);
-				delete window.busMarkers[i][j];
-			}
-		}
-	}
-}
-
-function cleanRouteName(route) {
-	if (route == "New Brunsquick 1 Shuttle") route = "NB 1";
-	else if (route == "New Brunsquick 2 Shuttle") route = "NB 2";
-	return route;
-}
-
-
-function closeEnough(arg1, arg2, decimalPlacesAccuracy) {
-	var x = Math.pow(10,decimalPlacesAccuracy);
-	return (Math.floor(arg1 * x) / x == Math.floor(arg2 * x) / x);
-}
-
-function sameBusStop(pos1, pos2) {
-	var acc = 3; // up to 110 meters
-	if (closeEnough(pos1['lat'], pos2['lat'], acc) && closeEnough(pos1['lon'], pos2['lon'], acc)) return true;
-	return false;
-}
-
-function ol( object ) {
-    var length = 0;
-    for( var key in object ) {
-        if( object.hasOwnProperty(key) ) {
-            ++length;
+    if (staleTime == undefined) staleTime = 60 * 1000; // 60 seconds = dead bus
+    for (i in window.busMarkers) {
+        for (j in window.busMarkers[i]) {
+            if (window.busMarkers[i][j].lastUpdated < (new Date).getTime() - staleTime) {
+                window.map.removeLayer(window.busMarkers[i][j]);
+                delete window.busMarkers[i][j];
+            }
         }
     }
-    return length;
-};
+}
+
-- 
GitLab