From: maxious Date: Fri, 22 Jul 2011 06:38:07 +0000 Subject: Show settings on less screens X-Git-Url: http://maxious.lambdacomplex.org/git/?p=busui.git&a=commitdiff&h=2217bb57770f794e6128f8aa0dbd08b3eda1322d --- Show settings on less screens --- --- a/about.php +++ b/about.php @@ -17,6 +17,10 @@ Feedback encouraged; contact maxious@lambdacomplex.org

Some icons by Joseph Wain / glyphish.com
+Native clients also available for iPhone(cbrTimetable by Sandor Kolotenko +, ACT Buses by David Sullivan) +and Android (MyBus 2.0 by Imagine Team) +

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 \ --- /dev/null +++ b/css/local.css.php @@ -1,1 +1,216 @@ - + + --- a/dotcloud/postinstall +++ b/dotcloud/postinstall @@ -3,8 +3,8 @@ curl http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ -o /home/dotcloud/current/cbrfeed.zip -wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ --O /tmp/Graph.obj +curl http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ +-o /tmp/Graph.obj #db setup #curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz --- a/include/common-geo.inc.php +++ b/include/common-geo.inc.php @@ -1,8 +1,9 @@ $mapPoint) { - $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1); + if ($twotone && $index == 0) { + $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . "iconr" . ($index + 1); + $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; + } + else { + $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1); + } if ($index + 1 != sizeof($mapPoints)) $markers.= "|"; - if ($mapPoint[0] < $minlat) $minlat = $mapPoint[0]; - if ($mapPoint[0] > $maxlat) $maxlat = $mapPoint[0]; - if ($mapPoint[1] < $minlon) $minlon = $mapPoint[1]; - if ($mapPoint[1] > $maxlon) $maxlon = $mapPoint[1]; + $dist = distance($mapPoints[0][0], $mapPoint[0][1], $mapPoint[0], $mapPoint[1]); + $mapwidthinmeters = ($dist > $mapwidthinmeters ? $dist : $mapwidthinmeters); $totalLat+= $mapPoint[0]; $totalLon+= $mapPoint[1]; } if ($zoom == 0) { $mapwidthinmeters = distance($minlat, $minlon, $minlat, $maxlon); foreach (array_reverse($metersperpixel, true) as $zoomLevel => $maxdistance) { - if ($zoom == 0 && $mapwidthinmeters < ($maxdistance + 50)) $zoom = $zoomLevel; + if ($zoom == 0 && $mapwidthinmeters * 1.5 < ($maxdistance)) $zoom = $zoomLevel; } } $center = $totalLat / sizeof($mapPoints) . "," . $totalLon / sizeof($mapPoints); } $output = ""; if ($collapsible) $output.= '

Open Map...

'; - $output.= ''; + $output.= ''; if ($collapsible) $output.= '
'; return $output; } @@ -66,11 +67,11 @@ $c = 2 * atan2(sqrt($a) , sqrt(1 - $a)); $km = $r * $c; if ($roundLargeValues) { - if ($km < 1) return floor($km * 1000); - else return round($km,2)."k"; - } else return floor($km * 1000); + if ($km < 1) return floor($km * 1000); + else return round($km, 2) . "k"; + } + else return floor($km * 1000); } - function decodePolylineToArray($encoded) { // source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5 --- a/include/common-request.inc.php +++ b/include/common-request.inc.php @@ -15,7 +15,7 @@ $nearby = true; } if (isset($_REQUEST['suburb'])) { - $suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING); + $suburb = $_REQUEST['suburb']; } $pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT); $lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); --- a/include/common-template.inc.php +++ b/include/common-template.inc.php @@ -23,47 +23,46 @@ $url.= "&guid=ON"; return str_replace("&", "&", $url); } - -$labsPath = ""; -if (strstr($_SERVER['PHP_SELF'],"labs")) $labsPath = "../"; - function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false) { -global $labsPath; + global $labsPath; echo ' - ' . $pageTitle . ' - - '; + +' . $pageTitle . ' + + + '; if (isDebugServer()) { - echo ' - - - + - '; - } - else { - echo ' - - - '; - } - echo ' - - - - - + + + - '; - echo ''; + echo ''; + echo ''; if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPad')) { echo ' @@ -211,10 +122,6 @@ $(document).ready(function() { $('#here').click(function(event) { $('#geolocate').val(geolocate()); return false;}); $('#here').show(); - /*if ($.mobile.media('screen and (min-width: 768px)')) { - $('map a:first').click(); - $('#settings a:first').click(); - }*/ }); "; if (!isset($_SESSION['lat']) || $_SESSION['lat'] == "") echo "geolocate();"; @@ -239,28 +146,26 @@
Back

