Merge branch 'master' of ssh://apples.lambdacomplex.org/git/bus
Merge branch 'master' of ssh://apples.lambdacomplex.org/git/bus

--- a/busui/about.php
+++ b/busui/about.php
@@ -1,23 +1,24 @@
 <?php
 include('common.inc.php');
+include_header("About")
 ?>
 <p>
-    Busness Time - An ACT bus timetable webapp
-Based on the maxious-canberra-transit-feed
-Uses jQuery Mobile, PHP, Ruby, Python, Google Transit Feed Specification tools, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service
-
-Feedback encouraged; contact maxious@lambdacomplex.org
-    
-Some icons by Joseph Wain / glyphish.com
-
-
-Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip.
+Busness Time - An ACT bus timetable webapp<br />
+Based on the maxious-canberra-transit-feed (<a href="cbrfeed.zip">download</a>, last updated <?php echo date("F d Y.", @filemtime('cbrfeed.zip')); ?>)<br />
+Source code for the transit feed and this site @ <a href="http://maxious.lambdacomplex.org/git">http://maxious.lambdacomplex.org/git</a><br />
+Uses jQuery Mobile, PHP, Ruby, Python, Google Transit Feed Specification tools, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service<br />
+<br />
+Feedback encouraged; contact maxious@lambdacomplex.org<br />
+    <br />
+Some icons by Joseph Wain / glyphish.com<br />
+<br />
+<small>Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip.
 Whilst every effort has been made to ensure the high quality and accuracy of the Site, the Author makes no warranty, 
 express or implied concerning the topicality, correctness, completeness or quality of the information, which is provided 
 "as is". The Author expressly disclaims all warranties, including but not limited to warranties of fitness for a particular purpose and warranties of merchantability. 
 All offers are not binding and without obligation. The Author expressly reserves the right, in his discretion, to suspend, 
 change, modify, add or remove portions of the Site and to restrict or terminate the use and accessibility of the Site 
-without prior notice. 
+without prior notice. </small>
 <?
 include_footer();
 ?>

--- a/busui/common.inc.php
+++ b/busui/common.inc.php
@@ -1,38 +1,55 @@
 <?php
 date_default_timezone_set('Australia/ACT');
 $APIurl = "http://localhost:8765";
-error_reporting(E_ALL ^ E_NOTICE);
+$cloudmadeAPIkey="daa03470bb8740298d4b10e3f03d63e6";
+$googleMapsAPIkey="ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q";
+$otpAPIurl = 'http://10.1.0.243:5080/opentripplanner-api-webapp/';
+if (isDebug()) error_reporting(E_ALL ^ E_NOTICE);
 
 // SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',')
-$suburbs = explode(",","Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Unclassified ACT,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla");
+$suburbs = explode(",","Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla");
 
  // you have to open the session to be able to modify or remove it 
