From 302b74983412534b2598271c12c4bff4b75d2a36 Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Thu, 19 Jan 2023 16:53:18 +0000 Subject: [PATCH] Frontend/Deployment work. Deployment now serves minified GZipped files. Frontend now has a responsive station title size. Need to: Change to an nginx container version that supports Brotli - I am after all the speed! Signed-off-by: Fred Boniface --- .test-tools/ferry-vc.json | 33 ++++++ static/.dockerignore | 3 +- static/Dockerfile | 16 +-- static/board.html | 19 +++- static/conf/deploy.sh | 37 ++++++ static/{nginx => conf}/nginx.conf | 0 static/js/boards.js | 153 ++----------------------- static/js/main.js | 10 ++ static/js/public-board.js | 180 ++++++++++++++++++++++++++++++ static/styles/boards.css | 46 ++++++-- 10 files changed, 329 insertions(+), 168 deletions(-) create mode 100644 .test-tools/ferry-vc.json create mode 100644 static/conf/deploy.sh rename static/{nginx => conf}/nginx.conf (100%) create mode 100644 static/js/public-board.js diff --git a/.test-tools/ferry-vc.json b/.test-tools/ferry-vc.json new file mode 100644 index 0000000..2397ad3 --- /dev/null +++ b/.test-tools/ferry-vc.json @@ -0,0 +1,33 @@ +{"service": +[ + {"sta":"16:07", + "eta":"On time", + "operator":"South Western Railway", + "operatorCode":"SW", + "serviceType":"ferry", + "serviceID":"37782PHBR____", + "origin": + {"location": + {"locationName": + "Ryde Pier Head","crs":"RYP" + } + }, + "destination": + {"location": + {"locationName":"Portsmouth Harbour", + "crs":"PMH" + } + }, + "previousCallingPoints": + {"callingPointList": + {"callingPoint": + {"locationName":"Ryde Pier Head", + "crs":"RYP", + "st":"15:45", + "et":"On time" + } + } + } +}, +{"std":"16:15","etd":"On time","operator":"South Western Railway","operatorCode":"SW","serviceType":"ferry","serviceID":"37746PHBR____","origin":{"location":{"locationName":"Portsmouth Harbour","crs":"PMH"}},"destination":{"location":{"locationName":"Ryde Pier Head","crs":"RYP"}},"subsequentCallingPoints":{"callingPointList":{"callingPoint": +{"locationName":"Ryde Pier Head","crs":"RYP","st":"16:37","et":"On time"}}}}]} \ No newline at end of file diff --git a/static/.dockerignore b/static/.dockerignore index 9a715f5..f1b90cf 100644 --- a/static/.dockerignore +++ b/static/.dockerignore @@ -1,2 +1,3 @@ .dockerignore -Dockerfile \ No newline at end of file +Dockerfile +*.xcf \ No newline at end of file diff --git a/static/Dockerfile b/static/Dockerfile index 1dccec4..e650692 100644 --- a/static/Dockerfile +++ b/static/Dockerfile @@ -1,14 +1,10 @@ FROM fedora:latest as compressor -RUN dnf install brotli nodejs npm -y && npm i uglify-js -g -COPY . /data/ -RUN rm -r /data/nginx -RUN uglifyjs /data/js/*.js --compress --mangle -RUN gzip -k -9 /data/*.json -RUN gzip -k -9 /data/styles/*.css -RUN gzip -k -9 /data/js/*.js -RUN gzip -k -9 /data/*.html +RUN dnf install brotli nodejs npm jq -y +RUN npm i uglifyjs-folder uglifycss html-minifier-terser -g +COPY . /data/in +RUN bash /data/in/conf/deploy.sh FROM nginx:mainline-alpine-slim RUN rm /etc/nginx/nginx.conf -COPY ./nginx/nginx.conf /etc/nginx/nginx.conf -COPY --from=compressor /data/ /site-static/ \ No newline at end of file +COPY ./conf/nginx.conf /etc/nginx/nginx.conf +COPY --from=compressor /data/out/ /site-static/ \ No newline at end of file diff --git a/static/board.html b/static/board.html index 8d92519..9a7305a 100644 --- a/static/board.html +++ b/static/board.html @@ -14,6 +14,7 @@ +
@@ -24,12 +25,16 @@
-
+

Example NRCC Message Here... ... ... NRCC Messages can sometimes be long, and sometimes @@ -51,7 +56,11 @@

-
+
+

Ferry Services

+
+ +

Oops

There was an error with your request

The station you are searching for cannot be found

diff --git a/static/conf/deploy.sh b/static/conf/deploy.sh new file mode 100644 index 0000000..34e2dd4 --- /dev/null +++ b/static/conf/deploy.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "Running UglifyJS" +JSIN="/data/in/js/" +JSOUT="/data/out/js" +uglifyjs-folder "$JSIN" -x ".js" -eo "$JSOUT" + +echo "Running UglifyCSS" +CSSIN="/data/in/styles/" +CSSOUT="/data/out/styles" + +cd $CSSIN +echo "Changed directory" +pwd +for f in * +do + if [ -f "$f" ]; then + uglifycss "$f" --output "$f"; + fi +done + +echo "Moving 'styles' to 'out'" +cp -r /data/in/styles /data/out/styles + +echo "Running html-minifier-terser" +HTMLIN="/data/in/" +HTMLOUT="/data/out" +html-minifier-terser --collapse-whitespace --remove-comments --file-ext html --input-dir /data/in/ --output-dir /data/out/ + +echo "Moving JSON Manifest file from root to output" +cat /data/in/manifest.json | jq -c > /data/out/manifest.json + +echo "Moving images folder from in/ to out/" +cp -r /data/in/images /data/out/images + +echo "Running GZIP & Brotli on all HTML, JS, CSS, JSON & SVG files" +find /data/out -type f -name \*.html -or -name \*.js -or -name \*.css -or -name \*.json -or -name \*.svg -or -name \*.ttf | while read file; do gzip -k -9 $file; brotli -k -q 11 $file; done \ No newline at end of file diff --git a/static/nginx/nginx.conf b/static/conf/nginx.conf similarity index 100% rename from static/nginx/nginx.conf rename to static/conf/nginx.conf diff --git a/static/js/boards.js b/static/js/boards.js index 4865446..55d649f 100644 --- a/static/js/boards.js +++ b/static/js/boards.js @@ -1,29 +1,4 @@ -// Enable delays -const delay = ms => new Promise(res => setTimeout(res, ms)); - -init() - -/* Supporting Functions */ -async function init() { - var stn = await getQuery("stn"); - log(`init: Looking up: ${stn}`); - var sv = await getQuery("sv"); - log(`init: Staff Version: ${sv}`); - if (sv === 'true') { - log("init: Staff Version not supported yet.") - log("init: Unable to proceed.") - } else { - try { - var data = await publicLdb(stn) - log("init: Fetched LDB Data") - parseLdb(data) - } catch (err) { - var data = null - log("init: Unable to fetch LDB data") - } - } -} - +// Fetch a known query parameter from the pages URL async function getQuery(param) { var params = new URLSearchParams(window.location.search) var query = params.get(param) @@ -34,124 +9,15 @@ async function getQuery(param) { } } -async function publicLdb(stn) { - var url = `${window.location.origin}/api/v1/ldb/${stn}`; - var resp = await fetch(url); - return await resp.json(); -} - +// Set page headers async function setHeaders(title,time) { var prefix = `OwlBoard - ` document.title = `${prefix}${title}` - document.getElementById("station_name").innerHTML = title + document.getElementById("stn_name").innerHTML = title document.getElementById("fetch_time").innerHTML = time.toLocaleTimeString() } -async function parseLdb(data) { - if (data.ERROR == "NOT_FOUND") { // Station not found - clearLoading(); - document.getElementById("error_notice").style = "display: block;"; - document.getElementById("err_not_found").style = "display: block;"; - setHeaders("Not Found",new Date()) - } else if (data == false) { // No data for station - clearLoading(); - document.getElementById("error_notice").style = "display: block;"; - document.getElementById("err_no_data").style = "display:block;"; - setHeaders("No Data",new Date()) - } else if (data == "err") { // Connection Error - clearLoading(); - document.getElementById("error_notice").style = "display: block;"; - document.getElementById("err_conn").style = "display: block;"; - setHeaders("Connection Error",new Date()) - await delay(5000); - log(`parseLdb: Waited five seconds, reloading`) - location.reload() - } else { - buildPage(data); - } -} - -// Build and Display Functions -async function buildPage(data) { - var stationName = data.GetStationBoardResult.locationName; - log(`buildPage: Data ready for ${stationName}`); - var generateTime = new Date(await data.GetStationBoardResult.generatedAt); - log(`buildPage: Data prepared at ${generateTime.toLocaleString()}`) - setHeaders(stationName, generateTime); - // Check for notices and if true pass to function - if (data.GetStationBoardResult.nrccMessages) { - displayNotices(data.GetStationBoardResult.nrccMessages.message) - } - if (typeof data.GetStationBoardResult.trainServices == 'undefined') { - displayNoTrains() - } else { - displayTrains(data) - } -} - -async function displayNotices(notices) { - // Input: data.GetStationBoardResult.nrccMessages.messages - // Processing: For each message, create a

