Warning from Transport for Canberra about using MyWay apps like this
Warning from Transport for Canberra about using MyWay apps like this

file:a/about.php -> file:b/about.php
--- a/about.php
+++ b/about.php
@@ -17,6 +17,10 @@
 Feedback encouraged; contact maxious@lambdacomplex.org<br />
     <br />
 Some icons by Joseph Wain / glyphish.com<br />
+Native clients also available for iPhone(<a href="http://itunes.apple.com/au/app/cbrtimetable/id444287349?mt=8">cbrTimetable by Sandor Kolotenko</a>
+, <a href="http://itunes.apple.com/au/app/act-buses/id376634797?mt=8">ACT Buses by David Sullivan</a>) 
+and Android (<a href="https://market.android.com/details?id=com.action">MyBus 2.0 by Imagine Team</a>)
+<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, 

--- a/aws/awsStartup.sh
+++ b/aws/awsStartup.sh
@@ -26,6 +26,9 @@
 #createuser transitdata -SDRP
 #password transitdata
 #psql -d transitdata -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\"
+#psql -d transitdata -c "GRANT SELECT,INSERT ON	TABLE myway_observations,myway_routes,myway_stops,myway_timingdeltas TO transitdata;"
+#psql -d transitdata -c	"GRANT SELECT,INSERT,UPDATE ON TABLE myway_routes,myway_stops TO transitdata;"
+##psql -d transitdata -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO transitdata;"
 php /var/www/updatedb.php
 
 wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \

--- a/css/local.css.php
+++ b/css/local.css.php
@@ -25,11 +25,11 @@
         margin: 0 !important;
      }
     .ui-icon-navigation {
-        background-image: url('.$_REQUEST['labsPath'].'css/images/113-navigation.png);
+        background-image: url(images/113-navigation.png);
         background-position: 1px 0;
      }
     .ui-icon-beaker {
-        background-image: url('.$_REQUEST['labsPath'].'css/images/91-beaker-2.png);
+        background-image: url(images/91-beaker-2.png);
         background-position: 1px 0;
     }
     #footer {

--- a/include/common-template.inc.php
+++ b/include/common-template.inc.php
@@ -23,10 +23,9 @@
 	$url.= "&guid=ON";
 	return str_replace("&", "&amp;", $url);
 }
