From 4eeb0036ecd4497d3900f46a7c5371d0e6a0bad6 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Mon, 23 Aug 2021 20:22:51 +0200 Subject: Add trip detail page --- bin/nvm | 154 ++++++++++++++++++++++++++++++++++++++++- sass/app.scss | 91 +++++++++++++++++++++++- sass/dark.scss | 4 ++ sass/light.scss | 6 +- static/css/dark.min.css | 2 +- static/css/light.min.css | 2 +- static/v4 | 1 - static/v6 | 1 + templates/departure_list.html | 2 +- templates/departures_page.html | 2 +- templates/header.html | 4 +- templates/landing_page.html | 2 +- templates/tripinfo.html | 72 +++++++++++++++++++ templates/tripinfo_page.html | 32 +++++++++ 14 files changed, 362 insertions(+), 13 deletions(-) delete mode 120000 static/v4 create mode 120000 static/v6 create mode 100644 templates/tripinfo.html create mode 100644 templates/tripinfo_page.html diff --git a/bin/nvm b/bin/nvm index 3df9013..41ac8c0 100755 --- a/bin/nvm +++ b/bin/nvm @@ -152,6 +152,114 @@ class TransportAPIs: return None +class Trip: + def __init__(self, obj): + + for key in "departure plannedDeparture arrival plannedArrival".split(): + try: + obj[key] = dateutil.parser.parse(obj[key]) + except TypeError: + obj[key] = None + + self.cancelled = None + self.__dict__.update(obj) + + if "," in self.direction: + self.direction, self.suffix = self.direction.split(",", maxsplit=1) + else: + self.suffix = None + + self.stopovers = list(map(Stopover, self.stopovers)) + self.where = None + self.quoted_where = None + + self.platform = None + self.plannedPlatform = None + self.platform_changed = None + + def set_ref(self, stop_name): + self.where = stop_name + self.quoted_where = aiohttp.helpers.quote(self.where) + ref_pos = -1 + for stopover in self.stopovers: + if stopover.stop["name"] == stop_name: + ref_pos = 0 + self.arrival = stopover.arrival + self.arrivalDelay = stopover.arrivalDelay + self.plannedArrival = stopover.plannedArrival + self.departure = stopover.departure + self.departureDelay = stopover.departureDelay + self.plannedDeparture = stopover.plannedDeparture + self.platform = stopover.platform + self.plannedPlatform = stopover.plannedPlatform + self.platform_changed = stopover.platform_changed + elif ref_pos == 0: + ref_pos = 1 + stopover.set_ref(ref_pos) + + def set_relative(self, now_ts): + for stopover in self.stopovers: + stopover.set_relative(now_ts) + + +class Stopover: + def __init__(self, obj): + + for key in "departure plannedDeparture arrival plannedArrival".split(): + try: + obj[key] = dateutil.parser.parse(obj[key]) + except TypeError: + obj[key] = None + + self.cancelled = None + self.__dict__.update(obj) + + if "," in self.stop["name"]: + self.name, self.suffix = self.stop["name"].split(",", maxsplit=1) + else: + self.name = self.stop["name"] + self.suffix = None + + if self.arrivalPlatform: + self.platform = self.arrivalPlatform + elif self.departurePlatform: + self.platform = departurePlatform + else: + self.platform = None + + if self.plannedArrivalPlatform: + self.plannedPlatform = self.plannedArrivalPlatform + elif self.plannedDeparturePlatform: + self.plannedPlatform = self.plannedDeparturePlatform + else: + self.plannedPlatform = None + + self.platform_changed = self.platform != self.plannedPlatform + + self.is_requested_stop = False + self.ref_pos = None + self.is_future = None + + def set_ref(self, ref_pos): + self.ref_pos = ref_pos + if ref_pos <= 0: + self.when = self.departure + self.plannedWhen = self.plannedDeparture + self.delay = self.departureDelay + else: + self.when = self.arrival + self.plannedWhen = self.plannedArrival + self.delay = self.arrivalDelay + if ref_pos == 0: + self.is_requested_stop = True + + def set_relative(self, now_ts): + if self.arrival is not None: + self.is_future = self.arrival.timestamp() > now_ts + elif self.departure is not None: + self.is_future = self.departure.timestamp() > now_ts + + class Departure: def __init__(self, obj): self.__dict__.update(obj) @@ -166,8 +274,6 @@ class Departure: self.stop_name = obj.get("stop", dict()).get("name", None) self.station_name = obj.get("station", dict()).get("name", self.stop_name) - self.quoted_stop_name = aiohttp.helpers.quote(self.stop_name) - try: self.location = ( obj["stop"]["location"]["longitude"], @@ -208,6 +314,14 @@ class Departure: self.delay = self.delay // 60 self.delay = f"{self.delay:+.0f}" + self.quoted_line_name = aiohttp.helpers.quote(self.line.name) + self.quoted_stop_name = aiohttp.helpers.quote(self.stop_name) + + def quoted_platform(self): + if self.platform: + return aiohttp.helpers.quote(self.platform) + return "" + def __repr__(self): return f"Departure" @@ -256,6 +370,8 @@ class Line: elif self.product == "bus": if self.name.startswith("Bus "): self.name = self.name[4:] + elif self.name.startswith("Bus"): + self.name = self.name[3:] self.css_class = "bus" def __repr__(self): @@ -286,6 +402,39 @@ def meta_privacy(request): ) +async def show_trip_info(request, trip_id=None): + if trip_id is None: + trip_id = request.match_info.get("tripid") + request_url = f"{db_rest_api}/trips/{trip_id}?lineName=0" + logging.debug(f"Requesting trip info from {request_url}") + async with aiohttp.ClientSession() as session: + async with session.get(request_url) as response: + tripinfo = await response.json() + + tripinfo = Trip(tripinfo) + + if request.query.get("highlight", None): + tripinfo.set_ref(request.query.get("highlight")) + + if not tripinfo.platform and request.query.get("platform", None): + tripinfo.platform = request.query.get("platform") + + now = datetime.now() + now_ts = now.timestamp() + tripinfo.set_relative(now_ts) + + tripinfo_page = env.get_template("tripinfo_page.html") + + return web.Response( + body=tripinfo_page.render( + title=tripinfo.line["name"] + " ➔ " + tripinfo.direction, + tripinfo=tripinfo, + version=nvm_version, + ), + headers=headers, + ) + + async def show_departure_board(request, eva=None): if eva is None: @@ -473,5 +622,6 @@ if __name__ == "__main__": app.router.add_get(f"{args.prefix}meta/about", meta_about) app.router.add_get(f"{args.prefix}meta/imprint", meta_imprint) app.router.add_get(f"{args.prefix}meta/privacy", meta_privacy) + app.router.add_get(f"{args.prefix}trip/{{tripid}}", show_trip_info) app.router.add_static(f"{args.prefix}static", "static") web.run_app(app, host="localhost", port=args.port) diff --git a/sass/app.scss b/sass/app.scss index c5391a2..be78af3 100644 --- a/sass/app.scss +++ b/sass/app.scss @@ -67,6 +67,32 @@ input[type="text"] { box-sizing: border-box; } +.smallbutton { + display: inline-block; + vertical-align: baseline; + border-radius: 4px; + border: 1px solid #2e6da4; + transition: background-color .3s; + color: #fff; + background-color: #337ab7; + cursor: pointer; + box-shadow: none; + padding: 0.9ex; + margin-right: 1em; + + &:active, &:focus, &:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; + } + + .material-icons { + display: block; + float: left; + margin-right: 0.5ex; + } +} + .globalnote { margin-top: 1em; font-style: italic; @@ -328,7 +354,7 @@ ul.departures { .delay { padding-right: 1ex; - color: #ff0000; + color: $delay-color; } } @@ -344,6 +370,67 @@ ul.departures { } } +.tripinfo { + padding-left: 1em; + padding-right: 1em; + .direction { + text-align: center; + font-size: 120%; + padding-top: 0.5em; + padding-bottom: 0.5em; + border-bottom: 0.1em dashed #cccccc; + } + .dataline { + margin-top: 0.6em; + font-size: 120%; + width: 100%; + display: flex; + justify-content: space-between; + margin-bottom: 1em; + + > div { + width: 33%; + } + + .arrival { + display: inline-block; + text-align: right; + } + .platform { + text-align: center; + } + .departure { + text-align: right; + } + + .delay { + color: $delay-color; + } + } + .linklist { + margin-top: 0.5em; + margin-bottom: 1em; + .material-icons { + font-size: 20px; + } + } + .route { + margin-top: 1em; + .past-stop { + list-style-type: disc; + } + .future-stop { + list-style-type: circle; + } + .this-stop { + font-weight: bold; + } + .cancelled-stop { + color: $cancelled-stop-color; + } + } +} + .navbar-fixed { position: relative; z-index: 997; @@ -398,7 +485,7 @@ ul.departures { font-weight: 400; src: local('Material Icons'), local('MaterialIcons-Regular'), - url(/static/v5/font/MaterialIcons-Regular.ttf) format('truetype'); + url(/static/v6/font/MaterialIcons-Regular.ttf) format('truetype'); } .material-icons { diff --git a/sass/dark.scss b/sass/dark.scss index 1b90571..34b82f4 100644 --- a/sass/dark.scss +++ b/sass/dark.scss @@ -23,6 +23,10 @@ $suburban-bg: #2f6639; $subway-bg: #2045b0; $bus-bg: #7f3d7f; +$cancelled-stop-color: #ff7777; $cancelled-bg-color: #512f00; +$delay-color: #ff7777; +$undelay-color: #77ff77; + @import 'app.scss'; diff --git a/sass/light.scss b/sass/light.scss index f080ddc..8061206 100644 --- a/sass/light.scss +++ b/sass/light.scss @@ -19,10 +19,14 @@ $longdistance-bg: #ffcccc; $regional-bg: #eeeeee; $tram-bg: #ffcccc; $taxi-bg: #eeaaee; -$suburban-bg: #aaffba; +$suburban-bg: #9fd79f; $subway-bg: #aac0ff; $bus-bg: #eeaaee; +$cancelled-stop-color: #cc0000; $cancelled-bg-color: #ffe7d0; +$delay-color: #ff0000; +$undelay-color: #006600; + @import 'app.scss'; diff --git a/static/css/dark.min.css b/static/css/dark.min.css index 7364729..61b17b3 100644 --- a/static/css/dark.min.css +++ b/static/css/dark.min.css @@ -1 +1 @@ -body{margin:0;color:#fff;background-color:#101010}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.textcontent{margin-left:1ex;margin-right:1ex}a{color:#99f;text-decoration:none}input,select,button{display:block;width:100%;max-width:100%;min-height:1.8em;border-radius:4px;font-size:90%;color:#fff;background-color:#101010;border:1px solid #444;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);margin-left:auto;margin-right:auto;text-align:center;vertical-align:middle;padding-top:1ex;padding-bottom:1ex;margin-top:1ex;margin-bottom:1ex}button{transition:background-color .3s;color:#fff;background-color:#337ab7;border-color:#2e6da4;cursor:pointer;box-shadow:none}button:active,button:focus,button:hover{color:#fff;background-color:#286090;border-color:#204d74}input[type="text"]{padding-left:0.5em;padding-right:0.5em;text-align:left;box-sizing:border-box}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops>li{border-bottom:1px solid grey;background-color:#101010}ul.stops>li a{color:#fff;text-decoration:none;display:block;height:4em;width:100%;position:relative}ul.stops>li a .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%}ul.stops>li a .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li a .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li a .lines .longdistance{border:0.1em solid #7c1a1a}ul.stops>li a .lines .regional{background-color:#444}ul.stops>li a .lines .tram{background-color:#852121}ul.stops>li a .lines .taxi{background-color:#7f3d7f;font-weight:normal}ul.stops>li a .lines .suburban{background-color:#2f6639;border-radius:30px}ul.stops>li a .lines .subway{background-color:#2045b0}ul.stops>li a .lines .bus{background-color:#7f3d7f;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{border-bottom:1px solid grey;background-color:#101010}ul.departures>li.cancelled{background-color:#512f00}ul.departures>li a{color:#fff;text-decoration:none;display:block;height:3em;width:100%;position:relative}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#444;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #7c1a1a}ul.departures .tram{background-color:#852121}ul.departures .suburban{background-color:#2f6639;border-radius:30px}ul.departures .subway{background-color:#2045b0}ul.departures .bus{background-color:#7f3d7f;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:#fff}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:#fff}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:#fff}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#bbb;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v5/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}} +body{margin:0;color:#fff;background-color:#101010}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.textcontent{margin-left:1ex;margin-right:1ex}a{color:#99f;text-decoration:none}input,select,button{display:block;width:100%;max-width:100%;min-height:1.8em;border-radius:4px;font-size:90%;color:#fff;background-color:#101010;border:1px solid #444;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);margin-left:auto;margin-right:auto;text-align:center;vertical-align:middle;padding-top:1ex;padding-bottom:1ex;margin-top:1ex;margin-bottom:1ex}button{transition:background-color .3s;color:#fff;background-color:#337ab7;border-color:#2e6da4;cursor:pointer;box-shadow:none}button:active,button:focus,button:hover{color:#fff;background-color:#286090;border-color:#204d74}input[type="text"]{padding-left:0.5em;padding-right:0.5em;text-align:left;box-sizing:border-box}.smallbutton{display:inline-block;vertical-align:baseline;border-radius:4px;border:1px solid #2e6da4;transition:background-color .3s;color:#fff;background-color:#337ab7;cursor:pointer;box-shadow:none;padding:0.9ex;margin-right:1em}.smallbutton:active,.smallbutton:focus,.smallbutton:hover{color:#fff;background-color:#286090;border-color:#204d74}.smallbutton .material-icons{display:block;float:left;margin-right:0.5ex}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops>li{border-bottom:1px solid grey;background-color:#101010}ul.stops>li a{color:#fff;text-decoration:none;display:block;height:4em;width:100%;position:relative}ul.stops>li a .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%}ul.stops>li a .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li a .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li a .lines .longdistance{border:0.1em solid #7c1a1a}ul.stops>li a .lines .regional{background-color:#444}ul.stops>li a .lines .tram{background-color:#852121}ul.stops>li a .lines .taxi{background-color:#7f3d7f;font-weight:normal}ul.stops>li a .lines .suburban{background-color:#2f6639;border-radius:30px}ul.stops>li a .lines .subway{background-color:#2045b0}ul.stops>li a .lines .bus{background-color:#7f3d7f;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{border-bottom:1px solid grey;background-color:#101010}ul.departures>li.cancelled{background-color:#512f00}ul.departures>li a{color:#fff;text-decoration:none;display:block;height:3em;width:100%;position:relative}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#444;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #7c1a1a}ul.departures .tram{background-color:#852121}ul.departures .suburban{background-color:#2f6639;border-radius:30px}ul.departures .subway{background-color:#2045b0}ul.departures .bus{background-color:#7f3d7f;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:#fff}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:#fff}ul.departures .time .delay{padding-right:1ex;color:#f77}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:#fff}.tripinfo{padding-left:1em;padding-right:1em}.tripinfo .direction{text-align:center;font-size:120%;padding-top:0.5em;padding-bottom:0.5em;border-bottom:0.1em dashed #cccccc}.tripinfo .dataline{margin-top:0.6em;font-size:120%;width:100%;display:flex;justify-content:space-between;margin-bottom:1em}.tripinfo .dataline>div{width:33%}.tripinfo .dataline .arrival{display:inline-block;text-align:right}.tripinfo .dataline .platform{text-align:center}.tripinfo .dataline .departure{text-align:right}.tripinfo .dataline .delay{color:#f77}.tripinfo .linklist{margin-top:0.5em;margin-bottom:1em}.tripinfo .linklist .material-icons{font-size:20px}.tripinfo .route{margin-top:1em}.tripinfo .route .past-stop{list-style-type:disc}.tripinfo .route .future-stop{list-style-type:circle}.tripinfo .route .this-stop{font-weight:bold}.tripinfo .route .cancelled-stop{color:#f77}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#bbb;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v6/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}} diff --git a/static/css/light.min.css b/static/css/light.min.css index d9a855e..827dfc3 100644 --- a/static/css/light.min.css +++ b/static/css/light.min.css @@ -1 +1 @@ -body{margin:0;color:#000;background-color:#fff}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.textcontent{margin-left:1ex;margin-right:1ex}a{color:#009;text-decoration:none}input,select,button{display:block;width:100%;max-width:100%;min-height:1.8em;border-radius:4px;font-size:90%;color:#000;background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);margin-left:auto;margin-right:auto;text-align:center;vertical-align:middle;padding-top:1ex;padding-bottom:1ex;margin-top:1ex;margin-bottom:1ex}button{transition:background-color .3s;color:#fff;background-color:#337ab7;border-color:#2e6da4;cursor:pointer;box-shadow:none}button:active,button:focus,button:hover{color:#fff;background-color:#286090;border-color:#204d74}input[type="text"]{padding-left:0.5em;padding-right:0.5em;text-align:left;box-sizing:border-box}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops>li{border-bottom:1px solid grey;background-color:#fff}ul.stops>li a{color:#000;text-decoration:none;display:block;height:4em;width:100%;position:relative}ul.stops>li a .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%}ul.stops>li a .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li a .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li a .lines .longdistance{border:0.1em solid #fcc}ul.stops>li a .lines .regional{background-color:#eee}ul.stops>li a .lines .tram{background-color:#fcc}ul.stops>li a .lines .taxi{background-color:#eae;font-weight:normal}ul.stops>li a .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li a .lines .subway{background-color:#aac0ff}ul.stops>li a .lines .bus{background-color:#eae;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{border-bottom:1px solid grey;background-color:#fff}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures>li a{color:#000;text-decoration:none;display:block;height:3em;width:100%;position:relative}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #fcc}ul.departures .tram{background-color:#fcc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eae;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:#000}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:#000}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:#000}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#666;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v5/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}} +body{margin:0;color:#000;background-color:#fff}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.textcontent{margin-left:1ex;margin-right:1ex}a{color:#009;text-decoration:none}input,select,button{display:block;width:100%;max-width:100%;min-height:1.8em;border-radius:4px;font-size:90%;color:#000;background-color:#fff;border:1px solid #ccc;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);margin-left:auto;margin-right:auto;text-align:center;vertical-align:middle;padding-top:1ex;padding-bottom:1ex;margin-top:1ex;margin-bottom:1ex}button{transition:background-color .3s;color:#fff;background-color:#337ab7;border-color:#2e6da4;cursor:pointer;box-shadow:none}button:active,button:focus,button:hover{color:#fff;background-color:#286090;border-color:#204d74}input[type="text"]{padding-left:0.5em;padding-right:0.5em;text-align:left;box-sizing:border-box}.smallbutton{display:inline-block;vertical-align:baseline;border-radius:4px;border:1px solid #2e6da4;transition:background-color .3s;color:#fff;background-color:#337ab7;cursor:pointer;box-shadow:none;padding:0.9ex;margin-right:1em}.smallbutton:active,.smallbutton:focus,.smallbutton:hover{color:#fff;background-color:#286090;border-color:#204d74}.smallbutton .material-icons{display:block;float:left;margin-right:0.5ex}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops>li{border-bottom:1px solid grey;background-color:#fff}ul.stops>li a{color:#000;text-decoration:none;display:block;height:4em;width:100%;position:relative}ul.stops>li a .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%}ul.stops>li a .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li a .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li a .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li a .lines .longdistance{border:0.1em solid #fcc}ul.stops>li a .lines .regional{background-color:#eee}ul.stops>li a .lines .tram{background-color:#fcc}ul.stops>li a .lines .taxi{background-color:#eae;font-weight:normal}ul.stops>li a .lines .suburban{background-color:#9fd79f;border-radius:30px}ul.stops>li a .lines .subway{background-color:#aac0ff}ul.stops>li a .lines .bus{background-color:#eae;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{border-bottom:1px solid grey;background-color:#fff}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures>li a{color:#000;text-decoration:none;display:block;height:3em;width:100%;position:relative}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #fcc}ul.departures .tram{background-color:#fcc}ul.departures .suburban{background-color:#9fd79f;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eae;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:#000}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:#000}ul.departures .time .delay{padding-right:1ex;color:red}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:#000}.tripinfo{padding-left:1em;padding-right:1em}.tripinfo .direction{text-align:center;font-size:120%;padding-top:0.5em;padding-bottom:0.5em;border-bottom:0.1em dashed #cccccc}.tripinfo .dataline{margin-top:0.6em;font-size:120%;width:100%;display:flex;justify-content:space-between;margin-bottom:1em}.tripinfo .dataline>div{width:33%}.tripinfo .dataline .arrival{display:inline-block;text-align:right}.tripinfo .dataline .platform{text-align:center}.tripinfo .dataline .departure{text-align:right}.tripinfo .dataline .delay{color:red}.tripinfo .linklist{margin-top:0.5em;margin-bottom:1em}.tripinfo .linklist .material-icons{font-size:20px}.tripinfo .route{margin-top:1em}.tripinfo .route .past-stop{list-style-type:disc}.tripinfo .route .future-stop{list-style-type:circle}.tripinfo .route .this-stop{font-weight:bold}.tripinfo .route .cancelled-stop{color:#c00}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#666;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v6/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}} diff --git a/static/v4 b/static/v4 deleted file mode 120000 index 945c9b4..0000000 --- a/static/v4 +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/static/v6 b/static/v6 new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/static/v6 @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/templates/departure_list.html b/templates/departure_list.html index 801e6d2..3df1e65 100644 --- a/templates/departure_list.html +++ b/templates/departure_list.html @@ -1,6 +1,6 @@ {% for departure in departures %}
  • - + {{ departure.line.name }} {% if departure.suffix %} {{ departure.suffix }} diff --git a/templates/departures_page.html b/templates/departures_page.html index 605acff..eeceb76 100644 --- a/templates/departures_page.html +++ b/templates/departures_page.html @@ -3,7 +3,7 @@ {{ title }} {% include 'header.html' %} - + diff --git a/templates/header.html b/templates/header.html index 9f57312..14953fe 100644 --- a/templates/header.html +++ b/templates/header.html @@ -12,11 +12,11 @@ --> - + + {% include 'navbar.html' %} diff --git a/templates/tripinfo.html b/templates/tripinfo.html new file mode 100644 index 0000000..bfe46d0 --- /dev/null +++ b/templates/tripinfo.html @@ -0,0 +1,72 @@ +
    + +
    +{% if tripinfo.arrival %} +An: +{% if tripinfo.arrivalDelay %} +{{ tripinfo.arrival.strftime('%H:%M') }} +
    Plan: {{ tripinfo.plannedArrival.strftime('%H:%M') }} +{% else %} +{{ tripinfo.arrival.strftime('%H:%M') }} +{% endif %} +{% endif %} +
    + +
    +{% if tripinfo.platform %} +Steig {{ tripinfo.platform }} +{% else %} +  +{% endif %} +
    + +
    +{% if tripinfo.arrival %} +Ab: +{% if tripinfo.departureDelay %} +{{ tripinfo.departure.strftime('%H:%M') }} +
    Plan: {{ tripinfo.plannedDeparture.strftime('%H:%M') }} +{% else %} +{{ tripinfo.departure.strftime('%H:%M') }} +{% endif %} +{% endif %} +
    + +
    + +