-session_start(); 
- 
-
+session_start();
+ if (isset($_REQUEST['service_period'])) $_SESSION['service_period'] = filter_var($_REQUEST['service_period'],FILTER_SANITIZE_STRING);
+ if (isset($_REQUEST['time'])) $_SESSION['time'] = filter_var($_REQUEST['time'],FILTER_SANITIZE_STRING);
+ if (isset($_REQUEST['geolocate'])) {
+    $contents = geocode(var_filter($_REQUEST['geolocate'],FILTER_SANITIZE_URL),true);
+    if (isset($contents[0]->centroid)) {
+        $session['lat'] = $contents[0]->centroid->coordinates[0];
+        $session['lon'] = $contents[0]->centroid->coordinates[1];
+    }
+    else {
+        $session['lat'] = "";
+        $session['lon'] = "";
+    }
+ }
+//print_r ($_SESSION);
 function isDebug()
 {
-    return $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME'];
+    return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME'];
+}
+
+function isMetricsOn()
+{
+    return true;
 }
 
 function debug($msg) {
     if (isDebug()) echo "<!-- $msg -->";
 }
 function isFastDevice() {
+    $fastDevices = Array("Mozilla/5.0 (X11;", "Mozilla/5.0 (Windows;", "Mozilla/5.0 (iP", "Mozilla/5.0 (Linux; U; Android", "Mozilla/4.0 (compatible; MSIE");
+    $slowDevices = Array("J2ME","MIDP","Opera/","Mozilla/2.0 (compatible;","Mozilla/3.0 (compatible;");
     return true;
 }
 
 function include_header($pageTitle, $opendiv = true, $geolocate = false) {
- // this starts the session 
- session_start(); 
- 
-    // if (isDebug()) // set php error level high
     echo '
 <!DOCTYPE html> 
 <html> 
 	<head> 
-	<title>busness time - '.$pageTitle.'</title> 
-	';
+	<title>'.$pageTitle.'</title>';
          if (isDebug()) echo '<link rel="stylesheet"  href="css/jquery-mobile-1.0a3.css" />
          <script type="text/javascript" src="js/jquery-1.5.js"></script>
         <script type="text/javascript" src="js/jquery-mobile-1.0a3.js"></script>';
@@ -51,9 +68,24 @@
 	<script src="js/jquery.ui.datepicker.mobile.js"></script> 
      <style type="text/css">
      .ui-navbar {
-     padding-bottom: 18px;
      width: 100%;
      }
+     .ui-btn-inner {
+        white-space: normal !important;
+     }
+     .ui-li-heading {
+        white-space: normal !important;
+     }
+    .ui-listview-filter {
+        margin: 0 !important;
+     }
+    #footer {
+        text-size: 0.75em;
+        text-align: center;
+    }
+    body {
+        background-color: #F0F0F0;
+    }
 </style>
 <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
@@ -62,35 +94,12 @@
  if ($geolocate) {
 echo "<script>
 
-function setCookie(c_name,value,expiredays)
-{
-var exdate=new Date();
-exdate.setDate(exdate.getDate()+expiredays);
-document.cookie=c_name+ \"=\" +escape(value)+
-((expiredays==null) ? \"\" : \";expires=\"+exdate.toUTCString());
-}
-
-function getCookie(c_name)
-{
-if (document.cookie.length>0)
-  {
-  c_start=document.cookie.indexOf(c_name + \"=\");
-  if (c_start!=-1)
-    {
-    c_start=c_start + c_name.length+1;
-    c_end=document.cookie.indexOf(\";\",c_start);
-    if (c_end==-1) c_end=document.cookie.length;
-    return unescape(document.cookie.substring(c_start,c_end));
-    }
-  }
-return \"\";
-}
-
 function success(position) {
 $('#geolocate').val(position.coords.latitude+','+position.coords.longitude);
-setCookie('geolocate',position.coords.latitude+','+position.coords.longitude,1);
-}
-
+// setCookie('geolocate',position.coords.latitude+','+position.coords.longitude,1);
+$('#here').click(function(event) { $('#geolocate').val(doAJAXrequestForGeolocSessionHere()); return false;});
+$('#here').show();
+}
 function error(msg) {
  console.log(msg);
 }
@@ -101,19 +110,34 @@
 
 </script> ";
  }
-echo '</head> 
-<body> 
+echo '</head>
+<body>
+
  ';
-if ($opendiv) echo '<div data-role="page"> 
- 
+if ($opendiv)  {
+    echo '<div data-role="page"> 
+ <script>
+$(document).ready(function ()
+{
+    document.title = "'.$pageTitle.'";
+});
+</script>
 	<div data-role="header"> 
 		<h1>'.$pageTitle.'</h1>
 	</div><!-- /header -->
         <div data-role="content"> ';
 }
+}
 
 function include_footer()
 {
+    if ($geolocate && isset($_SESSION['lat'])) {
+        echo "<script>
+        $('#here').click(function(event) { $('#geolocate').val(doAJAXrequestForGeolocSessionHere()); return false;});
+$('#here').show();
+</script>";
+    }
+    echo '<div id="footer"><a href="about.php">About/Contact Us</a>&nbsp;<a href="feedback.php">Feedback/Bug Report</a></a>';
     echo '</div>';
 }
 
@@ -143,7 +167,7 @@
 {
 // from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html
 if (isset($_SESSION['time'])) {
-        $time = mkdate($_SESSION['time']);
+        $time = strtotime($_SESSION['time']);
         return (date("G",$time) * 3600) + (date("i",$time) * 60) + date("s",$time);
     }
    return (date("G") * 3600) + (date("i") * 60) + date("s");
@@ -160,6 +184,7 @@
 }
 function getPage($url)
 {
+    debug($url);
     $ch = curl_init($url);
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
@@ -223,8 +248,11 @@
         }
        $center = $totalLat/sizeof($mapPoints).",".$totalLon/sizeof($mapPoints);
     }