' . $pageTitle . '

- Home + Home
'; $overrides = getServiceOverride(); if ($overrides['service_id']) { - if ($overrides['service_id'] == "noservice") { - echo '
Buses are not running today due to industrial action/public holiday. See Buses are not running today due to industrial action/public holiday. See http://www.action.act.gov.au for details.
'; - } - else { - echo '
Buses are running on an altered timetable today due to industrial action/public holiday. See http://www.action.act.gov.au for details.
'; - } + } + else { + echo '
Buses are running on an altered timetable today due to industrial action/public holiday. See http://www.action.act.gov.au for details.
'; } } - + } } function include_footer() { - -global $labsPath; - echo '
"; -} -function timePlaceSettings($geolocate = false) + } + echo "\n"; +} +function placeSettings() { global $service_periods; $geoerror = false; - if ($geolocate == true) { $geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == ""; - } + echo '
'; if ($geoerror) { echo 'Sorry, but your location could not currently be detected. @@ -292,27 +195,13 @@ } echo '
'; echo '
-

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

+

Change Location...

-
- - - Current Time? -
-
- - - -
--- a/include/common-transit.inc.php +++ b/include/common-transit.inc.php @@ -5,16 +5,16 @@ 'weekday' ); -function service_period() +function service_period($date = "") { if (isset($_SESSION['service_period'])) return $_SESSION['service_period']; - $override = getServiceOverride(); + $override = getServiceOverride($date); if ($override['service_id']){ return $override['service_id']; } - switch (date('w')) { + switch (date('w',($date != "" ? $date : time()))) { case 0: return 'sunday'; case 6: @@ -23,9 +23,12 @@ return 'weekday'; } } -function midnight_seconds() +function midnight_seconds($time = "") { // from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html + if ($time != "") { + return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time); + } if (isset($_SESSION['time'])) { $time = strtotime($_SESSION['time']); return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time); --- a/include/common.inc.php +++ b/include/common.inc.php @@ -6,7 +6,7 @@ "phperror", "awsotp", //"squallotp", - "vanilleotp", + //"vanilleotp", "database", "other" ); @@ -26,6 +26,13 @@ $otpAPIurl = 'http://10.0.1.135:8080/opentripplanner-api-webapp/'; } if (isDebug("phperror")) error_reporting(E_ALL ^ E_NOTICE); +$labsPath = ""; +if (strstr($_SERVER['PHP_SELF'],"labs")) $labsPath = "../"; + +function isDebugServer() +{ + return !isset($_SERVER['SERVER_NAME']) || $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "10.1.0.4" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" ; +} include_once ("common-geo.inc.php"); include_once ("common-net.inc.php"); @@ -36,10 +43,7 @@ include_once ("common-session.inc.php"); include_once ("common-template.inc.php"); -function isDebugServer() -{ - return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME']; -} + function isAnalyticsOn() { return !isDebugServer(); --- a/include/db/route-dao.inc.php +++ b/include/db/route-dao.inc.php @@ -14,6 +14,21 @@ return $query->fetch(PDO::FETCH_ASSOC); } +function getRouteByFullName($routeFullName) +{ + global $conn; + $query = "Select * from routes where route_short_name||route_long_name = :routeFullName LIMIT 1"; + debug($query, "database"); + $query = $conn->prepare($query); + $query->bindParam(":routeFullName", $routeFullName); + $query->execute(); + if (!$query) { + databaseError($conn->errorInfo()); + return Array(); + } + return $query->fetch(PDO::FETCH_ASSOC); +} + function getRoutes() { global $conn; @@ -32,7 +47,8 @@ global $conn; if ($routeNumber != "") { $query = "Select distinct routes.route_id,routes.route_short_name,routes.route_long_name,service_id from routes join trips on trips.route_id = -routes.route_id join stop_times on stop_times.trip_id = trips.trip_id where route_short_name = :routeNumber order by route_short_name;"; +routes.route_id join stop_times on stop_times.trip_id = trips.trip_id +where route_short_name = :routeNumber OR route_short_name LIKE :routeNumber2 order by route_short_name;"; } else { $query = "SELECT DISTINCT route_short_name from routes order by route_short_name"; @@ -41,6 +57,8 @@ $query = $conn->prepare($query); if ($routeNumber != "") { $query->bindParam(":routeNumber", $routeNumber); + $routeNumber2 = "% ".$routeNumber; + $query->bindParam(":routeNumber2", $routeNumber2); } $query->execute(); if (!$query) { --- a/include/db/servicealert-dao.inc.php +++ b/include/db/servicealert-dao.inc.php @@ -1,10 +1,10 @@ prepare($query); // Create a prepared statement - $query->bindParam(":date", date("Ymd")); + $query->bindParam(":date", date("Ymd",($date != "" ? $date : time()))); $query->execute(); if (!$query) { databaseError($conn->errorInfo()); @@ -12,4 +12,42 @@ } return $query->fetch(PDO::FETCH_ASSOC); } + +function getCurrentAlerts() { + global $conn; + $query = "SELECT * from servicealerts_alerts"; + //debug($query, "database"); + $query = $conn->prepare($query); + //if ($stop_sequence != "") $query->bindParam(":stop_sequence", $stop_sequence); + $query->execute(); + if (!$query) { + databaseError($conn->errorInfo()); + return Array(); + } + return $query->fetchAll(); +} +function getInformedAlerts($id,$filter_class,$filter_id) { + + global $conn; + $query = "SELECT * from servicealerts_informed where servicealert_id = :servicealert_id"; + + if ($filter_class != "" && $filter_id != "") { + $query .= " AND (informed_class = :informed_class OR informed_class = 'network') AND informed_id = :informed_id"; + + } + //debug($query, "database"); + $query = $conn->prepare($query); + if ($filter_class != "" && $filter_id != "") { + $query->bindParam(":informed_class", $filter_class); + $query->bindParam(":informed_id", $filter_id); + } + $query->bindParam(":servicealert_id", $id); + $query->execute(); + if (!$query) { + databaseError($conn->errorInfo()); + return Array(); + } + return $query->fetchAll(); +} + ?> --- a/include/db/stop-dao.inc.php +++ b/include/db/stop-dao.inc.php @@ -13,12 +13,13 @@ } return $query->fetch(PDO::FETCH_ASSOC); } -function getStops($timingPointsOnly = false, $firstLetter = "") +function getStops($timingPointsOnly = false, $firstLetter = "", $startsWith = "") { global $conn; $conditions = Array(); if ($timingPointsOnly) $conditions[] = "substr(stop_code,1,2) != 'Wj'"; if ($firstLetter != "") $conditions[] = "substr(stop_name,1,1) = :firstLetter"; + if ($startsWith != "") $conditions[] = "stop_name like :startsWith"; $query = "Select * from stops"; if (sizeof($conditions) > 0) { if (sizeof($conditions) > 1) { @@ -31,6 +32,11 @@ $query.= " order by stop_name;"; $query = $conn->prepare($query); if ($firstLetter != "") $query->bindParam(":firstLetter", $firstLetter); + + if ($startsWith != "") { + $startsWith = $startsWith."%"; + $query->bindParam(":startsWith", $startsWith); + } $query->execute(); if (!$query) { databaseError($conn->errorInfo()); @@ -72,11 +78,34 @@ } return $query->fetchAll(); } +function getStopsByStopCode($stop_code,$startsWith = "") +{ + global $conn; + $query = "Select * from stops where (stop_code = :stop_code OR stop_code LIKE :stop_code2)"; + if ($startsWith != "") $query .= " AND stop_name like :startsWith"; + + debug($query, "database"); + $query = $conn->prepare($query); + + $query->bindParam(":stop_code", $stop_code); + $stop_code2 = $stop_code . "%"; + $query->bindParam(":stop_code2", $stop_code2); + if ($startsWith != "") { + $startsWith = $startsWith."%"; + $query->bindParam(":startsWith", $startsWith); + } + $query->execute(); + if (!$query) { + databaseError($conn->errorInfo()); + return Array(); + } + return $query->fetchAll(); +} function getStopRoutes($stopID, $service_period) { if ($service_period == "") $service_period = service_period(); global $conn; - $query = "SELECT service_id,trips.route_id,route_short_name,route_long_name + $query = "SELECT distinct service_id,trips.route_id,route_short_name,route_long_name FROM stop_times join trips on trips.trip_id = stop_times.trip_id join routes on trips.route_id = routes.route_id WHERE stop_id = :stopID AND service_id=:service_period"; debug($query, "database"); @@ -90,9 +119,10 @@ } return $query->fetchAll(); } -function getStopTrips($stopID, $service_period = "", $afterTime = "") +function getStopTrips($stopID, $service_period = "", $afterTime = "", $limit = "") { if ($service_period == "") $service_period = service_period(); + if ($limit != "") $limitSQL = " LIMIT :limit "; global $conn; if ($afterTime != "") { $query = " SELECT stop_times.trip_id,stop_times.arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name, end_times.arrival_time as end_time @@ -105,7 +135,7 @@ AND stop_times.trip_id = end_times.trip_id AND service_id=:service_period AND end_times.arrival_time > :afterTime -ORDER BY end_time"; +ORDER BY end_time $limitSQL"; } else { $query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name @@ -115,12 +145,13 @@ join routes on trips.route_id = routes.route_id WHERE stop_times.stop_id = :stopID AND service_id=:service_period -ORDER BY arrival_time"; +ORDER BY arrival_time $limitSQL"; } debug($query, "database"); $query = $conn->prepare($query); $query->bindParam(":service_period", $service_period); $query->bindParam(":stopID", $stopID); + if ($limit != "") $query->bindParam(":limit", $limit); if ($afterTime != "") $query->bindParam(":afterTime", $afterTime); $query->execute(); if (!$query) { --- a/include/db/trip-dao.inc.php +++ b/include/db/trip-dao.inc.php @@ -75,8 +75,9 @@ $distance_between_timepoints+= distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]); } $next_timepoint = $stopTimes[$k]; - $rv[] = $stopTime; + } + $rv[] = $stopTime; } else { // is untimed point @@ -94,10 +95,11 @@ $stopTime["arrival_time"] = $cur_timepoint["arrival_time"]; } $rv[] = $stopTime; - //var_dump($rv); + } } + //var_dump($rv); return $rv; } function getTripPreviousTimePoint($tripID, $stop_sequence) @@ -144,7 +146,10 @@ // limit interpolation to between nearest actual points. $prevTimePoint = getTripPreviousTimePoint($tripID, $stop_sequence); $nextTimePoint = getTripNextTimePoint($tripID, $stop_sequence); - $range = "AND stop_sequence >= '{$prevTimePoint['stop_sequence']}' AND stop_sequence <= '{$nextTimePoint['stop_sequence']}'"; + //echo " prev {$lowestDelta['stop_sequence']} next {$nextTimePoint['stop_sequence']} "; + $range = ""; + if ($prevTimePoint != "") $range .= " AND stop_sequence >= '{$prevTimePoint['stop_sequence']}'"; + if ($nextTimePoint != "") $range .= " AND stop_sequence <= '{$nextTimePoint['stop_sequence']}'"; foreach (getTimeInterpolatedTrip($tripID, $range) as $tripStop) { if ($tripStop['stop_sequence'] == $stop_sequence) return $tripStop; } --- a/index.php +++ b/index.php @@ -24,7 +24,6 @@
  • Nearby Routes
  • Busness R&D'; include_footer(true) ?> --- /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;b0){for(var a=0;a=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); --- /dev/null +++ b/js/flot/excanvas.js @@ -1,1 +1,1428 @@ - +// Copyright 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +// Known Issues: +// +// * Patterns only support repeat. +// * Radial gradient are not implemented. The VML version of these look very +// different from the canvas one. +// * Clipping paths are not implemented. +// * Coordsize. The width and height attribute have higher priority than the +// width and height style values which isn't correct. +// * Painting mode isn't implemented. +// * Canvas width/height should is using content-box by default. IE in +// Quirks mode will draw the canvas using border-box. Either change your +// doctype to HTML5 +// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) +// or use Box Sizing Behavior from WebFX +// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) +// * Non uniform scaling does not correctly scale strokes. +// * Filling very large shapes (above 5000 points) is buggy. +// * Optimize. There is always room for speed improvements. + +// Only add this code if we do not already have a canvas implementation +if (!document.createElement('canvas').getContext) { + +(function() { + + // alias some functions to make (compiled) code shorter + var m = Math; + var mr = m.round; + var ms = m.sin; + var mc = m.cos; + var abs = m.abs; + var sqrt = m.sqrt; + + // this is used for sub pixel precision + var Z = 10; + var Z2 = Z / 2; + + /** + * This funtion is assigned to the elements as element.getContext(). + * @this {HTMLElement} + * @return {CanvasRenderingContext2D_} + */ + function getContext() { + return this.context_ || + (this.context_ = new CanvasRenderingContext2D_(this)); + } + + var slice = Array.prototype.slice; + + /** + * Binds a function to an object. The returned function will always use the + * passed in {@code obj} as {@code this}. + * + * Example: + * + * g = bind(f, obj, a, b) + * g(c, d) // will do f.call(obj, a, b, c, d) + * + * @param {Function} f The function to bind the object to + * @param {Object} obj The object that should act as this when the function + * is called + * @param {*} var_args Rest arguments that will be used as the initial + * arguments when the function is called + * @return {Function} A new function that has bound this + */ + function bind(f, obj, var_args) { + var a = slice.call(arguments, 2); + return function() { + return f.apply(obj, a.concat(slice.call(arguments))); + }; + } + + function encodeHtmlAttribute(s) { + return String(s).replace(/&/g, '&').replace(/"/g, '"'); + } + + function addNamespacesAndStylesheet(doc) { + // create xmlns + if (!doc.namespaces['g_vml_']) { + doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', + '#default#VML'); + + } + if (!doc.namespaces['g_o_']) { + doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', + '#default#VML'); + } + + // Setup default CSS. Only add one style sheet per document + if (!doc.styleSheets['ex_canvas_']) { + var ss = doc.createStyleSheet(); + ss.owningElement.id = 'ex_canvas_'; + ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + + // default size is 300x150 in Gecko and Opera + 'text-align:left;width:300px;height:150px}'; + } + } + + // Add namespaces and stylesheet at startup. + addNamespacesAndStylesheet(document); + + var G_vmlCanvasManager_ = { + init: function(opt_doc) { + if (/MSIE/.test(navigator.userAgent) && !window.opera) { + var doc = opt_doc || document; + // Create a dummy element so that IE will allow canvas elements to be + // recognized. + doc.createElement('canvas'); + doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); + } + }, + + init_: function(doc) { + // find all canvas elements + var els = doc.getElementsByTagName('canvas'); + for (var i = 0; i < els.length; i++) { + this.initElement(els[i]); + } + }, + + /** + * Public initializes a canvas element so that it can be used as canvas + * element from now on. This is called automatically before the page is + * loaded but if you are creating elements using createElement you need to + * make sure this is called on the element. + * @param {HTMLElement} el The canvas element to initialize. + * @return {HTMLElement} the element that was created. + */ + initElement: function(el) { + if (!el.getContext) { + el.getContext = getContext; + + // Add namespaces and stylesheet to document of the element. + addNamespacesAndStylesheet(el.ownerDocument); + + // Remove fallback content. There is no way to hide text nodes so we + // just remove all childNodes. We could hide all elements and remove + // text nodes but who really cares about the fallback content. + el.innerHTML = ''; + + // do not use inline function because that will leak memory + el.attachEvent('onpropertychange', onPropertyChange); + el.attachEvent('onresize', onResize); + + var attrs = el.attributes; + if (attrs.width && attrs.width.specified) { + // TODO: use runtimeStyle and coordsize + // el.getContext().setWidth_(attrs.width.nodeValue); + el.style.width = attrs.width.nodeValue + 'px'; + } else { + el.width = el.clientWidth; + } + if (attrs.height && attrs.height.specified) { + // TODO: use runtimeStyle and coordsize + // el.getContext().setHeight_(attrs.height.nodeValue); + el.style.height = attrs.height.nodeValue + 'px'; + } else { + el.height = el.clientHeight; + } + //el.getContext().setCoordsize_() + } + return el; + } + }; + + function onPropertyChange(e) { + var el = e.srcElement; + + switch (e.propertyName) { + case 'width': + el.getContext().clearRect(); + el.style.width = el.attributes.width.nodeValue + 'px'; + // In IE8 this does not trigger onresize. + el.firstChild.style.width = el.clientWidth + 'px'; + break; + case 'height': + el.getContext().clearRect(); + el.style.height = el.attributes.height.nodeValue + 'px'; + el.firstChild.style.height = el.clientHeight + 'px'; + break; + } + } + + function onResize(e) { + var el = e.srcElement; + if (el.firstChild) { + el.firstChild.style.width = el.clientWidth + 'px'; + el.firstChild.style.height = el.clientHeight + 'px'; + } + } + + G_vmlCanvasManager_.init(); + + // precompute "00" to "FF" + var decToHex = []; + for (var i = 0; i < 16; i++) { + for (var j = 0; j < 16; j++) { + decToHex[i * 16 + j] = i.toString(16) + j.toString(16); + } + } + + function createMatrixIdentity() { + return [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1] + ]; + } + + function matrixMultiply(m1, m2) { + var result = createMatrixIdentity(); + + for (var x = 0; x < 3; x++) { + for (var y = 0; y < 3; y++) { + var sum = 0; + + for (var z = 0; z < 3; z++) { + sum += m1[x][z] * m2[z][y]; + } + + result[x][y] = sum; + } + } + return result; + } + + function copyState(o1, o2) { + o2.fillStyle = o1.fillStyle; + o2.lineCap = o1.lineCap; + o2.lineJoin = o1.lineJoin; + o2.lineWidth = o1.lineWidth; + o2.miterLimit = o1.miterLimit; + o2.shadowBlur = o1.shadowBlur; + o2.shadowColor = o1.shadowColor; + o2.shadowOffsetX = o1.shadowOffsetX; + o2.shadowOffsetY = o1.shadowOffsetY; + o2.strokeStyle = o1.strokeStyle; + o2.globalAlpha = o1.globalAlpha; + o2.font = o1.font; + o2.textAlign = o1.textAlign; + o2.textBaseline = o1.textBaseline; + o2.arcScaleX_ = o1.arcScaleX_; + o2.arcScaleY_ = o1.arcScaleY_; + o2.lineScale_ = o1.lineScale_; + } + + var colorData = { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + grey: '#808080', + greenyellow: '#ADFF2F', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + oldlace: '#FDF5E6', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + whitesmoke: '#F5F5F5', + yellowgreen: '#9ACD32' + }; + + + function getRgbHslContent(styleString) { + var start = styleString.indexOf('(', 3); + var end = styleString.indexOf(')', start + 1); + var parts = styleString.substring(start + 1, end).split(','); + // add alpha if needed + if (parts.length == 4 && styleString.substr(3, 1) == 'a') { + alpha = Number(parts[3]); + } else { + parts[3] = 1; + } + return parts; + } + + function percent(s) { + return parseFloat(s) / 100; + } + + function clamp(v, min, max) { + return Math.min(max, Math.max(min, v)); + } + + function hslToRgb(parts){ + var r, g, b; + h = parseFloat(parts[0]) / 360 % 360; + if (h < 0) + h++; + s = clamp(percent(parts[1]), 0, 1); + l = clamp(percent(parts[2]), 0, 1); + if (s == 0) { + r = g = b = l; // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hueToRgb(p, q, h + 1 / 3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1 / 3); + } + + return '#' + decToHex[Math.floor(r * 255)] + + decToHex[Math.floor(g * 255)] + + decToHex[Math.floor(b * 255)]; + } + + function hueToRgb(m1, m2, h) { + if (h < 0) + h++; + if (h > 1) + h--; + + if (6 * h < 1) + return m1 + (m2 - m1) * 6 * h; + else if (2 * h < 1) + return m2; + else if (3 * h < 2) + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + else + return m1; + } + + function processStyle(styleString) { + var str, alpha = 1; + + styleString = String(styleString); + if (styleString.charAt(0) == '#') { + str = styleString; + } else if (/^rgb/.test(styleString)) { + var parts = getRgbHslContent(styleString); + var str = '#', n; + for (var i = 0; i < 3; i++) { + if (parts[i].indexOf('%') != -1) { + n = Math.floor(percent(parts[i]) * 255); + } else { + n = Number(parts[i]); + } + str += decToHex[clamp(n, 0, 255)]; + } + alpha = parts[3]; + } else if (/^hsl/.test(styleString)) { + var parts = getRgbHslContent(styleString); + str = hslToRgb(parts); + alpha = parts[3]; + } else { + str = colorData[styleString] || styleString; + } + return {color: str, alpha: alpha}; + } + + var DEFAULT_STYLE = { + style: 'normal', + variant: 'normal', + weight: 'normal', + size: 10, + family: 'sans-serif' + }; + + // Internal text style cache + var fontStyleCache = {}; + + function processFontStyle(styleString) { + if (fontStyleCache[styleString]) { + return fontStyleCache[styleString]; + } + + var el = document.createElement('div'); + var style = el.style; + try { + style.font = styleString; + } catch (ex) { + // Ignore failures to set to invalid font. + } + + return fontStyleCache[styleString] = { + style: style.fontStyle || DEFAULT_STYLE.style, + variant: style.fontVariant || DEFAULT_STYLE.variant, + weight: style.fontWeight || DEFAULT_STYLE.weight, + size: style.fontSize || DEFAULT_STYLE.size, + family: style.fontFamily || DEFAULT_STYLE.family + }; + } + + function getComputedStyle(style, element) { + var computedStyle = {}; + + for (var p in style) { + computedStyle[p] = style[p]; + } + + // Compute the size + var canvasFontSize = parseFloat(element.currentStyle.fontSize), + fontSize = parseFloat(style.size); + + if (typeof style.size == 'number') { + computedStyle.size = style.size; + } else if (style.size.indexOf('px') != -1) { + computedStyle.size = fontSize; + } else if (style.size.indexOf('em') != -1) { + computedStyle.size = canvasFontSize * fontSize; + } else if(style.size.indexOf('%') != -1) { + computedStyle.size = (canvasFontSize / 100) * fontSize; + } else if (style.size.indexOf('pt') != -1) { + computedStyle.size = fontSize / .75; + } else { + computedStyle.size = canvasFontSize; + } + + // Different scaling between normal text and VML text. This was found using + // trial and error to get the same size as non VML text. + computedStyle.size *= 0.981; + + return computedStyle; + } + + function buildStyle(style) { + return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + + style.size + 'px ' + style.family; + } + + function processLineCap(lineCap) { + switch (lineCap) { + case 'butt': + return 'flat'; + case 'round': + return 'round'; + case 'square': + default: + return 'square'; + } + } + + /** + * This class implements CanvasRenderingContext2D interface as described by + * the WHATWG. + * @param {HTMLElement} surfaceElement The element that the 2D context should + * be associated with + */ + function CanvasRenderingContext2D_(surfaceElement) { + this.m_ = createMatrixIdentity(); + + this.mStack_ = []; + this.aStack_ = []; + this.currentPath_ = []; + + // Canvas context properties + this.strokeStyle = '#000'; + this.fillStyle = '#000'; + + this.lineWidth = 1; + this.lineJoin = 'miter'; + this.lineCap = 'butt'; + this.miterLimit = Z * 1; + this.globalAlpha = 1; + this.font = '10px sans-serif'; + this.textAlign = 'left'; + this.textBaseline = 'alphabetic'; + this.canvas = surfaceElement; + + var el = surfaceElement.ownerDocument.createElement('div'); + el.style.width = surfaceElement.clientWidth + 'px'; + el.style.height = surfaceElement.clientHeight + 'px'; + el.style.overflow = 'hidden'; + el.style.position = 'absolute'; + surfaceElement.appendChild(el); + + this.element_ = el; + this.arcScaleX_ = 1; + this.arcScaleY_ = 1; + this.lineScale_ = 1; + } + + var contextPrototype = CanvasRenderingContext2D_.prototype; + contextPrototype.clearRect = function() { + if (this.textMeasureEl_) { + this.textMeasureEl_.removeNode(true); + this.textMeasureEl_ = null; + } + this.element_.innerHTML = ''; + }; + + contextPrototype.beginPath = function() { + // TODO: Branch current matrix so that save/restore has no effect + // as per safari docs. + this.currentPath_ = []; + }; + + contextPrototype.moveTo = function(aX, aY) { + var p = this.getCoords_(aX, aY); + this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); + this.currentX_ = p.x; + this.currentY_ = p.y; + }; + + contextPrototype.lineTo = function(aX, aY) { + var p = this.getCoords_(aX, aY); + this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); + + this.currentX_ = p.x; + this.currentY_ = p.y; + }; + + contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY) { + var p = this.getCoords_(aX, aY); + var cp1 = this.getCoords_(aCP1x, aCP1y); + var cp2 = this.getCoords_(aCP2x, aCP2y); + bezierCurveTo(this, cp1, cp2, p); + }; + + // Helper function that takes the already fixed cordinates. + function bezierCurveTo(self, cp1, cp2, p) { + self.currentPath_.push({ + type: 'bezierCurveTo', + cp1x: cp1.x, + cp1y: cp1.y, + cp2x: cp2.x, + cp2y: cp2.y, + x: p.x, + y: p.y + }); + self.currentX_ = p.x; + self.currentY_ = p.y; + } + + contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { + // the following is lifted almost directly from + // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes + + var cp = this.getCoords_(aCPx, aCPy); + var p = this.getCoords_(aX, aY); + + var cp1 = { + x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), + y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) + }; + var cp2 = { + x: cp1.x + (p.x - this.currentX_) / 3.0, + y: cp1.y + (p.y - this.currentY_) / 3.0 + }; + + bezierCurveTo(this, cp1, cp2, p); + }; + + contextPrototype.arc = function(aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise) { + aRadius *= Z; + var arcType = aClockwise ? 'at' : 'wa'; + + var xStart = aX + mc(aStartAngle) * aRadius - Z2; + var yStart = aY + ms(aStartAngle) * aRadius - Z2; + + var xEnd = aX + mc(aEndAngle) * aRadius - Z2; + var yEnd = aY + ms(aEndAngle) * aRadius - Z2; + + // IE won't render arches drawn counter clockwise if xStart == xEnd. + if (xStart == xEnd && !aClockwise) { + xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something + // that can be represented in binary + } + + var p = this.getCoords_(aX, aY); + var pStart = this.getCoords_(xStart, yStart); + var pEnd = this.getCoords_(xEnd, yEnd); + + this.currentPath_.push({type: arcType, + x: p.x, + y: p.y, + radius: aRadius, + xStart: pStart.x, + yStart: pStart.y, + xEnd: pEnd.x, + yEnd: pEnd.y}); + + }; + + contextPrototype.rect = function(aX, aY, aWidth, aHeight) { + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + }; + + contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { + var oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.stroke(); + + this.currentPath_ = oldPath; + }; + + contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { + var oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.fill(); + + this.currentPath_ = oldPath; + }; + + contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { + var gradient = new CanvasGradient_('gradient'); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + return gradient; + }; + + contextPrototype.createRadialGradient = function(aX0, aY0, aR0, + aX1, aY1, aR1) { + var gradient = new CanvasGradient_('gradientradial'); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.r0_ = aR0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + gradient.r1_ = aR1; + return gradient; + }; + + contextPrototype.drawImage = function(image, var_args) { + var dx, dy, dw, dh, sx, sy, sw, sh; + + // to find the original width we overide the width and height + var oldRuntimeWidth = image.runtimeStyle.width; + var oldRuntimeHeight = image.runtimeStyle.height; + image.runtimeStyle.width = 'auto'; + image.runtimeStyle.height = 'auto'; + + // get the original size + var w = image.width; + var h = image.height; + + // and remove overides + image.runtimeStyle.width = oldRuntimeWidth; + image.runtimeStyle.height = oldRuntimeHeight; + + if (arguments.length == 3) { + dx = arguments[1]; + dy = arguments[2]; + sx = sy = 0; + sw = dw = w; + sh = dh = h; + } else if (arguments.length == 5) { + dx = arguments[1]; + dy = arguments[2]; + dw = arguments[3]; + dh = arguments[4]; + sx = sy = 0; + sw = w; + sh = h; + } else if (arguments.length == 9) { + sx = arguments[1]; + sy = arguments[2]; + sw = arguments[3]; + sh = arguments[4]; + dx = arguments[5]; + dy = arguments[6]; + dw = arguments[7]; + dh = arguments[8]; + } else { + throw Error('Invalid number of arguments'); + } + + var d = this.getCoords_(dx, dy); + + var w2 = sw / 2; + var h2 = sh / 2; + + var vmlStr = []; + + var W = 10; + var H = 10; + + // For some reason that I've now forgotten, using divs didn't work + vmlStr.push('