From: Maxious Date: Sun, 20 Mar 2011 04:48:28 +0000 Subject: AWS fixes and js google analytics X-Git-Url: http://maxious.lambdacomplex.org/git/?p=busui.git&a=commitdiff&h=9ca11cc377fd0fad2461fe74c4718d4dc089f939 --- AWS fixes and js google analytics --- --- a/about.php +++ b/about.php @@ -4,9 +4,14 @@ ?>

Busness Time - An ACT bus timetable webapp
-Based on the maxious-canberra-transit-feed (download, last updated download, +last updated )
-Source code for the transit feed and this site @ http://maxious.lambdacomplex.org/git
+Source code for the transit +feed and this +site available from github.
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
--- a/aws/awsStartup.sh +++ b/aws/awsStartup.sh @@ -4,7 +4,6 @@ #screen php-cli php-gd tomcat6-webapps tomcat6-admin-webapps svn maven2 #http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12 -cp -rfv /tmp/busui/* /var/www cp /root/aws.php /tmp/ chcon -h system_u:object_r:httpd_sys_content_t /var/www chcon -R -h root:object_r:httpd_sys_content_t /var/www/* @@ -14,7 +13,8 @@ -O /var/www/cbrfeed.zip easy_install transitfeed easy_install simplejson -screen -d -m /var/www/view.sh +screen -S viewsh -X quit +screen -S viewsh -d -m /var/www/view.sh wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ -O /tmp/Graph.obj --- /dev/null +++ b/aws/compress.conf @@ -1,1 +1,33 @@ +####################################################################### +## +## Output Compression +## -------------------- +## +## see http://www.lighttpd.net/documentation/compress.html +## +server.modules += ( "mod_compress" ) +## +## where should the compressed files be cached? +## see the base config for the declaration of the variable. +## +## This directory should be changed per vhost otherwise you can +## run into trouble with overlapping filenames +## +compress.cache-dir = cache_dir + "/compress" + +## +## FileTypes to compress. +## +#compress.filetype = ("text/plain", "text/html") +compress.filetype = ("text/plain", "text/html", "text/javascript", "text/css", "text/xml") + +## +## Maximum filesize that will be compressed. +## Default is 0, which means unlimited file size. +## +#compress.max-filesize = 0 + +## +####################################################################### + --- /dev/null +++ b/aws/expire.conf @@ -1,1 +1,29 @@ +####################################################################### +## +## Expire Module +## --------------- +## +## See http://www.lighttpd.net/documentation/expire.html +## +server.modules += ( "mod_expire" ) +## +## assignes a expiration to all files below the specified path. The +## specification of the time is made up of: +## +## +## +#expire.url = ( +# "/buggy/" => "access 2 hours", +# "/images/" => "access plus 1 seconds 2 minutes" +#) +etag.use-mtime = "enable" +etag.use-inode = "disable" +static-file.etags = "enable" +$HTTP["url"] =~ "\.(css|js|png|jpg|ico|gif)$" { + expire.url = ( "" => "access 7 days" ) +} + +## +####################################################################### + --- a/aws/modules.conf +++ b/aws/modules.conf @@ -76,7 +76,7 @@ ## ## mod_compress ## -#include "conf.d/compress.conf" +include "conf.d/compress.conf" ## ## mod_userdir @@ -106,7 +106,7 @@ ## ## mod_expire ## -#include "conf.d/expire.conf" +include "conf.d/expire.conf" ## ## mod_secdownload --- a/aws/php.ini +++ b/aws/php.ini @@ -1,4 +1,6 @@ [PHP] + +date.timezone = "Australia/Sydney" ;;;;;;;;;;;;;;;;;;; ; About php.ini ; @@ -288,7 +290,7 @@ ; Note: You need to use zlib.output_handler instead of the standard ; output_handler, or otherwise the output will be corrupted. ; http://www.php.net/manual/en/zlib.configuration.php#ini.zlib.output-compression -zlib.output_compression = Off +zlib.output_compression = on ; http://www.php.net/manual/en/zlib.configuration.php#ini.zlib.output-compression-level ;zlib.output_compression_level = -1 @@ -1264,7 +1266,7 @@ ; where MODE is the octal representation of the mode. Note that this ; does not overwrite the process's umask. ; http://www.php.net/manual/en/session.configuration.php#ini.session.save-path -session.save_path = "/var/lib/php/session" +session.save_path = "/tmp" ; Whether to use cookies. ; http://www.php.net/manual/en/session.configuration.php#ini.session.use-cookies --- a/common-template.inc.php +++ b/common-template.inc.php @@ -1,11 +1,38 @@ - - - ' . $pageTitle . ''; + + + + ' . $pageTitle . ' + '; if ($datepicker) echo ''; if (isDebugServer()) echo ' @@ -20,7 +47,7 @@ }); '; -echo ' - + #jqm-homeheader { + text-align: center; + } + .viaPoints { + display: none; + text-size: 0.2em; + } + .min-width-480px .viaPoints { + display: block; + } + // source http://webaim.org/techniques/skipnav/ + #skip a, #skip a:hover, #skip a:visited +{ +position:absolute; +left:0px; +top:-500px; +width:1px; +height:1px; +overflow:hidden; +} + +#skip a:active, #skip a:focus +{ +position:static; +width:auto; +height:auto; +} +'; + if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod')) { + echo ' '; + } if ($geolocate) { echo " "; } - echo ' + echo ' + +