-
 function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false)
 {
-global $labsPath;
+	global $labsPath;
 	echo '
 <!DOCTYPE html> 
 <html lang="en">
@@ -34,34 +33,36 @@
         <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1"> 	
 <title>' . $pageTitle . '</title>
-        <meta name="google-site-verification" 
-content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" />
-	<link rel="stylesheet"  href="'.$labsPath.'css/jquery-ui-1.8.12.custom.css" />';
+        <meta name="google-site-verification" content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" />
+	<link rel="stylesheet"  href="' . $labsPath . 'css/jquery-ui-1.8.12.custom.css" />
+	<script src="' . $labsPath . 'js/LAB.min.js"></script> ';
 	if (isDebugServer()) {
-		echo '<link rel="stylesheet"  href="'.$labsPath.'css/jquery.mobile-1.0b1.css" />
-	
-         <script type="text/javascript" src="'.$labsPath.'js/jquery-1.6.1.min.js"></script>
-	 <script>$(document).bind("mobileinit", function(){
+		$jqmcss = $labsPath . 'css/jquery.mobile-1.0b1.css';
+		$jqjs = $labsPath . 'js/jquery-1.6.1.min.js';
+		$jqmjs = $labsPath . 'js/jquery.mobile-1.0b1.js';
+	}
+	else {
+		$jqmcss = "http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css";
+		$jqjs = "http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js";
+		$jqmjs = "http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js";
+	}
+	echo '<link rel="stylesheet"  href="' . $jqmcss . '" />
+	<script src="'.$jqjs.'"></script>
+		 <script>$(document).bind("mobileinit", function(){
   $.mobile.ajaxEnabled = false;
 });
-</script>
-        <script type="text/javascript" src="'.$labsPath.'js/jquery.mobile-1.0b1.js"></script>';
-	}
-	else {
-		echo '<link rel="stylesheet"  href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
-        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
-	 <script>$(document).bind("mobileinit", function(){
-  $.mobile.ajaxEnabled = false;
-});
-</script>
-        <script type="text/javascript" src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>';
-	}
-	echo '
-	<script src="'.$labsPath.'js/jquery.ui.autocomplete.min.js"></script>
-<script src="'.$labsPath.'js/jquery.ui.core.min.js"></script>
-<script src="'.$labsPath.'js/jquery.ui.position.min.js"></script>
-<script src="'.$labsPath.'js/jquery.ui.widget.min.js"></script>
-  <script>
+</script> 
+	<script src="'.$jqmjs.'"></script>
+	
+	 <script>
+	  $LAB.setOptions({AlwaysPreserveOrder:true})
+  .script("' . $jqjs . '").wait()
+
+.script("' . $labsPath . 'js/jquery.ui.core.min.js")
+.script("' . $labsPath . 'js/jquery.ui.position.min.js")
+.script("' . $labsPath . 'js/jquery.ui.widget.min.js").wait()
+  .script("' . $labsPath . 'js/jquery.ui.autocomplete.min.js")
+ .wait(function() {
 	$(function() {
 		$( "#geolocate" ).autocomplete({
 			source: "lib/autocomplete.php",
@@ -76,11 +77,10 @@
 			minLength: 2
 		});
 	});
-	</script>
-	';
+});
+	</script>';
 	echo '<style type="text/css">';
-
-if (strstr($_SERVER['HTTP_USER_AGENT'], 'Android')) echo '.ui-shadow,.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a,.ui-body-b,.ui-btn-up-b,.ui-btn-hover-b,
+	if (strstr($_SERVER['HTTP_USER_AGENT'], 'Android')) echo '.ui-shadow,.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a,.ui-body-b,.ui-btn-up-b,.ui-btn-hover-b,
 .ui-btn-down-b,.ui-bar-c,.ui-body-c,.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c,.ui-bar-c,.ui-body-d,
 .ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d,.ui-bar-d,.ui-body-e,.ui-btn-up-e,.ui-btn-hover-e,
 .ui-btn-down-e,.ui-bar-e,.ui-overlay-shadow,.ui-shadow,.ui-btn-active,.ui-body-a,.ui-bar-a {
@@ -88,11 +88,8 @@
  box-shadow: none;
  -webkit-box-shadow: none;
 }';
-echo '</style>';
-
-echo '<link rel="stylesheet"  href="'.$labsPath.'css/local.css.php?labsPath='.$labsPath.'" />';
-	
-
+	echo '</style>';
+	echo '<link rel="stylesheet"  href="' . $labsPath . 'css/local.css.php" />';
 	if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPad')) {
 		echo '<meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
@@ -149,28 +146,26 @@
 	<div data-role="header" data-position="inline">
 	<a href="' . (isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "javascript:history.go(-1)") . '" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a> 
 		<h1>' . $pageTitle . '</h1>
-		<a href="'.$labsPath.'/index.php" data-icon="home" class="ui-btn-right">Home</a>
+		<a href="' . $labsPath . '/index.php" data-icon="home" class="ui-btn-right">Home</a>
 	</div><!-- /header -->
         <a name="maincontent" id="maincontent"></a>
         <div data-role="content"> ';
 		$overrides = getServiceOverride();
 		if ($overrides['service_id']) {
-				if ($overrides['service_id'] == "noservice") {
-					echo '<div id="servicewarning">Buses are <strong>not running today</strong> due to industrial action/public holiday. See <a 
+			if ($overrides['service_id'] == "noservice") {
+				echo '<div id="servicewarning">Buses are <strong>not running today</strong> due to industrial action/public holiday. See <a 
 href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>';
-				}
-				else {
-					echo '<div id="servicewarning">Buses are running on an altered timetable today due to industrial action/public holiday. See <a href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>';
-				}
+			}
+			else {
+				echo '<div id="servicewarning">Buses are running on an altered timetable today due to industrial action/public holiday. See <a href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>';
 			}
 		}
-
+	}
 }
 function include_footer()
 {
-
-global $labsPath;
-	echo '<div id="footer"><a href="'.$labsPath.'about.php">About/Contact Us</a>&nbsp;<a href="'.$labsPath.'feedback.php">Feedback/Bug Report</a>&nbsp;<a href="'.$labsPath.'privacy.php">Privacy Policy</a>';
+	global $labsPath;
+	echo '<div id="footer"><a href="' . $labsPath . 'about.php">About/Contact Us</a>&nbsp;<a href="' . $labsPath . 'feedback.php">Feedback/Bug Report</a>&nbsp;<a href="' . $labsPath . 'privacy.php">Privacy Policy</a>';
 	echo '</div>';
 	if (isAnalyticsOn()) {
 		echo "<script>  (function() {
@@ -183,9 +178,8 @@
   })();</script>";
 		$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl();
 		echo '<noscript><img src="' . $googleAnalyticsImageUrl . '" /></noscript>';