-    
-    return '<img src="staticmaplite/staticmap.php?center='.$center.'&zoom='.$zoom.'&size='.$width.'x'.$height.'&maptype=mapnik&markers='.$markers.'" width='.$width.' height='.$height.'>';
+    $output = "";
+    $output .= '<div data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>';
+    $output .= '<center><img src="staticmaplite/staticmap.php?center='.$center.'&zoom='.$zoom.'&size='.$width.'x'.$height.'&maptype=mapnik&markers='.$markers.'" width='.$width.' height='.$height.'></center>';
+    $output .= '</div>';
+    return $output;
 }
 
 function distance($lat1, $lng1, $lat2, $lng2)
@@ -330,7 +358,8 @@
 }
 
 function geocode($query, $giveOptions) {
-       $url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?query=".$query."&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true";
+    global $cloudmadeAPIkey;
+       $url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?query=".$query."&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true";
       $contents = json_decode(getPage($url));
       if ($giveOptions) return $contents->features;
       elseif (isset($contents->features[0]->centroid)) return $contents->features[0]->centroid->coordinates[0].",".$contents->features[0]->centroid->coordinates[1];
@@ -338,7 +367,8 @@
 }
 
 function reverseGeocode($lat,$lng) {
-      $url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?around=".$lat.",".$lng."&distance=closest&object_type=road";
+    global $cloudmadeAPIkey;
+       $url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?around=".$lat.",".$lng."&distance=closest&object_type=road";
       $contents = json_decode(getPage($url));
       return $contents->features[0]->properties->name;
 }
@@ -355,6 +385,76 @@
 function bracketsMeanNewLine($input) {
     return str_replace(")","</small>",str_replace("(","<br><small>",$input));
 }