'; if ($opendiv) { echo '
@@ -87,6 +162,7 @@

' . $pageTitle . '

+
'; } } @@ -100,6 +176,10 @@ } echo ''; + if (!isDebug()) { + $googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(); + echo ''; + } } function timePlaceSettings($geolocate = false) { @@ -114,22 +194,22 @@ or enter an address/co-ordinates in the box below.
'; } echo '
-

Change Time/Place (' . (isset($_SESSION['time']) ? $_SESSION['time'] : "Current Time,") . ' '.ucwords(service_period()).')...

-
+

Change Time/Place (' . (isset($_SESSION['time']) ? $_SESSION['time'] : "Current Time,") . ' ' . ucwords(service_period()) . ')...

+
- +
- '; foreach ($service_periods as $service_period) { - echo "'; + echo "'; } echo ' --- a/common.inc.php +++ b/common.inc.php @@ -25,31 +25,31 @@ $_SESSION['time'] = filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING); } if (isset($_REQUEST['geolocate'])) { - $geocoded = false; if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) { $_SESSION['lat'] = trim(filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); $_SESSION['lon'] = trim(filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); } else { - $geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); - echo $_REQUEST['geolocate']; - if (startsWith($geolocate, "-")) { - $locateparts = explode(",",$geolocate); - $_SESSION['lat'] = $locateparts[0]; - $_SESSION['lon'] =$locateparts[1]; - } else { - $contents = geocode($geolocate, true); - print_r($contents); - if (isset($contents[0]->centroid)) { - $geocoded = true; - $_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; - $_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; + $geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); + echo $_REQUEST['geolocate']; + if (startsWith($geolocate, "-")) { + $locateparts = explode(",", $geolocate); + $_SESSION['lat'] = $locateparts[0]; + $_SESSION['lon'] = $locateparts[1]; } else { - $_SESSION['lat'] = ""; - $_SESSION['lon'] = ""; - } + $contents = geocode($geolocate, true); + print_r($contents); + if (isset($contents[0]->centroid)) { + $geocoded = true; + $_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; + $_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; + } + else { + $_SESSION['lat'] = ""; + $_SESSION['lon'] = ""; + } } } } @@ -66,6 +66,31 @@ function debug($msg, $debugReason = "other") { if (isDebug($debugReason)) echo "\n\n"; +} +function isJQueryMobileDevice() +{ + // http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897 + $user_agent = $_SERVER['HTTP_USER_AGENT']; + return preg_match('/iphone/i', $user_agent) + || preg_match('/android/i', $user_agent) + || preg_match('/webos/i', $user_agent) + || preg_match('/ios/i', $user_agent) + || preg_match('/bada/i', $user_agent) + || preg_match('/maemo/i', $user_agent) + || preg_match('/meego/i', $user_agent) + || preg_match('/fennec/i', $user_agent) + || (preg_match('/symbian/i', $user_agent) + && preg_match('/s60/i', $user_agent) + && $browser['majorver'] >= 5) + || (preg_match('/symbian/i', $user_agent) + && preg_match('/platform/i', $user_agent) + && $browser['majorver'] >= 3) + || (preg_match('/blackberry/i', $user_agent) + && $browser['majorver'] >= 5) + || (preg_match('/opera mobile/i', $user_agent) + && $browser['majorver'] >= 10) + || (preg_match('/opera mini/i', $user_agent) + && $browser['majorver'] >= 5); } function isFastDevice() { @@ -129,33 +154,27 @@ { return str_replace(")", "", str_replace("(", "
", $input)); } - -function sksort(&$array, $subkey="id", $sort_ascending=false) { - - if (count($array)) - $temp_array[key($array)] = array_shift($array); - - foreach($array as $key => $val){ - $offset = 0; - $found = false; - foreach($temp_array as $tmp_key => $tmp_val) - { - if(!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) - { - $temp_array = array_merge( (array)array_slice($temp_array,0,$offset), - array($key => $val), - array_slice($temp_array,$offset) - ); - $found = true; - } - $offset++; - } - if(!$found) $temp_array = array_merge($temp_array, array($key => $val)); - } - - if ($sort_ascending) $array = array_reverse($temp_array); - - else $array = $temp_array; +function sksort(&$array, $subkey = "id", $sort_ascending = false) +{ + if (count($array)) $temp_array[key($array) ] = array_shift($array); + foreach ($array as $key => $val) { + $offset = 0; + $found = false; + foreach ($temp_array as $tmp_key => $tmp_val) { + if (!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) { + $temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array( + $key => $val + ) , array_slice($temp_array, $offset)); + $found = true; + } + $offset++; + } + if (!$found) $temp_array = array_merge($temp_array, array( + $key => $val + )); + } + if ($sort_ascending) $array = array_reverse($temp_array); + else $array = $temp_array; } ?> --- /dev/null +++ b/ga.php @@ -1,1 +1,187 @@ + array( + "method" => "GET", + "user_agent" => $_SERVER["HTTP_USER_AGENT"], + "header" => ("Accepts-Language: " . $_SERVER["HTTP_ACCEPT_LANGUAGE"])) + ); + if (!empty($_GET["utmdebug"])) { + $data = file_get_contents( + $utmUrl, false, stream_context_create($options)); + } else { + $data = @file_get_contents( + $utmUrl, false, stream_context_create($options)); + } + } + + // Track a page view, updates all the cookies and campaign tracker, + // makes a server side request to Google Analytics and writes the transparent + // gif byte data to the response. + function trackPageView() { + $timeStamp = time(); + $domainName = $_SERVER["SERVER_NAME"]; + if (empty($domainName)) { + $domainName = ""; + } + + // Get the referrer from the utmr parameter, this is the referrer to the + // page that contains the tracking pixel, not the referrer for tracking + // pixel. + $documentReferer = $_GET["utmr"]; + if (empty($documentReferer) && $documentReferer !== "0") { + $documentReferer = "-"; + } else { + $documentReferer = urldecode($documentReferer); + } + $documentPath = $_GET["utmp"]; + if (empty($documentPath)) { + $documentPath = ""; + } else { + $documentPath = urldecode($documentPath); + } + + $account = $_GET["utmac"]; + $userAgent = $_SERVER["HTTP_USER_AGENT"]; + if (empty($userAgent)) { + $userAgent = ""; + } + + // Try and get visitor cookie from the request. + $cookie = $_COOKIE[COOKIE_NAME]; + + $guidHeader = $_SERVER["HTTP_X_DCMGUID"]; + if (empty($guidHeader)) { + $guidHeader = $_SERVER["HTTP_X_UP_SUBNO"]; + } + if (empty($guidHeader)) { + $guidHeader = $_SERVER["HTTP_X_JPHONE_UID"]; + } + if (empty($guidHeader)) { + $guidHeader = $_SERVER["HTTP_X_EM_UID"]; + } + + $visitorId = getVisitorId($guidHeader, $account, $userAgent, $cookie); + + // Always try and add the cookie to the response. + setrawcookie( + COOKIE_NAME, + $visitorId, + $timeStamp + COOKIE_USER_PERSISTENCE, + COOKIE_PATH); + + $utmGifLocation = "http://www.google-analytics.com/__utm.gif"; + + // Construct the gif hit url. + $utmUrl = $utmGifLocation . "?" . + "utmwv=" . VERSION . + "&utmn=" . getRandomNumber() . + "&utmhn=" . urlencode($domainName) . + "&utmr=" . urlencode($documentReferer) . + "&utmp=" . urlencode($documentPath) . + "&utmac=" . $account . + "&utmcc=__utma%3D999.999.999.999.999.1%3B" . + "&utmvid=" . $visitorId . + "&utmip=" . getIP($_SERVER["REMOTE_ADDR"]); + + sendRequestToGoogleAnalytics($utmUrl); + + // If the debug parameter is on, add a header to the response that contains + // the url that was used to contact Google Analytics. + if (!empty($_GET["utmdebug"])) { + header("X-GA-MOBILE-URL:" . $utmUrl); + } + // Finally write the gif data to the response. + writeGifData(); + } +?> + --- a/index.php +++ b/index.php @@ -5,9 +5,10 @@
-

busness time


Canberra Bus Timetables and Trip Planner
+

busness time


Canberra Bus Timetables and Trip Planner
- Launch Trip Planner... + + Launch Trip Planner... '; - foreach ($suburbs as $suburb) { - if (!isset($_REQUEST['firstLetter'])) { - foreach (range('A', 'Z') as $letter) { - echo "
  • $letter...
  • \n"; + if (!isset($_REQUEST['firstLetter'])) { + foreach (range('A', 'Z') as $letter) { + echo "
  • $letter...
  • \n"; + } + } + else { + foreach ($suburbs as $suburb) { + if (startsWith($suburb, $_REQUEST['firstLetter'])) { + echo '
  • ' . $suburb . '
  • '; } - } - else if (startsWith($suburb, $_REQUEST['firstLetter'])) { - echo '
  • ' . $suburb . '
  • '; } } echo ''; --- a/stop.php +++ b/stop.php @@ -48,7 +48,7 @@ } include_header($stop[1], "stop"); timePlaceSettings(); -echo '
    '; +echo '
    '; echo $stopLinks; if (sizeof($stops) > 0) { echo '

    ' . staticmap($stopPositions) . '

    '; @@ -72,10 +72,8 @@ foreach ($trips as $row) { echo '
  • '; echo '

    ' . $row[1][1]; - if (isFastDevice()) { - $viaPoints = viaPointNames($row[1][0], $stopid); - if ($viaPoints != "") echo '
    Via: ' . $viaPoints . ''; - } + $viaPoints = viaPointNames($row[1][0], $stopid); + if ($viaPoints != "") echo '
    Via: ' . $viaPoints . '
    '; if (sizeof($tripStopNumbers) > 0) { echo '
    Boarding At: '; foreach ($tripStopNumbers[$row[1][0]] as $key) { --- a/stopList.php +++ b/stopList.php @@ -1,7 +1,8 @@ '; - foreach ($suburbs as $suburb) { - if (!isset($_REQUEST['firstLetter'])) { - foreach (range('A', 'Z') as $letter) { - echo "
  • $letter...
  • \n"; + if (!isset($_REQUEST['firstLetter'])) { + foreach (range('A', 'Z') as $letter) { + echo "
  • $letter...
  • \n"; + } + } + else { + foreach ($suburbs as $suburb) { + if (startsWith($suburb, $_REQUEST['firstLetter'])) { + echo '
  • ' . $suburb . '
  • '; } - } - else if (startsWith($suburb, $_REQUEST['firstLetter'])) { - echo '
  • ' . $suburb . '
  • '; } } echo ''; @@ -48,6 +51,10 @@ include_header("Nearby Stops", "stopList"); navbar(); timePlaceSettings(true); + if (!isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "") { + include_footer(); + die(); + } } else if ($_REQUEST['suburb']) { $suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING); @@ -75,48 +82,47 @@ } // Sort the stops by name array_multisort($stopName, SORT_ASC, $stops); - if (!isset($_REQUEST['suburb']) && !isset($_REQUEST['nearby'])){ - $stops = array_filter($stops, "filterByFirstLetter"); - } + if (!isset($_REQUEST['suburb']) && !isset($_REQUEST['nearby'])) { + $stops = array_filter($stops, "filterByFirstLetter"); + } $stopsGrouped = Array(); foreach ($stops as $key => $row) { - if ((trim(preg_replace("/\(Platform.*/","",$stops[$key][1])) != trim(preg_replace("/\(Platform.*/","",$stops[$key + 1][1]))) || $key + 1 >= sizeof($stops)) { - if (sizeof($stopsGrouped) > 0) { - // print and empty grouped stops - // subsequent duplicates - $stopsGrouped["stop_ids"][] = $row[0]; - echo '
  • '; - - if (!startsWith($stopsGrouped['stop_codes'][0], "Wj")) echo 'Timing Point'; - echo ''; - if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { - echo '' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away'; - } - echo bracketsMeanNewLine(trim(preg_replace("/\(Platform.*/","",$row[1])) . '(' . sizeof($stopsGrouped["stop_ids"]) . ' stops)'); - echo "
  • \n"; - $stopsGrouped = Array(); + if ((trim(preg_replace("/\(Platform.*/", "", $stops[$key][1])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key + 1][1]))) || $key + 1 >= sizeof($stops)) { + if (sizeof($stopsGrouped) > 0) { + // print and empty grouped stops + // subsequent duplicates + $stopsGrouped["stop_ids"][] = $row[0]; + echo '
  • '; + if (!startsWith($stopsGrouped['stop_codes'][0], "Wj")) echo 'Timing Point: '; + echo ''; + if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { + echo '' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away'; } - else { - // just a normal stop - echo '
  • '; - if (!startsWith($row[5], "Wj")) echo 'Timing Point'; - echo ''; - if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { - echo '' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away'; - } - echo bracketsMeanNewLine($row[1]); - echo "
  • \n"; + echo bracketsMeanNewLine(trim(preg_replace("/\(Platform.*/", "", $row[1])) . '(' . sizeof($stopsGrouped["stop_ids"]) . ' stops)'); + echo "

  • \n"; + $stopsGrouped = Array(); + } + else { + // just a normal stop + echo '
  • '; + if (!startsWith($row[5], "Wj")) echo 'Timing Point'; + echo ''; + if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { + echo '' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away'; } - - } else { + echo bracketsMeanNewLine($row[1]); + echo "
  • \n"; + } + } + else { // this is a duplicated line item - if ($key - 1 <= 0 || (trim(preg_replace("/\(Platform.*/","",$stops[$key][1])) != trim(preg_replace("/\(Platform.*/","",$stops[$key - 1][1])))) { + if ($key - 1 <= 0 || (trim(preg_replace("/\(Platform.*/", "", $stops[$key][1])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key - 1][1])))) { // first duplicate $stopsGrouped = Array( - "name" => trim(preg_replace("/\(Platform.*/","",$row[1])), + "name" => trim(preg_replace("/\(Platform.*/", "", $row[1])) , "stop_ids" => Array( $row[0] - ), + ) , "stop_codes" => Array( $row[5] ) @@ -127,8 +133,7 @@ $stopsGrouped["stop_ids"][] = $row[0]; } } - - } + } } echo ''; } --- a/trip.php +++ b/trip.php @@ -34,12 +34,12 @@ $viaPoints[] = $stop[1]; } } -echo 'Via: ' . implode(", ", $viaPoints) . '
    '; -echo '

    Other Trips: '; +echo '

    Via:

    ' . implode(", ", $viaPoints) . '

    '; +echo '

    Other Trips:

    '; foreach ($routetrips as $othertrip) { echo '' . midnight_seconds_to_time($othertrip[0]) . ' '; } -echo '

    Other directions/timing periods: '; +echo '

    Other directions/timing periods:

    '; $url = $APIurl . "/json/routesearch?routeshortname=" . $trips[1]->route_short_name; $json = json_decode(getPage($url)); foreach ($json as $row) { --- a/tripPlanner.php +++ b/tripPlanner.php @@ -5,7 +5,10 @@ $to = (isset($_REQUEST['to']) ? filter_var($_REQUEST['to'], FILTER_SANITIZE_STRING) : "Barry"); $date = (isset($_REQUEST['date']) ? filter_var($_REQUEST['date'], FILTER_SANITIZE_STRING) : date("m/d/Y")); $time = (isset($_REQUEST['time']) ? filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING) : date("H:i")); -// todo: convert date from form to h:ia? +function formatTime($timeString) { + $timeParts = explode("T",$timeString); + return str_replace("Z","",$timeParts[1]); +} function tripPlanForm($errorMessage = "") { global $date, $time, $from, $to; @@ -40,7 +43,7 @@ } function processItinerary($itineraryNumber, $itinerary) { - echo '
    0 ? 'data-collapsed="true"' : "") . '>

    Option #' . ($itineraryNumber + 1) . ": " . floor($itinerary->duration / 60000) . " minutes ({$itinerary->startTime} to {$itinerary->endTime})

    "; + echo '

    0 ? 'data-collapsed="true"' : "") . '>

    Option #' . ($itineraryNumber + 1) . ": " . floor($itinerary->duration / 60000) . " minutes (".formatTime($itinerary->startTime)." to ".formatTime($itinerary->endTime).")

    "; echo "Walking time: " . floor($itinerary->walkTime / 60000) . " minutes (" . floor($itinerary->walkDistance) . " meters)
    \n"; echo "Transit time: " . floor($itinerary->transitTime / 60000) . " minutes
    \n"; echo "Waiting time: " . floor($itinerary->waitingTime / 60000) . " minutes
    \n"; @@ -119,7 +122,7 @@ $tripplan = json_decode($page); debug(print_r($triplan, true)); echo "

    From: {$tripplan->plan->from->name} To: {$tripplan->plan->to->name}

    "; - echo "

    At: {$tripplan->plan->date}

    "; + echo "

    At: ".formatTime($tripplan->plan->date)."

    "; if (is_array($tripplan->plan->itineraries->itinerary)) { echo '
    '; foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {