Panels,some new stations,afix for missing cancellation data
This commit is contained in:
parent
9fbda5665f
commit
0d99419797
209
fonts/fujicons.css
Normal file
209
fonts/fujicons.css
Normal file
@ -0,0 +1,209 @@
|
||||
@font-face {
|
||||
font-family: 'Fujicons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(fujicons.ttf) format('truetype');
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
|
||||
.fa {
|
||||
display: inline-block;
|
||||
font: normal normal normal 14px/1 Fujicons;
|
||||
font-size: inherit;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
/* makes the font 33% larger relative to the icon container */
|
||||
.fa-lg {
|
||||
font-size: 1.33333333em;
|
||||
line-height: 0.75em;
|
||||
vertical-align: -15%;
|
||||
}
|
||||
.fa-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
.fa-3x {
|
||||
font-size: 3em;
|
||||
}
|
||||
.fa-4x {
|
||||
font-size: 4em;
|
||||
}
|
||||
.fa-5x {
|
||||
font-size: 5em;
|
||||
}
|
||||
.fa-fw {
|
||||
width: 1.28571429em;
|
||||
text-align: center;
|
||||
}
|
||||
.fa-ul {
|
||||
padding-left: 0;
|
||||
margin-left: 2.14285714em;
|
||||
list-style-type: none;
|
||||
}
|
||||
.fa-ul > li {
|
||||
position: relative;
|
||||
}
|
||||
.fa-li {
|
||||
position: absolute;
|
||||
left: -2.14285714em;
|
||||
width: 2.14285714em;
|
||||
top: 0.14285714em;
|
||||
text-align: center;
|
||||
}
|
||||
.fa-li.fa-lg {
|
||||
left: -1.85714286em;
|
||||
}
|
||||
.fa-border {
|
||||
padding: .2em .25em .15em;
|
||||
border: solid 0.08em #eeeeee;
|
||||
border-radius: .1em;
|
||||
}
|
||||
.fa-pull-left {
|
||||
float: left;
|
||||
}
|
||||
.fa-pull-right {
|
||||
float: right;
|
||||
}
|
||||
.fa.fa-pull-left {
|
||||
margin-right: .3em;
|
||||
}
|
||||
.fa.fa-pull-right {
|
||||
margin-left: .3em;
|
||||
}
|
||||
/* Deprecated as of 4.4.0 */
|
||||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
.fa.pull-left {
|
||||
margin-right: .3em;
|
||||
}
|
||||
.fa.pull-right {
|
||||
margin-left: .3em;
|
||||
}
|
||||
.fa-spin {
|
||||
-webkit-animation: fa-spin 2s infinite linear;
|
||||
animation: fa-spin 2s infinite linear;
|
||||
}
|
||||
.fa-pulse {
|
||||
-webkit-animation: fa-spin 1s infinite steps(8);
|
||||
animation: fa-spin 1s infinite steps(8);
|
||||
}
|
||||
@-webkit-keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.fa-rotate-180 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
|
||||
-webkit-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.fa-rotate-270 {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.fa-flip-horizontal {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
.fa-flip-vertical {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(1, -1);
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
:root .fa-rotate-90,
|
||||
:root .fa-rotate-180,
|
||||
:root .fa-rotate-270,
|
||||
:root .fa-flip-horizontal,
|
||||
:root .fa-flip-vertical {
|
||||
filter: none;
|
||||
}
|
||||
.fa-stack {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.fa-stack-1x {
|
||||
line-height: inherit;
|
||||
}
|
||||
.fa-stack-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
.fa-inverse {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
.fa-back:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.fa-forward:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.fa-globe:before {
|
||||
content: "\EA12"
|
||||
}
|
||||
|
||||
.fa-up:before {
|
||||
content: "\E925"
|
||||
}
|
||||
|
||||
.fa-down:before {
|
||||
content: "\E922"
|
||||
}
|
||||
|
||||
.fa-work:before {
|
||||
content: "\E998"
|
||||
}
|
||||
|
||||
.fa-home:before {
|
||||
content: "\EA1E"
|
||||
}
|
||||
|
||||
.fa-refresh:before {
|
||||
content: "\EA88"
|
||||
}
|
BIN
fonts/fujicons.ttf
Executable file
BIN
fonts/fujicons.ttf
Executable file
Binary file not shown.
@ -25,7 +25,7 @@ gulp.task('bundleBackbone', function () {
|
||||
|
||||
.pipe(sourcemaps.init({ 'loadMaps': true }))
|
||||
// Add transformation tasks to the pipeline here.
|
||||
.pipe(uglify())
|
||||
// .pipe(uglify())
|
||||
.on('error', gutil.log)
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('./live/js'));
|
||||
|
@ -69,3 +69,9 @@ gulp.task('gotham', function() {
|
||||
gulp.src(['fonts/gotham.css']).pipe(gulp.dest('live/fonts'));
|
||||
gulp.src(['fonts/GothamSSm-Black.otf', 'fonts/GothamSSm-Bold.otf', 'fonts/GothamSSm-Book.otf', 'fonts/GothamSSm-Light.otf', 'fonts/GothamSSm-Medium.otf']).pipe(gulp.dest('live/fonts'));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('fujicons', function() {
|
||||
gulp.src(['fonts/fujicons.css']).pipe(gulp.dest('live/fonts'));
|
||||
gulp.src(['fonts/fujicons.ttf']).pipe(gulp.dest('live/fonts'));
|
||||
});
|
||||
|
@ -4,4 +4,4 @@ const requireDir = require('require-dir');
|
||||
|
||||
requireDir('./gulp');
|
||||
|
||||
gulp.task('default', ['bundleBackbone', 'styles', 'copy', 'customMUI', 'vendor', 'fonts', 'gotham']);
|
||||
gulp.task('default', ['bundleBackbone', 'styles', 'copy', 'customMUI', 'vendor', 'fonts', 'gotham', 'fujicons']);
|
||||
|
5246
package-lock.json
generated
5246
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
172
server/lib/logging.js
Normal file
172
server/lib/logging.js
Normal file
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* @fileOverview Utilities to simplify logging for the rest of the code.
|
||||
*/
|
||||
const winston = require('winston');
|
||||
const _ = require('lodash');
|
||||
const debug = require('debug')('logging:compliance');
|
||||
|
||||
/**
|
||||
* Requiring `winston-mongodb` will expose
|
||||
* `winston.transports.MongoDB`
|
||||
*/
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
require('winston-mongodb');
|
||||
|
||||
module.exports = logging;
|
||||
|
||||
const MONGO_LOG_COLLECTION = 'ComplianceLog';
|
||||
|
||||
/**
|
||||
* Initialise log transports
|
||||
*/
|
||||
const transports = [
|
||||
new winston.transports.Console({
|
||||
'name': 'console.info',
|
||||
'format': winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.simple()
|
||||
),
|
||||
'colorize': true,
|
||||
'silent': false
|
||||
})
|
||||
];
|
||||
let mongoTransport;
|
||||
|
||||
/**
|
||||
* Create a very basic logger
|
||||
*/
|
||||
const logger = winston.createLogger({
|
||||
'level': 'info',
|
||||
transports
|
||||
});
|
||||
|
||||
/**
|
||||
* Trivially handle the 'error' event to prevent unhandled exception errors.
|
||||
*/
|
||||
logger.on('error', (error) => {
|
||||
debug('Logger error:', error);
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialisation functions
|
||||
*/
|
||||
module.exports.init = {
|
||||
initMongoTransport
|
||||
};
|
||||
|
||||
/**
|
||||
* For test, also export a way to control the logger
|
||||
*/
|
||||
module.exports._test = {
|
||||
'getLogger': () => logger,
|
||||
'getTransports': () => transports
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {function} BridgeLogFunction
|
||||
* @param {Object} req - Express request object (to access IP, request ID, etc.)
|
||||
* @param {string} [req.ip] - The IP address of the caller
|
||||
* @param {string} [req.bridgeUniqueId] - The unique id of this request
|
||||
* @param {string} [req.sessionData.User] - UserID for the user the request is for
|
||||
* @param {string} msg - The basic message to log
|
||||
* @param {Object} [opt] - Optional object containing additional values to log. These will be
|
||||
* automatically prefixed with '_' and added to the logged object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BridgeLog
|
||||
* @property {BridgeLogFunction} info - log at info level
|
||||
* @property {BridgeLogFunction} error - log at error level
|
||||
*/
|
||||
|
||||
/**
|
||||
* Factory function for initialising logging for the specified calling file.
|
||||
*
|
||||
* It returns an object containing log functions such as `log.info()`, `log.error()`.
|
||||
* The format of this functions is defined as @see BridgeLogFunction.
|
||||
*
|
||||
* @example
|
||||
* const log = require('<path>/utils/logging.js')(__filename, 'utils:text:example');
|
||||
* log.info(req, 'Some info text I want to log');
|
||||
* log.error(req, 'Some error text I want to log', {customValue: 'Some custom value for this log'});
|
||||
*
|
||||
* @param {string} file - The filename. Usually just __filename
|
||||
* @param {string} logID - A colon seperated id for this log group (e.g as used by debug())
|
||||
* @returns {BridgeLog} - The logging object
|
||||
*/
|
||||
function logging(file, logID) {
|
||||
return {
|
||||
'info': doLog.bind(undefined, file, logID, 'info'),
|
||||
'error': doLog.bind(undefined, file, logID, 'error')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to actually do the logging for any of the specific functions specified above.
|
||||
*
|
||||
* @param {string} file - the full filepath of the file that initialised this logger
|
||||
* @param {string} logId - id of the log group
|
||||
* @param {string} level - log level
|
||||
* @param {Object} req - Express request object (to access IP, requestID etc.)
|
||||
* @param {string} msg - The simple string to log
|
||||
* @param {Object} opt - Additional options to log
|
||||
*/
|
||||
function doLog(file, logId, level, req, msg, opt) {
|
||||
const toLog = {
|
||||
level,
|
||||
'message': msg,
|
||||
'meta': {
|
||||
logId,
|
||||
'ip': req.ip,
|
||||
'reqId': req.bridgeUniqueId,
|
||||
'userId': _.get(req, 'session.data.user'),
|
||||
file
|
||||
}
|
||||
};
|
||||
const loggableOpt = _.mapKeys(opt, (value, key) => `_${ key}`);
|
||||
|
||||
//
|
||||
// Update the base object with the loggable options (prefix with _ per GELF);
|
||||
//
|
||||
_.assign(toLog.meta, loggableOpt);
|
||||
|
||||
//
|
||||
// Call the real logger and return the result
|
||||
//
|
||||
return logger.log(toLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* The MongoDB `Db` class from the NodeJS driver
|
||||
* @typedef {Object} Db
|
||||
*/
|
||||
|
||||
/**
|
||||
* Updates the logger to include a MongoDB transport that talks to the provided
|
||||
* `db` instance.
|
||||
* This also removes any prior MongoDB transport.
|
||||
*
|
||||
* @param {Db} db - the MongoDB `Db` instance to use for calling the DB.
|
||||
*/
|
||||
function initMongoTransport(db) {
|
||||
// Remove any existing mongodb transport
|
||||
if (mongoTransport)
|
||||
logger.remove(mongoTransport);
|
||||
|
||||
//
|
||||
// Create a new mongodb transport and register it with the logger
|
||||
//
|
||||
mongoTransport = new winston.transports.MongoDB({
|
||||
'name': 'mongotransport',
|
||||
'decolorize': true,
|
||||
'storeHost': true,
|
||||
db,
|
||||
'collection': MONGO_LOG_COLLECTION,
|
||||
'format': winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.printf((info) => info.message)
|
||||
)
|
||||
});
|
||||
|
||||
logger.add(mongoTransport);
|
||||
}
|
@ -19,6 +19,7 @@ function getTrainTimes(req, res) {
|
||||
|
||||
Query(function (a, b) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
res.end(JSON.stringify(a));
|
||||
}, res, 'huxley.apphb.com', url);
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ $mui-base-font-family: 'Roboto', "Helvetica Neue", Helvetica, Arial, Verdana,"Tr
|
||||
|
||||
// import MUI SASS
|
||||
@import "./node_modules/muicss/lib/sass/mui";
|
||||
|
||||
@import "./src/css/viewport";
|
||||
@import "./src/css/horscroll";
|
||||
@import "./src/css/spinner";
|
||||
////
|
||||
|
||||
body {
|
||||
@ -171,3 +173,115 @@ However, delay the fade out process for 2.5 seconds */
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.stop-scrolling {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
position: absolute;
|
||||
z-index: 5000;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: mui-color('grey','300');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
}
|
||||
|
||||
.fillpanel {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color: mui-color('amber', '50');
|
||||
}
|
||||
|
||||
.fullscreen .header {
|
||||
/*position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: +1;
|
||||
transition: left 0.2s;*/
|
||||
|
||||
position:sticky;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height:100%;
|
||||
// overflow:auto;
|
||||
}
|
||||
|
||||
.box .headerSpacer {
|
||||
flex: 0 1 66px;
|
||||
}
|
||||
|
||||
.box .content {
|
||||
flex: 1 1 auto;
|
||||
background-color: mui-color('white');
|
||||
overflow: auto;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.newsarticle img {
|
||||
max-height:100%;
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
.tiny {
|
||||
font-size:1rem;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size:2rem;
|
||||
}
|
||||
|
||||
.medium {
|
||||
font-size:4rem;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size:6rem;
|
||||
}
|
||||
|
||||
.cardLink {
|
||||
color: mui-color('blue', '500');
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.endbumper {
|
||||
height:66px;
|
||||
}
|
||||
|
||||
.seemore {
|
||||
font-size:14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#connectionStatus {
|
||||
margin-top:15px;
|
||||
margin-bottom:15px;
|
||||
}
|
||||
|
||||
.trafficHeavy {
|
||||
color: #fa4a50;
|
||||
}
|
||||
|
||||
.trafficLight {
|
||||
color: #fdbd15;
|
||||
}
|
||||
|
||||
.trafficMedium {
|
||||
color: #fba010;
|
||||
}
|
||||
|
||||
#map { height: 180px; }
|
||||
|
||||
#bymeImages {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
52
src/css/horscroll.scss
Normal file
52
src/css/horscroll.scss
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
.scrolling-wrapper-flexbox {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
|
||||
.scrollCard, .scrollCardHalf, .imageCard, .hourlyCard {
|
||||
flex: 0 0 auto;
|
||||
margin-right: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.scrollCard, .scrollCardHalf, .hourlyCard {
|
||||
width: 250px;
|
||||
height: 175px;
|
||||
overflow-y: hidden;
|
||||
border-radius: 3px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 5px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.05), 0 2px 1px -2px rgba(0,0,0,0.05), 0 1px 5px 0 rgba(0,0,0,0.05)
|
||||
}
|
||||
|
||||
.scrollCardHalf {
|
||||
height: 85px;
|
||||
}
|
||||
|
||||
.hourlyCard {
|
||||
width: 42px;
|
||||
height:70px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.imageCard {
|
||||
// width: 250px;
|
||||
height: 175px;
|
||||
overflow-y: hidden;
|
||||
|
||||
}
|
||||
.imageCard img {
|
||||
max-height:100%;
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
.scrolling-wrapper, .scrolling-wrapper-flexbox {
|
||||
height: 75px;
|
||||
width: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
93
src/css/spinner.scss
Normal file
93
src/css/spinner.scss
Normal file
@ -0,0 +1,93 @@
|
||||
$green: #008744;
|
||||
$blue: #0fa3ef;
|
||||
$red: #dc4f43;
|
||||
$yellow: #ffbe39;
|
||||
$white: #eee;
|
||||
$black: #301010;
|
||||
|
||||
// scaling... any units
|
||||
$width: 100px;
|
||||
|
||||
body {
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
// demo-specific
|
||||
.showbox {
|
||||
position: absolute;
|
||||
top: 40vh;
|
||||
bottom: 60vh;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 5%;
|
||||
}
|
||||
// end demo-specific
|
||||
|
||||
.loader {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: $width;
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-top: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.circular {
|
||||
animation: rotate 2s linear infinite;
|
||||
height: 100%;
|
||||
transform-origin: center center;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.path {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -35px;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -124px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes color {
|
||||
100%,
|
||||
0% {
|
||||
stroke: $red;
|
||||
}
|
||||
40% {
|
||||
stroke: $yellow;
|
||||
}
|
||||
66% {
|
||||
stroke: $blue;
|
||||
}
|
||||
80%,
|
||||
90% {
|
||||
stroke: $black;
|
||||
}
|
||||
}
|
81
src/css/viewport.scss
Normal file
81
src/css/viewport.scss
Normal file
@ -0,0 +1,81 @@
|
||||
.viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* encapsulate the various syntax in helper clases */
|
||||
/* inspired by http://infrequently.org/2009/08/css-3-progress/ */
|
||||
|
||||
/* items flex/expand vertically */
|
||||
.vbox {
|
||||
/* previous syntax */
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: box;
|
||||
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-ms-box-orient: vertical;
|
||||
box-orient: vertical;
|
||||
|
||||
/* current syntax */
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flex;
|
||||
display: flex;
|
||||
|
||||
-webkit-flex-direction: column;
|
||||
-moz-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ebf1f6+0,abd3ee+50,89c3eb+51,d5ebfb+100;Blue+Gloss+%234 */
|
||||
background: #ebf1f6; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #ebf1f6 0%, #abd3ee 50%, #89c3eb 51%, #d5ebfb 100%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, #ebf1f6 0%, #abd3ee 50%, #89c3eb 51%, #d5ebfb 100%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, #ebf1f6 0%, #abd3ee 50%, #89c3eb 51%, #d5ebfb 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ebf1f6', endColorstr='#d5ebfb', GradientType=0); /* IE6-9 */
|
||||
}
|
||||
|
||||
.backgroundImage {
|
||||
background-image: url(http://via.placeholder.com/411x823);
|
||||
}
|
||||
|
||||
.appPanel {
|
||||
/* previous syntax */
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-ms-box-flex: 1;
|
||||
box-flex: 1;
|
||||
|
||||
/* current syntax */
|
||||
-webkit-flex: 1;
|
||||
-moz-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
|
||||
overflow-y: auto;
|
||||
/* background-color: white;*/
|
||||
}
|
||||
|
||||
[data-id~="main"] {
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
}
|
||||
|
||||
[data-id~="routeP"] {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
<title>Train Times</title>
|
||||
<link href="fonts/fonts.css" rel="stylesheet">
|
||||
<link href="fonts/gotham.css" rel="stylesheet">
|
||||
<link href="fonts/fujicons.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="css/mui.custom.css" rel="stylesheet" type="text/css"/>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
|
||||
@ -22,21 +23,26 @@
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header id="header">
|
||||
<div class="mui-appbar mui--appbar-line-height mui--z2">
|
||||
<div class='mui-col-xs-8 mui-col-md-8 mui--appbar-height titleBar'>Train Times</div>
|
||||
<div class='mui-col-xs-4 mui-col-md-4 mui--appbar-height'></div>
|
||||
<div class="appPanel" data-id="main">
|
||||
<header id="header">
|
||||
<div class="mui-appbar mui--appbar-line-height mui--z2">
|
||||
<div class='mui-col-xs-8 mui-col-md-8 mui--appbar-height titleBar'>Train Times</div>
|
||||
<div class='mui-col-xs-4 mui-col-md-4 mui--appbar-height'></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="mui--appbar-height"></div>
|
||||
<div class="mui-container">
|
||||
<div class="app"></div>
|
||||
<div id="trains" ></div>
|
||||
|
||||
<!--<div id='trainResults' class="mui--hide"></div>-->
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="mui--appbar-height"></div>
|
||||
<div class="mui-container">
|
||||
<div class="app"></div>
|
||||
<div id="trains" class="unsliced"></div>
|
||||
|
||||
<div id='trainResults' class="mui--hide"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="snackbar">⚡ Offline, waiting to reconnect...</div>
|
||||
|
||||
|
||||
|
@ -82,12 +82,12 @@ const { RouteModel, RouteView } = require('./route');
|
||||
}
|
||||
};
|
||||
|
||||
if ('serviceWorker' in navigator)
|
||||
/*if ('serviceWorker' in navigator)
|
||||
navigator.serviceWorker
|
||||
.register('./service-worker.js')
|
||||
.then(function() {
|
||||
console.log('Service Worker Registered');
|
||||
});
|
||||
});*/
|
||||
|
||||
app.createViews();
|
||||
})();
|
||||
|
102
src/js/libs/panel.js
Normal file
102
src/js/libs/panel.js
Normal file
@ -0,0 +1,102 @@
|
||||
const $ = require('jquery');
|
||||
let panelCount = 0;
|
||||
|
||||
function createPanel(params) {
|
||||
const { title, divId } = params;
|
||||
const newPanel = `
|
||||
<div class="appPanel" data-id="${divId}">
|
||||
<div id="card_${divId}" class="fullscreen" style="display:;">
|
||||
<header class="header">
|
||||
<div class="mui-appbar mui--appbar-line-height mui--z1" style="vertical-align:middle;">
|
||||
<span>
|
||||
<button class="mui-btn mui-btn--large mui-btn--primary mui-btn--flat closebutton" id="close_${divId}">
|
||||
<i class="fa-3x fa fa-back mui--align-middle" style="color:white;"></i>
|
||||
</button>
|
||||
|
||||
</span>
|
||||
<span class="mui--text-title mui--align-middle" id="title_${divId}">
|
||||
${title}
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="box">
|
||||
|
||||
<div class="content mui-panel" id="${divId}">
|
||||
<div class="showbox">
|
||||
<div class="loader">
|
||||
<svg class="circular" viewBox="25 25 50 50">
|
||||
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- end fullscreen-->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const $newPanel = $(newPanel);
|
||||
const $closeButton = $newPanel.find(`button#close_${divId}`);
|
||||
|
||||
$closeButton.on('click', () => {
|
||||
doClose();
|
||||
});
|
||||
$newPanel.offset({ 'top': $(window).scrollTop(), 'left': 0 });
|
||||
|
||||
function doClose() {
|
||||
const $body = $('body');
|
||||
$newPanel.hide().remove();
|
||||
console.log('panelCount', panelCount);
|
||||
panelCount--;
|
||||
if (panelCount === 0)
|
||||
console.log('Removing panel stuff');
|
||||
$body.removeClass('stop-scrolling').unbind('touchmove');
|
||||
}
|
||||
|
||||
return $newPanel;
|
||||
}
|
||||
|
||||
function addPanel($newPanel) {
|
||||
const $body = $('body');
|
||||
const $content = $newPanel.find('.content');
|
||||
$body.append($newPanel);
|
||||
if (panelCount === 0) {
|
||||
$body.addClass('stop-scrolling');
|
||||
$body.bind('touchmove', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
panelCount++;
|
||||
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
module.exports = { createPanel, addPanel };
|
||||
|
||||
/*
|
||||
class Template {
|
||||
constructor(item) {
|
||||
// "pubdate": "Tue, 06 Feb 2018 17:05:00 +0100",
|
||||
const pubdateSrc = fecha.parse(item.pubdate, 'ddd, DD MMM YYYY HH:mm:SS ZZ');
|
||||
const pubdate = fecha.format(pubdateSrc, 'dddd MMMM Do, YYYY');
|
||||
const description = item.description.replace(/(<script(\s|\S)*?<\/script>)|(<style(\s|\S)*?<\/style>)|(<!--(\s|\S)*?-->)|(<\/?(\s|\S)*?>)/g, '');
|
||||
this.data = `<article>
|
||||
<header>
|
||||
<a href="${item.guid.text}">${item.title}</a>
|
||||
<time class="published">${pubdate}</time>
|
||||
</header>
|
||||
<p class="description">${description}</p>
|
||||
</article>`;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
@ -1,6 +1,7 @@
|
||||
const $ = require('jquery');
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
const { createPanel, addPanel } = require('./libs/panel');
|
||||
|
||||
const RouteModel = Backbone.Model.extend({
|
||||
'initialize': function () {
|
||||
@ -138,7 +139,7 @@ const RouteView = Backbone.View.extend({
|
||||
console.log(this);
|
||||
this.eventBus.on('showRoute', function(d) {
|
||||
console.log('Showroute', d);
|
||||
|
||||
this.showPanel();
|
||||
this.model.set('to', d.to);
|
||||
this.model.set('from', d.from);
|
||||
this.model.update();
|
||||
@ -146,6 +147,16 @@ const RouteView = Backbone.View.extend({
|
||||
|
||||
this.initView();
|
||||
},
|
||||
'showPanel': function(guid) {
|
||||
console.log('Showing panel', guid);
|
||||
|
||||
this.$newPanel = createPanel({ 'title':'Details', 'divId':'routeP' });
|
||||
|
||||
this.$el = addPanel(this.$newPanel);
|
||||
|
||||
// this.$el.empty();
|
||||
this.$newPanel.show();
|
||||
},
|
||||
'events': {
|
||||
'click a': 'news',
|
||||
'click .station': 'stations'
|
||||
@ -191,7 +202,9 @@ const RouteView = Backbone.View.extend({
|
||||
const statusMode = (status.toLowerCase() === 'on time') ? 'ontime' : 'delayed';
|
||||
const delayReason = (item.delayReason !== null) ? `<tr><td colspan="4" class="mui--bg-danger mui--text-white" style="font-size:75%;">${item.delayReason}</td></tr>` : '';
|
||||
|
||||
services.push({ 'location':dest.locationName, 'time':time, 'status':status, 'platform':platform, 'cancel':item.cancelReason, 'type':'train' });
|
||||
const cancelReason = (item.cancelReason !== null) ? item.cancelReason : 'No reason given 🤷';
|
||||
|
||||
services.push({ 'location':dest.locationName, 'time':time, 'status':status, 'platform':platform, 'cancel':cancelReason, 'type':'train' });
|
||||
if (!item.isCancelled)
|
||||
ws = `${ws }<tr><td data-id="${item.serviceIdUrlSafe}" class="station">${dest.locationName} ${via}</td>
|
||||
<td class="mui--text-center time">${time}</td>
|
||||
@ -200,7 +213,7 @@ const RouteView = Backbone.View.extend({
|
||||
</tr>${delayReason}`;
|
||||
else
|
||||
ws = `${ws }<tr><td>${dest.locationName} ${via}</td><td>${time}</td>
|
||||
<td colspan="2" class="delayed">❌ ${item.cancelReason}</td></tr>`;
|
||||
<td colspan="2" class="delayed">❌ ${cancelReason}</td></tr>`;
|
||||
}
|
||||
|
||||
if (typeof route.busServices === 'object' && route.busServices !== null)
|
||||
@ -222,7 +235,7 @@ const RouteView = Backbone.View.extend({
|
||||
for (const item of route.nrccMessages) {
|
||||
const msg = item.value.replace('href=', 'data-url=').replace(' ">', '">');
|
||||
|
||||
nrMessages = `${nrMessages}<div class="mui--bg-danger mui--text-white" style="padding:2px;">${msg}</div>`;
|
||||
nrMessages = `${nrMessages}<div class="mui--bg-danger mui--text-white nrccAlert" style="padding:2px;">${msg}</div>`;
|
||||
}
|
||||
|
||||
const thead = `<div class="mui--text-center mui--text-accent">${route.locationName} TO ${route.filterLocationName}</div>
|
||||
@ -234,15 +247,20 @@ ${nrMessages}
|
||||
<th class="mui--text-center">Platform</th></tr></thead>`;
|
||||
|
||||
ws = `${thead}<tbody class="tableBody">${ws}</tbody></table>`;
|
||||
// ws = `${thead}${ws}</table>`;
|
||||
|
||||
this.$traintext.empty().html(ws);
|
||||
this.$traintext.removeClass('mui--hide').addClass('mui--show');
|
||||
this.$el.empty().html(ws);
|
||||
// this.$traintext.removeClass('mui--hide').addClass('mui--show');
|
||||
|
||||
/*
|
||||
if (this.$trains.hasClass('unsliced'))
|
||||
this.$trains.addClass('sliced').removeClass('unsliced');
|
||||
*/
|
||||
|
||||
// this.$trains.css("background-color", "yellow");
|
||||
// this.$trains.css("height", "300px");
|
||||
|
||||
this.$el.find('a').on('click', this.news);
|
||||
},
|
||||
'initView': function () {
|
||||
|
||||
|
@ -11,6 +11,13 @@ const Backbone = require('backbone');
|
||||
const { findStation } = require('./stations');
|
||||
|
||||
const TrainModel = Backbone.Model.extend({
|
||||
'defaults': function (obj) {
|
||||
// return a new object
|
||||
return {
|
||||
'update': 0,
|
||||
'last': new Date().getTime()
|
||||
};
|
||||
},
|
||||
'initialize': function () {
|
||||
const fromStation = this.get('from');
|
||||
const toStation = this.get('to');
|
||||
@ -45,6 +52,7 @@ const TrainModel = Backbone.Model.extend({
|
||||
setTimeout(trainUpdateFn.bind(this), mod + 10);
|
||||
},
|
||||
'getTrain': function () {
|
||||
this.set('update', new Date().getTime());
|
||||
const url = this.get('url');
|
||||
const self = this;
|
||||
const bus = this.get('bus');
|
||||
@ -61,6 +69,8 @@ const TrainModel = Backbone.Model.extend({
|
||||
},
|
||||
'success': function (data) {
|
||||
bus.trigger('online');
|
||||
|
||||
console.log('Data', data);
|
||||
self.set('trainData', data);
|
||||
},
|
||||
'error': function (xhr, type) {
|
||||
@ -76,8 +86,15 @@ const TrainView = Backbone.View.extend({
|
||||
'tagName': 'div',
|
||||
'initialize': function (options) {
|
||||
_.bindAll(this, 'render');
|
||||
|
||||
this.eventBus = options.eventBus;
|
||||
this.model.bind('change', this.render);
|
||||
this.model.bind('change:trainData', this.render);
|
||||
this.model.bind('change:update', function(){
|
||||
console.log('Updating....');
|
||||
});
|
||||
this.model.bind('all', function(eventName) {
|
||||
console.log(`${eventName } was triggered!`);
|
||||
});
|
||||
this.$trains = $('#trains');
|
||||
this.$traininfo = $('#traininfo');
|
||||
this.$traintext = $('#trainResults');
|
||||
@ -125,7 +142,7 @@ const TrainView = Backbone.View.extend({
|
||||
|
||||
this.$button = $(`#${target}`);
|
||||
|
||||
const output = 'OFF';
|
||||
const output = '<i class="fa fa-refresh mui--align-middle" ></i>';
|
||||
const status = (output === 'on time') ? 'ontime' : 'delayed';
|
||||
|
||||
this.$button.html(output);
|
||||
|
Loading…
Reference in New Issue
Block a user