inside #notices. - // If there is more than one notice, scroll between them. - // Output: Only to DOM. - //document.getElementById("notices").innerHTML = notices; -} - -async function displayNoTrains() { - document.getElementById('no_services').style = "display: block;"; - clearLoading(); -} - -async function displayTrains(data) { - log(`Inserting data in DOM`) - for(var i = 0; i < data.GetStationBoardResult.trainServices.service.length; i++) { - // Reset Vars - var svc = data.GetStationBoardResult.trainServices.service[i]; - displayService(svc); - } - - clearLoading(); - log(`Insertion complete`) -} - -async function displayService(svc) { - var table = document.getElementById("output"); - - // Determine Time Message - var sta = await parseTime(svc.sta); - var eta = await parseTime(svc.eta); - var std = await parseTime(svc.std); - var etd = await parseTime(svc.etd); - // Determine Platform Message - //if (svc.platform != undefined){var plt = svc.platform} else {var plt = "-"}; - var plt = await parsePlatform(svc); - // Define Table Row - var row = ` - - - - - - - - - - -
${svc.origin.location.locationName}${svc.destination.location.locationName}${plt.num}${sta.data}${eta.data}${std.data}${etd.data}
` - // Put Table Row - table.insertAdjacentHTML("beforeend", row) - // Parse cancelReason & delayReason - if (svc.cancelReason) { - var cancelRow = `

${svc.cancelReason}

` - table.insertAdjacentHTML("beforeend", cancelRow); - } - if (svc.delayReason) { - var delayRow = `

${svc.delayReason}

` - table.insertAdjacentHTML("beforeend", delayRow); - } - -} - +// Determine what should display in 'platform' column async function parsePlatform(svc){ if (svc.platform != undefined) { var platform = svc.platform; @@ -166,6 +32,7 @@ async function parsePlatform(svc){ return {num: platform, change: changed} } +// Use different time strings to default to make better on small screens async function parseTime(string){ switch (string) { case "Delayed": @@ -185,7 +52,7 @@ async function parseTime(string){ var change = ""; break; case undefined: - var output = ""; + var output = "-"; var change = ""; break; default: @@ -195,11 +62,7 @@ async function parseTime(string){ return {data: output, changed: change}; } +// Sometimes the origin or destination names are undefined, need to catch that async function parseName(){ -} - -// Log Helper -function log(msg) { - var time = new Date().toISOString(); - console.log(`${time} - ${msg}`) + return; } \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js index 7c985d9..7529c5f 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1,7 +1,17 @@ +// Toggle Loading Box async function clearLoading() { document.getElementById("loading").style = "display: none;"; } async function showLoading() { document.getElementById("loading").style = "display: block;"; +} + +// Enable delays +const delay = ms => new Promise(res => setTimeout(res, ms)); + +// Log Helper +function log(msg) { + var time = new Date().toISOString(); + console.log(`${time} - ${msg}`) } \ No newline at end of file diff --git a/static/js/public-board.js b/static/js/public-board.js new file mode 100644 index 0000000..248172a --- /dev/null +++ b/static/js/public-board.js @@ -0,0 +1,180 @@ +/* Page Init: */ +init() + +/* Supporting Functions */ +async function init() { + var stn = await getQuery("stn"); + log(`init: Looking up: ${stn}`); + var sv = await getQuery("sv"); + log(`init: Staff Version: ${sv}`); + if (sv === 'true') { + log("init: Staff Version not supported yet.") + log("init: Unable to proceed.") + } else { + try { + var data = await publicLdb(stn) + log("init: Fetched LDB Data") + parseLdb(data) + } catch (err) { + var data = null + log("init: Unable to fetch LDB data") + } + } +} + +async function publicLdb(stn) { + var url = `${window.location.origin}/api/v1/ldb/${stn}`; + var resp = await fetch(url); + return await resp.json();} + + +async function parseLdb(data) { + if (data.ERROR == "NOT_FOUND") { // Station not found + clearLoading(); + document.getElementById("error_notice").style = "display: block;"; + document.getElementById("err_not_found").style = "display: block;"; + setHeaders("Not Found",new Date()) + } else if (data == false) { // No data for station + clearLoading(); + document.getElementById("error_notice").style = "display: block;"; + document.getElementById("err_no_data").style = "display:block;"; + setHeaders("No Data",new Date()) + } else if (data == "err") { // Connection Error + clearLoading(); + document.getElementById("error_notice").style = "display: block;"; + document.getElementById("err_conn").style = "display: block;"; + setHeaders("Connection Error",new Date()) + await delay(5000); + log(`parseLdb: Waited five seconds, reloading`) + location.reload() + } else { + buildPage(data); + } +} + +// Build and Display Functions +async function buildPage(data) { + var stationName = data.GetStationBoardResult.locationName; + log(`buildPage: Data ready for ${stationName}`); + var generateTime = new Date(await data.GetStationBoardResult.generatedAt); + log(`buildPage: Data prepared at ${generateTime.toLocaleString()}`) + setHeaders(stationName, generateTime); + // Check for notices and if true pass to function + if (data.GetStationBoardResult.nrccMessages) { + displayNotices(data.GetStationBoardResult.nrccMessages.message) + } + if (typeof data.GetStationBoardResult.trainServices == 'undefined') { + displayNoTrains() + } else { + displayTrains(data) + } + if (data.GetStationBoardResult.ferryServices) { + displayFerry(data.GetStationBoardResult.ferryServices) + } +} + +async function displayNotices(notices) { + // Input: data.GetStationBoardResult.nrccMessages.messages + // Processing: For each message, create a

inside #notices. + // If there is more than one notice, scroll between them. + // Output: Only to DOM. + //document.getElementById("notices").innerHTML = notices; +} + +async function displayNoTrains() { + document.getElementById('no_services').style = "display: block;"; + clearLoading(); +} + +async function displayTrains(data) { + log(`Inserting data in DOM`) + for(var i = 0; i < data.GetStationBoardResult.trainServices.service.length; i++) { + // Reset Vars + var svc = data.GetStationBoardResult.trainServices.service[i]; + await displayService(svc); + } + + clearLoading(); + log(`Insertion complete`) +} + +async function displayFerry(ferrySvc) { + log(JSON.stringify(ferrySvc)) + for(var i = 0; i < ferrySvc.service.length; i++) { + displayFerryService(ferrySvc.service[i]) + } +} + +async function displayService(svc) { + var table = document.getElementById("output"); + + // Determine Time Message + var sta = await parseTime(svc.sta); + var eta = await parseTime(svc.eta); + var std = await parseTime(svc.std); + var etd = await parseTime(svc.etd); + // Determine Platform Message + //if (svc.platform != undefined){var plt = svc.platform} else {var plt = "-"}; + var plt = await parsePlatform(svc); + // Define Table Row + var row = ` + + + + + + + + + + +
${svc.origin.location.locationName}${svc.destination.location.locationName}${plt.num}${sta.data}${eta.data}${std.data}${etd.data}
` + // Put Table Row + table.insertAdjacentHTML("beforeend", row) + // Parse cancelReason & delayReason + if (svc.cancelReason) { + var cancelRow = `