+
+function viaPoints($tripid,$stopid, $timingPointsOnly = false) {
+    global $APIurl;
+    $url = $APIurl."/json/tripstoptimes?trip=".$tripid;
+
+$json = json_decode(getPage($url));
+debug(print_r($json,true));
+$stops = $json[0];
+$times = $json[1];
+$foundStop = false;
+$viaPoints = Array();
+foreach ($stops as $key => $row)
+{
+    if ($foundStop) {
+        if (!$timingPointsOnly || !startsWith($row[5],"Wj") ) {
+            $viaPoints[] = Array("id" => $row[0], "name" => $row[1], "time" => $times[$key]);
+        }
+    } else {
+        if ($row[0] == $stopid) $foundStop = true;
+    }
+}
+    return $viaPoints;
+}
+
+function viaPointNames($tripid,$stopid) {
+    $points = viaPoints($tripid,$stopid,true);
+    $pointNames = Array();
+    foreach ($points as $point) {
+        $pointNames[] = $point['name'];
+    }
+    return implode(", ",$pointNames);
+}
+
+function timePlaceSettings($geolocate = false) {
+    global $service_periods;
+    $geoerror = false;
+    if ($geolocate == true) {
+       $geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat'])
+       || $_SESSION['lat'] == "" || $_SESSION['lon'] == "";
+    }
+    if ($geoerror) {
+        echo '<div class="error">Sorry, but your location could not currently be detected.
+        Please allow location permission, wait for your location to be detected,
+        or enter an address/co-ordinates in the box below.</div>';
+    }
+    echo '<div data-role="collapsible" data-collapsed="'.!$geoerror.'">
+        <h3>Change Time/Place...</h3>
+        <form action="" method="post">
+        <div class="ui-body"> 
+		<div data-role="fieldcontain">
+	            <label for="geolocate"> Current Location: </label>
+			<input type="text" id="geolocate" name="geolocate" value="Enter co-ordinates or address here"/> <a href="#" style="display:none" name="here" id="here"/>Here?</a>
+	        </div>
+    		<div data-role="fieldcontain">
+		        <label for="time"> Time: </label>
+		    	<input type="time" name="time" id="time" value="'. ($_SESSION['time'] ? $_SESSION['time'] : date("H:m")).'"/> <a href="#" name="currentTime" id="currentTime"/>Current Time?</a>
+	        </div>
+		<div data-role="fieldcontain">
+		    <label for="service_period"> Service Period:  </label>
+			<select name="service_period">';
+
+			   foreach ($service_periods as $service_period) {
+			    echo "<option value=\"$service_period\"".(service_period() === $service_period ? "SELECTED" : "").'>'.ucwords($service_period).'</option>';
+			   }
+			echo '</select>
+			<a href="#" style="display:none" name="currentPeriod" id="currentPeriod"/>Current Period?</a>
+		</div>
+		
+		<input type="submit" value="Update"/>
+                </form>
+            </div></div>';
+}
 ?>
-  
-

 Binary files /dev/null and b/busui/css/images/ajax-loader.png differ
 Binary files /dev/null and b/busui/css/images/form-check-off.png differ
 Binary files /dev/null and b/busui/css/images/form-check-on.png differ
 Binary files /dev/null and b/busui/css/images/form-radio-off.png differ
 Binary files /dev/null and b/busui/css/images/form-radio-on.png differ
 Binary files /dev/null and b/busui/css/images/icon-search-black.png differ
 Binary files /dev/null and b/busui/css/images/icons-18-black.png differ
 Binary files /dev/null and b/busui/css/images/icons-18-white.png differ
 Binary files /dev/null and b/busui/css/images/icons-36-black.png differ
 Binary files /dev/null and b/busui/css/images/icons-36-white.png differ
--- /dev/null
+++ b/busui/feedback.php
@@ -1,1 +1,18 @@
+<?php
+include('common.inc.php');
+include_header("Feedback")
+?>
+<h3>Add/Move/Delete a Bus Stop Location</h3>
+StopID:
+or StopCode:
 
+Suggested Stop Location (lat/long or words):
+
+<h3>Bug Report/Feedback</h3>
+<textarea id="extrainfo">
+    Referrer URL
+    User Agent
+    User host/IP
+    Current date/time
+    Dump of $_SESSION
+</textarea>

--- a/busui/index.php
+++ b/busui/index.php
@@ -1,63 +1,28 @@
 <?php 
 include('common.inc.php');
- // remove all the variable in the session 
- session_unset(); 
-
- //destroy the session 
- session_destroy();
- if (isset($_REQUEST['service_period'])) $_SESSION['service_period'] = $_REQUEST['service_period'];
- if (isset($_REQUEST['time'])) $_SESSION['time'] = $_REQUEST['time'];
- // todo take in cellids and crossreference with http://realtimeblog.free.fr/latest/cellular/processed/sqlite/505_sqlite_zones.zip to estimate location
 include_header("bus.lambdacomplex.org",false, true)
 ?>
 <div data-role="page">
 	<div data-role="content">
 			<div id="jqm-homeheader">
