updated stations

This commit is contained in:
Martin Donnelly 2017-12-18 22:17:42 +00:00
parent 271ebbc504
commit b77783a88c
14 changed files with 249 additions and 66 deletions

View File

@ -91,3 +91,54 @@ li {
margin: 6px 0;
vertical-align: middle;
}
/* The snackbar - position it at the bottom and in the middle of the screen */
#snackbar {
visibility: hidden; /* Hidden by default. Visible on click */
min-width: 250px; /* Set a default minimum width */
margin-left: -125px; /* Divide value of min-width by 2 */
background-color: #333; /* Black background color */
color: #fff; /* White text color */
text-align: center; /* Centered text */
border-radius: 2px; /* Rounded borders */
padding: 16px; /* Padding */
position: fixed; /* Sit on top of the screen */
z-index: 1; /* Add a z-index if needed */
left: 50%; /* Center the snackbar */
bottom: 30px; /* 30px from the bottom */
}
/* Show the snackbar when clicking on a button (class added with JavaScript) */
#snackbar.show {
visibility: visible; /* Show the snackbar */
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
However, delay the fade out process for 2.5 seconds */
/* -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;*/
-webkit-animation: fadein 0.5s;
animation: fadein 0.5s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@-webkit-keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
@keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}

BIN
src/img/Icon-144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
src/img/Icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/img/Icon-36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/img/Icon-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/img/Icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
src/img/Icon-72.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/img/Icon-96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -33,6 +33,8 @@
<div id='trainResults' class="mui--hide"></div>
</div>
<div id="snackbar">⚡ Offline, waiting to reconnect...</div>
<!--<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>-->
<script src="js/vendor.js" async></script>
<script src="js/bundle.js" async></script>

View File

@ -1,12 +1,13 @@
require('muicss');
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { TrainModel, TrainView } = require('./train');
const { RouteModel, RouteView } = require('./route');
const Minibus = require('minibus');
(function () {
const bus = Minibus.create();
let offline = false;
const $snackbar = $('#snackbar');
const app = {
'routes' : [
@ -20,15 +21,33 @@ const Minibus = require('minibus');
};
const routeView = new RouteView( { 'model': new RouteModel() });
app.eventBus = _.extend({}, Backbone.Events);
app.eventBus.on('offline', function() {
if (offline === false) {
offline = true;
// console.log('Set it once!');
$snackbar.addClass('show');
}
}, this);
app.eventBus.on('online', function() {
if (offline === true) {
offline = false;
// console.log('Set it once!');
$snackbar.removeClass('show');
}
}, this);
app.routeView = new RouteView( { 'model': new RouteModel(), 'eventBus': app.eventBus });
app.createViews = function() {
for (const route of this.routes) {
console.log(route);
var key = Symbol(route.from + route.to);
console.log(key);
this.views[key] = new TrainView({ 'model': new TrainModel(route), 'routeView': routeView });
// console.log(route);
const key = Symbol(route.from + route.to);
// console.log(key);
route.bus = this.eventBus;
this.views[key] = new TrainView({ 'model': new TrainModel(route), 'eventBus': app.eventBus });
}
};
@ -40,9 +59,4 @@ const Minibus = require('minibus');
});
app.createViews();
/* app.views.dbqglqView = new TrainView({ 'model': new TrainModel({ 'from': 'dbe', 'to': 'glq' }) });
app.views.glqdbeView = new TrainView({ 'model': new TrainModel({ 'from': 'glq', 'to': 'dbe' }) });
app.views.glqhymView = new TrainView({ 'model': new TrainModel({ 'from': 'glq', 'to': 'hym' }) });
app.views.hymglqView = new TrainView({ 'model': new TrainModel({ 'from': 'hym', 'to': 'glq' }) });*/
})();

View File