-
-	}
-			echo "\n</div></div></body></html>";
+	}
+	echo "\n</div></div></body></html>";
 }
 function timePlaceSettings($geolocate = false)
 {

file:b/js/LAB.min.js (new)
--- /dev/null
+++ b/js/LAB.min.js
@@ -1,1 +1,5 @@
-
+/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
+    v2.0.1 (c) Kyle Simpson
+    MIT License
+*/
+(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b<a.scripts.length;b++){if(a.scripts[b].ready&&a.scripts[b].exec_trigger){c=true;a.scripts[b].exec_trigger();a.scripts[b].exec_trigger=null}}return c}function t(a,c,b,d){a.onload=a.onreadystatechange=function(){if((a.readyState&&a.readyState!="complete"&&a.readyState!="loaded")||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c<a.finished_listeners.length;c++){setTimeout(a.finished_listeners[c],0)}a.ready_listeners=[];a.finished_listeners=[]}function P(d,f,e,g,h){setTimeout(function(){var a,c=f.real_src,b;if("item"in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement("script");if(f.type)a.type=f.type;if(f.charset)a.charset=f.charset;if(h){if(r){e.elem=a;if(E){a.preload=true;a.onpreload=g}else{a.onreadystatechange=function(){if(a.readyState=="loaded")g();a.onreadystatechange=null}}a.src=c}else if(h&&c.indexOf(D)==0&&d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+"\n//@ sourceURL="+c;g()}};b.open("GET",c);b.send()}else{a.type="text/cache-script";t(a,e,"ready",function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}},0)}function J(){var l={},Q=r||M,n=[],p={},m;l[y]=true;l[z]=false;l[u]=false;l[A]=false;l[B]="";function R(a,c,b){var d;function f(){if(d!=null){I(b);d=null}}if(p[c.src].finished)return;if(!a[u])p[c.src].finished=true;d=b.elem||document.createElement("script");if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,"finished",f);if(b.elem){b.elem=null}else if(b.text){d.onload=d.onreadystatechange=null;d.text=b.text}else{d.src=c.real_src}i.insertBefore(d,i.firstChild);if(b.text){f()}}function S(c,b,d,f){var e,g,h=function(){b.ready_cb(b,function(){R(c,b,e)})},j=function(){b.finished_cb(b,d)};b.src=N(b.src,c[B]);b.real_src=b.src+(c[A]?((/\?.*$/.test(b.src)?"&_":"?_")+~~(Math.random()*1E9)+"="):"");if(!p[b.src])p[b.src]={items:[],finished:false};g=p[b.src].items;if(c[u]||g.length==0){e=g[g.length]={ready:false,finished:false,ready_listeners:[h],finished_listeners:[j]};P(c,b,e,((f)?function(){e.ready=true;for(var a=0;a<e.ready_listeners.length;a++){setTimeout(e.ready_listeners[a],0)}e.ready_listeners=[]}:function(){I(e)}),f)}else{e=g[0];if(e.finished){setTimeout(j,0)}else{e.finished_listeners.push(j)}}}function v(){var e,g=s(l,{}),h=[],j=0,w=false,k;function T(a,c){a.ready=true;a.exec_trigger=c;x()}function U(a,c){a.ready=a.finished=true;a.exec_trigger=null;for(var b=0;b<c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j<h.length){if(G(h[j])){try{h[j]()}catch(err){}}else if(!h[j].finished){if(O(h[j]))continue;break}j++}if(j==h.length){w=false;k=false}}function V(){if(!k||!k.scripts){h.push(k={scripts:[],finished:true})}}e={script:function(){for(var f=0;f<arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d<c.length;d++){V();a=c[d];if(G(a))a=a();if(!a)continue;if(H(a)){b=[].slice.call(a);b.push(d,1);c.splice.call(c,b);d--;continue}if(typeof a=="string")a={src:a};a=s(a,{ready:false,ready_cb:T,finished:false,finished_cb:U});k.finished=false;k.scripts.push(a);S(g,a,k,(Q&&w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length>0){for(var a=0;a<arguments.length;a++){h.push(arguments[a])}k=h[h.length-1]}else k=false;x();return e}};return{script:e.script,wait:e.wait,setOptions:function(a){s(a,g);return e}}}m={setGlobalDefaults:function(a){s(a,l);return m},setOptions:function(){return v().setOptions.apply(null,arguments)},script:function(){return v().script.apply(null,arguments)},wait:function(){return v().wait.apply(null,arguments)},queueScript:function(){n[n.length]={type:"script",args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:"wait",args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b>=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this);

--- a/labs/index.php
+++ b/labs/index.php
@@ -6,13 +6,22 @@
 		<li data-role="list-divider" > Experimental Features </li>
 		<li><a href="mywaybalance.php"><h3>MyWay Balance for mobile</h3>
 		<p>Mobile viewer for MyWay balance. Warning! No HTTPS security.</p></a></li>
-		<li><a href="networkstats.php"><h3>Route Statistics</h3>
-		<p>Analysis of route timing points</p></a></li>
 		<li><a href="busstopdensity.php"><h3>Bus Stop Density Map</h3>
 		<p>Analysis of bus stop coverage</p></a></li>
 		<li><a href="stopBrowser.php"><h3>Bus Stop Browser Map</h3>
 		<p>Bus stop location/route browser</p></a></li>
-		<li>More coming soon!</li>
+            </ul>
+   <ul data-role="listview" data-theme="e" data-groupingtheme="e">
+
+		<li data-role="list-divider" > MyWay Timeliness Graphs </li>
+		<li><a href="myway_timeliness.php"><h3>Timeliness over Day</h3>
+		<p>Displays the deviation from the timetable over the day</p></a></li>
+		<li><a href="myway_timeliness_freqdist.php"><h3>Frequency Distribution of Time Deviation</h3>
+		<p>Displays spread of time deviations</p></a></li>
+		<li><a href="myway_timeliness_route.php"><h3>Timeliness over Route</h3>
+		<p>Displays the deviation from timetable as a specific route progresses</p></a></li>
+		<li><a href="myway_timeliness_stop.php"><h3>Timeliness at Stop</h3>
+		<p>Displays the deviation from the timetable at a specific stop</p></a></li>
             </ul>
 	    </div>
 <?php

--- a/labs/myway_timeliness.php
+++ b/labs/myway_timeliness.php
@@ -6,7 +6,7 @@
     <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]--> 
  
     <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script> 
-  <div id="placeholder" style="width:1000px;height:600px"></div> 
+  <center><div id="placeholder" style="width:900px;height:550px"></div></center>
 <script type="text/javascript"> 
 $(function () {
     var d = new Date();
@@ -26,16 +26,16 @@
 $labels = Array();
 $lastRoute = "";
 foreach ($query->fetchAll() as $delta) {
-    $routeName = $delta['route_full_name'];
-    if (strstr($routeName," 3")) $routeName = "312-319";
-    else $routeName = preg_replace('/\D/', '', $routeName);
-    if ($routeName != $lastRoute) {
-        	$i++;
-                echo "    var d$i = [];";
-                $lastRoute = $routeName;
-                $labels[$i] = $routeName;
-    }
-	echo "d$i.push([ midnight+ (1000*" . midnight_seconds(strtotime($delta['time'])) . "), {$delta['timing_delta']}]); \n";
+	$routeName = $delta['route_full_name'];
+	if (strstr($routeName, " 3")) $routeName = "312-319";
+	else $routeName = preg_replace('/\D/', '', $routeName);
+	if ($routeName != $lastRoute) {
+		$i++;
+		echo "    var d$i = [];";
+		$lastRoute = $routeName;
+		$labels[$i] = $routeName;
+	}
+	echo "d$i.push([ midnight+ (1000*" . midnight_seconds(strtotime($delta['time'])) . "), ".intval($delta['timing_delta'])."]); \n";
 };
 ?>
 
@@ -44,13 +44,13 @@
     var plot = $.plot(placeholder, [
 <?php
 foreach ($labels as $key => $label) {
-    echo "        {
+	echo "        {
             data: d$key,
             points: { show: true },
             label: '$label'
         },";
 }
-        ?>
+?>
     ],
         {
             xaxis: {
@@ -61,17 +61,18 @@
             yaxis: {
                 tickFormatter: yformatter
             },
-            grid: { hoverable: true, clickable: true },
+            grid: { hoverable: true, clickable: true, labelMargin: 32   },
     });
         var o;
     o = plot.pointOffset({ x: midnight+ (9*60*60*1000), y: -1.2});
     placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">9am</div>');
-  o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
+    o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
     placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">4pm</div>');
 
  });
 function yformatter(v) {
-    return Math.floor(v/60) + " minutes " + (v == 0 ? "" : (v >0 ? "early":"late"))
+    if (Math.floor(v/60) < -9) return "";
+    return Math.abs(Math.floor(v/60)) + " min " + (v == 0 ? "" : (v >0 ? "early":"late"))
 }
   function showTooltip(x, y, contents) {
         $('<div id="tooltip">' + contents + '</div>').css( {
@@ -105,7 +106,7 @@
 
                     
                     showTooltip(item.pageX, item.pageY,
-                                item.series.label + " at "+ time +" = " + y +" ( "+ y/60+" minutes )");
+                                item.series.label + " at "+ time +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
                 }
             }
             else {

--- a/labs/myway_timeliness_calculate.php
+++ b/labs/myway_timeliness_calculate.php
@@ -87,7 +87,8 @@
 						//work out time delta, put into array with index of delta
 						$timeDeltas[] = Array(
 							"timeDiff" => $timeDiff,
-							"stop_code" => $potentialStop['stop_code']
+							"stop_code" => $potentialStop['stop_code'],
+                                                        "stop_sequence" => $timedTrip['stop_sequence']
 						);
 						echo "Found trip {$trip['trip_id']} at stop {$potentialStop['stop_code']} (#{$potentialStop['stop_id']}, sequence #{$trip['stop_sequence']})<br>";
                                                 echo "Arriving at {$timedTrip['arrival_time']}, difference of " . round($timeDiff / 60, 2) . " minutes<br>";
@@ -110,18 +111,18 @@
 		echo "Lowest difference of " . round($lowestDelta / 60, 2) . " minutes will be recorded for this observation<br>";
 		$observation_id = $obsv['observation_id'];
 		$route_full_name = $obsv['route_full_name'];
-		$myway_route = $obsv['myway_stop'];
 		$stop_code = $timeDeltas[0]["stop_code"];
-		$stmt = $conn->prepare("insert into myway_timingdeltas (observation_id, route_full_name, myway_route, stop_code, timing_delta, time, date, timing_period)
-				      values (:observation_id, :route_full_name, :myway_route, :stop_code, :timing_delta, :time, :date, :timing_period)");
+		$stop_sequence = $timeDeltas[0]["stop_sequence"];
+		$stmt = $conn->prepare("insert into myway_timingdeltas (observation_id, route_full_name, stop_code, timing_delta, time, date, timing_period, stop_sequence)
+				      values (:observation_id, :route_full_name, :stop_code, :timing_delta, :time, :date, :timing_period, :stop_sequence)");
 		$stmt->bindParam(':observation_id', $observation_id);
 		$stmt->bindParam(':route_full_name', $route_full_name);
-		$stmt->bindParam(':myway_route', $myway_route);
 		$stmt->bindParam(':stop_code', $stop_code);
 		$stmt->bindParam(':timing_delta', $lowestDelta);
 		$stmt->bindParam(':time', $time);
 		$stmt->bindParam(':date', $date);
 		$stmt->bindParam(':timing_period', $timing_period);
+                $stmt->bindParam(':stop_sequence', $stop_sequence);
 		// insert a record
 		$stmt->execute();
 		if ($stmt->rowCount() > 0) {

--- /dev/null
+++ b/labs/myway_timeliness_freqdist.php
@@ -1,1 +1,78 @@
+<?php
+include ('../include/common.inc.php');
+include_header("MyWay Deltas", "mywayDelta");
+?>
 
+    <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]--> 
+ 
+    <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script> 
+  <center><div id="placeholder" style="width:900px;height:550px"></div></center>
+<script type="text/javascript"> 
+$(function () {
+
+ var d1 = [];
+<?php
+$query = "select td, count(*) from (select (timing_delta - MOD(timing_delta,10)) as td from myway_timingdeltas where abs(timing_delta) < 2*(select stddev(timing_delta) from myway_timingdeltas)) as a  group by td order by td";
+$query = $conn->prepare($query);
+$query->execute();
+if (!$query) {
+	databaseError($conn->errorInfo());
+	return Array();
+}
+
+foreach ($query->fetchAll() as $delta) {
+
+	echo "d1.push([ ".intval($delta['td']).", ".intval($delta['count'])."]); \n";
+};
+?>
+
+       var placeholder = $("#placeholder");
+
+    var plot = $.plot(placeholder, [
+       {
+            data: d1,
+            bars: { show: true }
+        },
+    ],
+        {
+
+            grid: { hoverable: true, clickable: true, labelMargin: 17  },
+    });
+ /*       var o;
+    o = plot.pointOffset({ x: midnight+ (9*60*60*1000), y: -1.2});
+    placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">9am</div>');
+    o = plot.pointOffset({ x: midnight+ (16*60*60*1000), y: -1.2});
+    placeholder.append('<div style="position:absolute;left:' + (o.left + 4) + 'px;top:' + o.top + 'px;color:#666;font-size:smaller">4pm</div>');
+ */
+ });
+
+  /*
+    var previousPoint = null;
+    $("#placeholder").bind("plothover", function (event, pos, item) {
+        $("#x").text(pos.x.toFixed(2));
+        $("#y").text(pos.y.toFixed(2));
+ 
+            if (item) {
+                if (previousPoint != item.dataIndex) {
+                    previousPoint = item.dataIndex;
+                    
+                    $("#tooltip").remove();
+                    var x = item.datapoint[0].toFixed(2),
+                        y = item.datapoint[1].toFixed(2);
+                    
+                    var d = new Date();
+d.setTime(x);
+var time = d.getUTCHours() +':'+ (d.getUTCMinutes().toString().length == 1 ? '0'+ d.getMinutes():  d.getUTCMinutes())
+
+                    
+                    showTooltip(item.pageX, item.pageY,
+                                item.series.label + " at "+ time +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
+                }
+            }
+            else {
+                $("#tooltip").remove();
+                previousPoint = null;            
+            }
+    });
+  */
+</script> 

--- a/labs/myway_timeliness_reconcile.php
+++ b/labs/myway_timeliness_reconcile.php
@@ -7,8 +7,8 @@
 		$query = "update myway_routes set route_full_name = :route_full_name where myway_route = :myway_route";
 		debug($query, "database");
 		$query = $conn->prepare($query);
-		$query->bindParam(":myway_route", $myway_route);
-		$query->bindParam(":route_full_name", $route_full_name);
+		$query->bindParam(":myway_route", $myway_route,PDO::PARAM_STR, 5);
+		$query->bindParam(":route_full_name", $route_full_name,PDO::PARAM_STR, 42);
 		$query->execute();
 		die(print_r($conn->errorInfo() , true));
 	}
@@ -19,8 +19,8 @@
 		$query = "update myway_stops set stop_code = :stop_code, stop_street = :stop_street where myway_stop = :myway_stop";
 		debug($query, "database");
 		$query = $conn->prepare($query);
-		$query->bindParam(":myway_stop", $myway_stop);
-		$query->bindParam(":stop_code", $stop_code);
+		$query->bindParam(":myway_stop", $myway_stop, PDO::PARAM_STR, 25);
+		$query->bindParam(":stop_code", $stop_code, PDO::PARAM_STR, 32);
                 		$query->bindParam(":stop_street", $stop_street);
 		$query->execute();
 		die(print_r($conn->errorInfo() , true));

--- /dev/null
+++ b/labs/myway_timeliness_route.json.php
@@ -1,1 +1,25 @@
-
+<?php
+include ('../include/common.inc.php');
+header('Content-Type: text/javascript; charset=utf8');
+// header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/');
+header('Access-Control-Max-Age: 3628800');
+header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
+?>
+{
+    "label": "<?php echo $_REQUEST['routeid']; ?>",
+    "data": <?php
+   $query = "select * from myway_timingdeltas where route_full_name = :route_full_name AND abs(timing_delta) < 2*(select stddev(timing_delta) from myway_timingdeltas)  order by stop_sequence;";
+$query = $conn->prepare($query);
+$query->bindParam(':route_full_name', $_REQUEST['routeid'],PDO::PARAM_STR, 42);
+		
+$query->execute();
+if (!$query) {
+	databaseError($conn->errorInfo());
+	return Array();
+}
+foreach ($query->fetchAll() as $delta) {
+	$points[] = "[{$delta['stop_sequence']}, {$delta['timing_delta']}]";
+};
+echo "[".implode(",",$points)."]";
+?>
+}

--- /dev/null
+++ b/labs/myway_timeliness_route.php
@@ -1,1 +1,120 @@
+<?php
+include ('../include/common.inc.php');
+include_header("MyWay Deltas", "mywayDelta");
+?>
 
+    <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../js/flot/excanvas.min.js"></script><![endif]--> 
+ 
+    <script language="javascript" type="text/javascript" src="../js/flot/jquery.flot.js"></script> 
+  <form method="get" action="">
+    <select id="routeid" name="routeid">
+<?php
+$query = "select distinct route_full_name from myway_routes where myway_route != '' order by route_full_name";
+$query = $conn->prepare($query);
+$query->execute();
+if (!$query) {
+	databaseError($conn->errorInfo());
+	return Array();
+}
+foreach ($query->fetchAll() as $route) {
+           echo "<option value=\"{$route['route_full_name']}\">{$route['route_full_name']}</option>";
+  
+};
+?>    </select>
+  <center><div id="placeholder" style="width:900px;height:550px"></div></center>
+<script type="text/javascript"> 
+$(function () {
+
+       var placeholder = $("#placeholder");
+ var data = [];
+    var options = {
+            xaxis: {
+            },
+            yaxis: {
+                tickFormatter: yformatter
+            },
+            grid: { hoverable: true, clickable: true, labelMargin: 32   },
+    };
+    
+    var plot = $.plot(placeholder, data, options);
+ 
+// fetch one series, adding to what we got
+    var alreadyFetched = {};
+    
+   $("#routeid").change(function () {
+   var select = $(this);
+        
+        // find the URL in the link right next to us 
+    //    var dataurl = button.siblings('a').attr('href');
+ var dataurl = "myway_timeliness_route.json.php?routeid=" + select.val();
+        // then fetch the data with jQuery
+        function onDataReceived(series) {
+            // extract the first coordinate pair so you can see that
+            // data is now an ordinary Javascript object
+            var firstcoordinate = '(' + series.data[0][0] + ', ' + series.data[0][1] + ')';
+ 
+      
+            // let's add it to our current data
+            if (!alreadyFetched[series.label]) {
+                alreadyFetched[series.label] = true;
+                data.push(series);
+            }
+            
+            // and plot all we got
+            $.plot(placeholder, data, options);
+         }
+        
+        $.ajax({
+            url: dataurl,
+            method: 'GET',
+            dataType: 'json',
+            success: onDataReceived
+        });
+    });
+ 
+
+ });
+
+
+
+function yformatter(v) {
+    if (Math.floor(v/60) < -9) return "";
+    return Math.abs(Math.floor(v/60)) + " min " + (v == 0 ? "" : (v >0 ? "early":"late"))
+}
+  function showTooltip(x, y, contents) {
+        $('<div id="tooltip">' + contents + '</div>').css( {
+            position: 'absolute',
+            display: 'none',
+            top: y + 5,
+            left: x + 5,
+            border: '1px solid #fdd',
+            padding: '2px',
+            'background-color': '#fee',
+            opacity: 0.80
+        }).appendTo("body").fadeIn(200);
+    }
+ 
+    var previousPoint = null;
+    $("#placeholder").bind("plothover", function (event, pos, item) {
+        $("#x").text(pos.x.toFixed(2));
+        $("#y").text(pos.y.toFixed(2));
+ 
+            if (item) {
+                if (previousPoint != item.dataIndex) {
+                    previousPoint = item.dataIndex;
+                    
+                    $("#tooltip").remove();
+                    var x = item.datapoint[0],
+                        y = item.datapoint[1].toFixed(2);
+                    
+                    showTooltip(item.pageX, item.pageY,
+                                item.series.label + " at stop_sequence "+ x +" = " + Math.abs(new Number(y/60).toFixed(2))+" minutes "+(y >0 ? "early":"late"));
+                }
+            }</