-	    	<center><h3 id="jqm-logo"><img src="apple-touch-icon.png" alt="logo" width="64" height="64" /><br>
-		busness time</h3></center>
+	    	<center><h3>busness time</h3><br><small>Canberra Bus Timetables and Trip Planner</small></center>
 	</div> 
 	    <a href="tripPlanner.php" data-role="button">Launch Trip Planner...</a>
             <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
                 <li data-role="list-divider">Timetables - Stops</li>
                 <li><a href="stopList.php">Major (Timing Point) Stops</a></li>
 		<li><a href="stopList.php">All Stops</a></li>
-		<li><a href="stopList.php?nearbyfavs=yes">Nearby/Favourite Stops</a></li>
+		<li><a href="stopList.php?suburbs=yes">Stops By Suburb</a></li>
+		<li><a class="nearby" href="stopList.php?nearby=yes">Nearby Stops</a></li>
             </ul>
 	    <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
                 <li data-role="list-divider">Timetables - Routes</li>
                 <li><a href="routeList.php">Routes By Final Destination</a></li>
 		<li><a href="routeList.php?bynumber=yes">Routes By Number</a></li>
-		<li><a href="routeList.php?nearbyfavs=yes">Nearby/Favourites Routes</a></li>
+		<li><a class="nearby" href="routeList.php?nearby=yes">Nearby Routes</a></li>
             </ul>
-            <div class="ui-body ui-body-c">
-		<h3>Time/Place Settings</h3>
-		<div data-role="fieldcontain">
-	            <label for="geolocate"> Current Location: </label>
-			<input type="text" id="geolocate" name="geolocate"/> <a href="#" style="display:none" name="here" id="here"/>Here?</a>
-	        </div>
-    		<div data-role="fieldcontain">
-		        <label for="time"> Time: </label>
-		    	<input type="time" value="<?php echo date("H:m"); ?>"/> <a href="#" style="display:none" name="currentTime" id="currentTime"/>Current Time?</a>
-	        </div>
-		<div data-role="fieldcontain">
-		    <label for="service_period"> Service Period:  </label>
-			<select name="service_period">	
-			   <?php
-			   foreach ($service_periods as $service_period) {
-			    echo "<option value=\"$service_period\"".(service_period() === $service_period ? "SELECTED" : "").'>'.ucwords($service_period).'</option>';
-			   }?>
-			</select>
-			<a href="#" style="display:none" name="currentPeriod" id="currentPeriod"/>Current Period?</a>
-		</div>
-		
-		<input type="submit" value="Update"/>
-            </div>
-	<script>
-$('#here').click(function(event) { $('#geolocate').val(getCookie('geolocate')); return false;});
-$('#here').show();
-        </script>
-        </div>
-   </div>
- </body>
-</html>
-
+<?php
+echo timePlaceSettings();
+include_footer(true)
+?>
+        

--- a/busui/js/jquery.ui.datepicker.mobile.js
+++ b/busui/js/jquery.ui.datepicker.mobile.js
@@ -47,9 +47,12 @@
 	};
 		
 	//bind to pagecreate to automatically enhance date inputs	
-	$( ".ui-page" ).live( "pagecreate", function(){		
+	$( ".ui-page" ).live( "pagecreate", function(){     
 		$( "input[type='date'], input[data-type='date']" ).each(function(){
+		    if ($(this).hasClass("hasDatepicker") == false) {
 			$(this).after( $( "<div />" ).datepicker({ altField: "#" + $(this).attr( "id" ), showOtherMonths: true }) );
-		});	
-	});
+			$(this).addClass("hasDatepicker");
+		    }
+		}); 
+    });
 })( jQuery );

--- /dev/null
+++ b/busui/layar_api.php
@@ -1,1 +1,58 @@
+<?php
+include('common.inc.php');
+$output = Array();
+$output['hotspots'] = Array();
+$output['layer'] = "canberrabusstops";
 