@ -6,19 +6,24 @@ const RouteModel = Backbone.Model.extend({
'initialize': function () {
const fromStation = this.get('from');
const toStation = this.get('to');
const routeUrl = `/gettrainsbob?from=${ this.get('from') }&to=${ this.get('to')}`;
const routeUrl = `/gettrains?from=${ this.get('from') }&to=${ this.get('to')}`;
const target = this.get('from') + this.get('to');
this.set('url', routeUrl);
this.set('routeUrl', routeUrl);
this.set('target', target);
this.set('visible', false);
this.set('trainData', { 'eta':'OFF', 'sta': 'OFF' });
this.set('interval', null);
this.update();
},
'update': function () {
console.log('Route model update');
if (!this.get('to'))
return;
const now = new Date;
const hours = now.getHours();
const limit = (hours < 6) ? 3600000 : 60000;
const limit = (hours < 6) ? 3600000 : 95000;
const mod = limit - (now.getTime() % limit);
@ -28,45 +33,44 @@ const RouteModel = Backbone.Model.extend({
this.set('trainData', { 'eta':'OFF', 'sta': 'OFF' });
const routeUpdateFn = function () {
console.log('routeUpdateFn');
this.update();
};
setTimeout(routeUpdateFn.bind(this), mod + 10);
if (!this.get('interval'))
this.set('interval', setInterval(routeUpdateFn.bind(this), mod + 10)) ;
},
'getRoute': function () {
const url = this.get('routeUrl');
const url = `/gettrains?from=${ this.get('from') }&to=${ this.get('to')}`;
const self = this;
if (this.get('visible') === true)
this.set('visible', false);
else
$.ajax({
'type': 'GET',
'url': url,
'data': '',
'dataType': 'json',
$.ajax({
'type': 'GET',
'url': url,
'data': '',
'dataType': 'json',
'timeout': 10000,
'headers': {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
'timeout': 10000,
'headers': {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
},
'success': function (data) {
// getTrainsCB(data);
// console.log('Got', data);
},
'success': function (data) {
// getTrainsCB(data);
console.log('Got', data);
self.set('route', data);
self.set('visible', true);
},
'error': function (xhr, type) {
console.error('ajax error');
console.log('readyState', xhr.readyState);
console.log('status', xhr.status);
console.error(type);
}
});
self.set('route', data);
self.set('visible', true);
},
'error': function (xhr, type) {
console.error('ajax error');
console.log('readyState', xhr.readyState);
console.log('status', xhr.status);
console.error(type);
}
});
},
'setRoute': function(from, to) {
this.set('from', from);
@ -76,21 +80,84 @@ const RouteModel = Backbone.Model.extend({
const RouteView = Backbone.View.extend({
'tagName': 'div',
'initialize': function () {
'initialize': function (options) {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.eventBus = options.eventBus;
// this.model.bind('change', this.render);
/* this.model.bind('change:route', function(e){
console.log('event:', e);
});*/
this.listenTo(this.model, 'change:route', _.debounce(_.bind(this.render), 128));
this.$trains = $('#trains');
this.$traininfo = $('#traininfo');
this.$traintext = $('#trainResults');
this.$el = this.$traintext;
this.eventBus.on('showRoute', function(d) {
console.log('Showroute', d);
this.model.set('to', d.to);
this.model.set('from', d.from);
this.model.update();
}, this);
this.initView();
},
'events': {
'click': 'showTrains'
},
'render': function () {
console.log('Routeview:Render');
const route = this.model.get('route');
console.log(route);
let ws = `<div class="mui--text-center mui--text-accent">${route.locationName} TO ${route.filterLocationName}</div>
<table class="mui-table mui-table-bordered">
<tr><th>Destination</th>
<th>Time</th>
<th>Status</th>
<th>Platform</th></tr>
`;
const services = [];
if (typeof route.trainServices === 'object' && route.trainServices !== null)
for (const item of route.trainServices) {
const dest = item.destination[0];
const via = dest.via !== null ? `<em class="mui--text-accent">${dest.via}</em>` : '';
const platform = item.platform !== null ? item.platform : '💠';
const time = item.sta !== null ? item.sta : `D ${item.std}`;
const status = item.eta !== null ? item.eta : item.etd;
const statusMode = (item.eta.toLowerCase() === 'on time') ? 'ontime' : 'delayed';
services.push({ 'location':dest.locationName, 'time':time, 'status':status, 'platform':platform, 'cancel':item.cancelReason, 'type':'train' });
if (!item.isCancelled)
ws = `${ws }<tr><td>${dest.locationName} ${via}</td>
<td>${time}</td>
<td class="${statusMode}">${status}</td>
<td>${platform}</td>
</tr>`;
else
ws = `${ws }<tr><td>${dest.locationName} ${via}</td><td>${time}</td>
<td colspan="2" class="delayed"> ${item.cancelReason}</td></tr>`;
}
if (typeof route.busServices === 'object' && route.busServices !== null)
for (const item of route.busServices) {
const dest = item.destination[0];
const via = dest.via !== null ? `<em>${dest.via}</em>` : '';
const platform = item.platform !== null ? item.platform : '';
const time = item.sta !== null ? item.sta : `D ${item.std}`;
const status = item.eta !== null ? item.eta : item.etd;
services.push({ 'location':dest.locationName, 'time':time, 'status':status, 'platform':platform, 'cancel':item.cancelReason, 'type':'bus' });
ws = `${ws }<tr><td>🚌 ${dest.locationName} ${via}</td><td>${time}</td><td>${status}</td><td>${platform}</td></tr>`;
}
ws = `${ws }</table>`;
this.$traintext.empty().html(ws);
this.$traintext.removeClass('mui--hide').addClass('mui--show');
},
'initView': function () {
},

View File

@ -44,6 +44,7 @@ const TrainModel = Backbone.Model.extend({
'getTrain': function () {
const url = this.get('url');
const self = this;
const bus = this.get('bus');
$.ajax({
'type': 'GET',
'url': url,
@ -56,13 +57,19 @@ const TrainModel = Backbone.Model.extend({
'Access-Control-Allow-Headers': 'Content-Type'
},
'success': function (data) {
bus.trigger('online');
self.set('trainData', data);
},
'error': function (xhr, type) {
/*
console.log('ajax error');
console.log('readyState', xhr.readyState);
console.log('status', xhr.status);
console.log(type);
*/
if (xhr.readyState === 0 && xhr.status === 0)
bus.trigger('offline');
}
});
},
@ -95,8 +102,8 @@ const TrainModel = Backbone.Model.extend({
},
'error': function (xhr, type) {
console.error('ajax error');
console.error(xhr);
console.error(type);
/*console.error(xhr);
console.error(type);*/
}
});
}
@ -104,15 +111,17 @@ const TrainModel = Backbone.Model.extend({
const TrainView = Backbone.View.extend({
'tagName': 'div',
'initialize': function () {
'initialize': function (options) {
_.bindAll(this, 'render');
this.eventBus = options.eventBus;
this.model.bind('change', this.render);
this.$trains = $('#trains');
this.$traininfo = $('#traininfo');
this.$traintext = $('#trainResults');
this.$el = this.$trains;
this.initView();
// console.log(this.get('routeView'));
// console.log(this.get('routeView'));
// console.log(this);
},
'events': {
'click': 'showTrains'
@ -128,8 +137,8 @@ const TrainView = Backbone.View.extend({
this.$button.html(output);
this.$button.removeClass('delayed').removeClass('ontime').addClass(status);
if (visible) {
let ws = `<div>${route.locationName} TO ${route.filterLocationName}</div>
/*if (visible) {
let ws = `<div class="mui--text-center mui--text-accent">${route.locationName} TO ${route.filterLocationName}</div>
<table class="mui-table mui-table-bordered">
<tr><th>Destination</th>
<th>Time</th>
@ -175,25 +184,27 @@ const TrainView = Backbone.View.extend({
this.$traintext.removeClass('mui--hide').addClass('mui--show');
}
else
this.$traintext.removeClass('mui--show').addClass('mui--hide');
this.$traintext.removeClass('mui--show').addClass('mui--hide');*/
},
'initView': function () {
const self = this;
const target = this.model.get('target');
const html = `
<div class="mui-row card">
<div class='mui-col-xs-8 mui-col-md-8 entry'>${target.toUpperCase()}</div>
<div class='mui-col-xs-4 mui-col-md-4'>
<div class='mui-col-xs-7 mui-col-md-7 entry'>${target.toUpperCase()}</div>
<div class='mui-col-xs-5 mui-col-md-5 mui--text-right'>
<button class="mui-btn mui-btn--flat" id="${target}"></button>
</div>
</div>
`;
this.$html = $(html);
this.$html.on('click', function () {
this.$html.on('click', () => {
// console.log('from', self.model.get('from'));
// console.log('to', self.model.get('to'));
self.eventBus.trigger('showRoute', { 'from': self.model.get('from'), 'to': self.model.get('to') });
// console.log(self)
self.model.getRoute();
// self.model.getRoute();
});
this.$trains.append(this.$html);
@ -209,7 +220,8 @@ const TrainView = Backbone.View.extend({
this.events[cevent] = 'showTrains';
},
'showTrains': function () {
console.log('Show train');
// console.log('Show train');
this.eventBus.trigger('showRoute');
}
});

View File

@ -3,9 +3,39 @@
"short_name": "Train Times",
"icons": [
{
"src": "/img/android-chrome-192x192.png",
"src": "/img/Icon-36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/img/Icon-48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/img/Icon-72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/img/Icon-96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/img/Icon-144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/img/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",

View File

@ -26,7 +26,14 @@ const filesToCache = [
'/fonts/Roboto_Condensed-normal-400.woff',
'/fonts/Roboto_Slab-normal-400.woff',
'/js/bundle.js',
'/js/vendor.js'
'/js/vendor.js',
'/img/Icon-36.png',
'/img/Icon-48.png',
'/img/Icon-72.png',
'/img/Icon-96.png',
'/img/Icon-144.png',
'/img/Icon-192.png',
'/img/Icon-512.png'
];
self.addEventListener('install', function(e) {