${svc.cancelReason}

` + table.insertAdjacentHTML("beforeend", cancelRow); + } + if (svc.delayReason) { + var delayRow = `

${svc.delayReason}

` + table.insertAdjacentHTML("beforeend", delayRow); + } +} + +async function displayFerryService(svc) { + var table = document.getElementById("ferry"); + log(JSON.stringify(svc)) + // Determine Time Message + var sta = await parseTime(svc.sta); + var eta = await parseTime(svc.eta); + var std = await parseTime(svc.std); + var etd = await parseTime(svc.etd); + // Determine Platform Message + var plt = ""; + // Define Table Row + var row = ` + + + + + + + + + + +
${svc.origin.location.locationName}${svc.destination.location.locationName}${plt}${sta.data}${eta.data}${std.data}${etd.data}
` + // Put Table Row + table.insertAdjacentHTML("beforeend", row) + // Parse cancelReason & delayReason + if (svc.cancelReason) { + var cancelRow = `

${svc.cancelReason}

` + table.insertAdjacentHTML("beforeend", cancelRow); + } + if (svc.delayReason) { + var delayRow = `

${svc.delayReason}

` + table.insertAdjacentHTML("beforeend", delayRow); + } + document.getElementById("ferry").style = "display:block" +} \ No newline at end of file diff --git a/static/styles/boards.css b/static/styles/boards.css index 52ef333..25f5e7a 100644 --- a/static/styles/boards.css +++ b/static/styles/boards.css @@ -29,13 +29,36 @@ #station_name { position: absolute; - margin-top: 10px; - font-size: 15pt; - left: 10px; + max-width: 50%; + left: 7px; + top: 3px; +} + +.header-large{ + left: 0; + text-align: left; + font-size: 13pt; + margin-top: -2px; + overflow-wrap: anywhere; text-transform: capitalize; } -.header-right { +@media (min-width: 380px){ + .header-large{ + font-size: 13pt; + margin-top: 9px; + white-space: nowrap; + } +} +@media (min-width: 580px){ + .header-large{ + font-size: 19pt; + margin-top: 5px; + white-space: nowrap; + } +} + +.header-small { text-align: right; padding-right: 5px; margin: 3px; @@ -74,19 +97,28 @@ } table { + color: white; width: 100%; margin-top: 3px; font-size: 10.5px; } - +#ferry{ + margin-top: 45px; +} .name{ width: 25%; text-align: left; } + +.name-item { + color: yellow; +} + .plat{ width: 4%; text-align: center; } + .time{ width: 11.5%; text-align: center; @@ -98,7 +130,7 @@ table { margin: 0; margin-left: 3px; text-align: left; - color: white; + color: lightblue; } .changed{ @@ -108,6 +140,6 @@ table { /* Animations */ @keyframes pulse { 50% { - opacity: 0; + color: orange; } } \ No newline at end of file