+$max_page = 10;
+$max_results = 50;
+$page_start = 0+$_REQUEST['pageKey'];
+$page_end = $max_page+$_REQUEST['pageKey'];
+
+$url = $APIurl."/json/neareststops?lat={$_REQUEST['lat']}&lon={$_REQUEST['lon']}&limit=50";
+$contents = json_decode(getPage($url));
+debug(print_r($contents,true));
+$stopNum = 0;
+foreach ($contents as $row)
+{
+    $stopNum++;
+    if ($stopNum > $page_start && $stopNum <= $page_end) {
+        $hotspot = Array();
+        $hotspot['id'] = $row[0];
+        $hotspot['title'] = $row[1];
+        $hotspot['type'] = 0;
+        $hotspot['lat'] = floor($row[2]*1000000);
+        $hotspot['lon'] = floor($row[3]*1000000);
+        $hotspot['distance'] = distance($row[2], $row[3], $_REQUEST['lat'], $_REQUEST['lon']);
+        if (!isset($_REQUEST['radius']) || $hotspot['distance'] < $_REQUEST['radius']) {
+            $hotspot['actions'] = Array(Array("label" => 'View more trips/information', 'uri' => 'http://bus.lambdacomplex.org/'.'stop.php?stopid='.$row[0]));
+            $url = $APIurl."/json/stoptrips?stop=".$row[0]."&time=".midnight_seconds()."&service_period=".service_period();
+            $trips = json_decode(getPage($url));
+            debug(print_r($trips,true));
+            foreach ($trips as $key => $row)
+            {
+                if ($key < 3) {
+                    $hotspot['line'.strval($key+2)]= $row[1][1] .' @ ' .midnight_seconds_to_time($row[0]);
+                }
+            }
+            if (sizeof($trips) == 0) $hotspot['line2'] = 'No trips in the near future.';
+            $output['hotspots'][] = $hotspot;
+        }
+    }
+}
+if (sizeof($hotspot) > 0) {
+    $output['errorString'] = 'ok';
+    $output['errorCode'] = 0;
+    } else {
+    $output['errorString'] = 'no results, try increasing range';
+    $output['errorCode'] = 21;
+}
+if ($page_end >= $max_results || sizeof($hotspot) < $max_page) {
+ $output["morePages"] = false;
+ $output["nextPageKey"] = null;
+} else {
+ $output["morePages"] = true;
+ $output["nextPageKey"] = $page_end;    
+}
+echo json_encode($output);
+?>

--- a/busui/routeList.php
+++ b/busui/routeList.php
@@ -6,7 +6,8 @@
 			<ul> 
 				<li><a href="routeList.php">By Final Destination...</a></li> 
 				<li><a href="routeList.php?bynumber=yes">By Number... </a></li>
-				<!--<li><a href="routeList.php?bysuburb=yes">By Suburb... </a></li>-->
+				<li><a href="routeList.php?bysuburb=yes">By Suburb... </a></li>
+				<li><a href="routeList.php?nearby=yes">Nearby... </a></li>
 			</ul>
                 </div>
 	';

--- a/busui/schedule_viewer.py
+++ b/busui/schedule_viewer.py
@@ -89,10 +89,10 @@
   """Return tuple as expected by javascript function addStopMarkerFromList"""
   return (stop.stop_id, stop.stop_name, float(stop.stop_lat),
           float(stop.stop_lon), stop.location_type, stop.stop_code)
-def StopCodeToTuple(stop, code):
+def StopZoneToTuple(stop):
+  """Return tuple as expected by javascript function addStopMarkerFromList"""
   return (stop.stop_id, stop.stop_name, float(stop.stop_lat),
-          float(stop.stop_lon), stop.location_type, code)
-
+          float(stop.stop_lon), stop.location_type, stop.stop_code, stop.zone_id)
 
 class ScheduleRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
   def do_GET(self):
@@ -360,10 +360,8 @@
     schedule = self.server.schedule
     matches = []
     for s in schedule.GetStopList():
-      #wtf, stop_code changes into stop_name after .find()
-      virginstopCode = s.stop_code
       if s.stop_code.find("Wj") == -1:
-        matches.append(StopCodeToTuple(s,virginstopCode))
+        matches.append(StopToTuple(s))
     return matches
 
   def handle_json_GET_stopsearch(self, params):
@@ -389,10 +387,17 @@
     query = params.get('q', None).lower()
     matches = []
     for s in schedule.GetStopList():
-      #wtf, stop_code changes into stop_name after .find()
-      virginstopCode = s.stop_code
       if s.stop_code.lower().find(query) != -1:
-        matches.append(StopCodeToTuple(s,virginstopCode))
+        matches.append(StopToTuple(s))
+    return matches
+
+  def handle_json_GET_stopzonesearch(self, params):
+    schedule = self.server.schedule
+    query = params.get('q', None).lower()
+    matches = []
+    for s in schedule.GetStopList():
+      if s.zone_id != None and s.zone_id.lower().find(query) != -1:
+        matches.append(StopToTuple(s))
     return matches
 
   def handle_json_GET_stop(self, params):
@@ -416,7 +421,6 @@
     # Need make a tuple to find correct bisect point
     time_trips = time_trips[bisect.bisect_left(time_trips, (time, 0)):]
     time_trips = time_trips[:15]
-    # TODO: combine times for a route to show next 2 departure times
     result = []
     for time, (trip, index), tp in time_trips:
       headsign = None
@@ -436,8 +440,9 @@
         if len(trip_name):
           trip_name += " - "
         trip_name += route.route_long_name
-      if headsign:
-        trip_name += " (Direction: %s)" % headsign
+        # comment out directions because we already have them in the long name
+      #if headsign:
+      #  trip_name += " (Direction: %s)" % headsign
       if service_period == None or trip.service_id == service_period:
         result.append((time, (trip.trip_id, trip_name, trip.service_id), tp))
     return result

--- a/busui/stop.php
+++ b/busui/stop.php
@@ -3,9 +3,9 @@
 $url = $APIurl."/json/stop?stop_id=".$_REQUEST['stopid'];
 $stop = json_decode(getPage($url));
 
-include_header("Trips passing ".$stop[1]);
+include_header($stop[1]);
+timePlaceSettings();
 echo '<div data-role="content" class="ui-content" role="main"><p>'.staticmap(Array(0 => Array($stop[2],$stop[3]))).'</p>';
-// change date/time/service_period
 echo '  <ul data-role="listview"  data-inset="true">';
 $url = $APIurl."/json/stoptrips?stop=".$_REQUEST['stopid']."&time=".midnight_seconds()."&service_period=".service_period();
 $trips = json_decode(getPage($url));
@@ -13,7 +13,8 @@
 foreach ($trips as $row)
 {
 echo  '<li>';
-echo '<h3><a href="trip.php?stopid='.$_REQUEST['stopid'].'&tripid='.$row[1][0].'">'.bracketsMeanNewLine($row[1][1]).'</a></h3>';      
+echo '<h3><a href="trip.php?stopid='.$_REQUEST['stopid'].'&tripid='.$row[1][0].'">'.$row[1][1];
+echo '<br><small>Via: '.viaPointNames($row[1][0],$_REQUEST['stopid']).'</small> </a></h3>';      
 echo '<p class="ui-li-aside"><strong>'.midnight_seconds_to_time($row[0]).'</strong></p>';
 echo '</li>';  
 }

--- a/busui/stopList.php
+++ b/busui/stopList.php
@@ -1,8 +1,9 @@
 <?php
 include('common.inc.php');
-include_header("Stops");
-echo'
-		<div data-role="navbar"> 
+
+function navbar() {
+   echo'
+		<div data-role="navbar">
 			<ul> 
 				<li><a href="stopList.php">Timing Points</a></li>
 				<li><a href="stopList.php?suburbs=yes">By Suburb</a></li>
@@ -11,8 +12,12 @@
 			</ul>
                 </div>
 	';
+	timePlaceSettings();
+}
 // By su