This commit is contained in:
Martin Donnelly 2018-02-23 10:36:49 +00:00
commit cf8ce54146
47 changed files with 14331 additions and 0 deletions

55
.eslintrc.json Normal file
View File

@ -0,0 +1,55 @@
{
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module",
"ecmaFeatures": {
"jsx": false
}
},
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"arrow-spacing": "error",
"block-scoped-var": "error",
"block-spacing": "error",
"brace-style": ["error", "stroustrup", {}],
"camelcase": "error",
"comma-dangle": ["error", "never"],
"comma-spacing": ["error", { "before": false, "after": true }],
"comma-style": [1, "last"],
"consistent-this": [1, "_this"],
"curly": [1, "multi"],
"eol-last": 1,
"eqeqeq": 1,
"func-names": 1,
"indent": ["error", 2, { "SwitchCase": 1 }],
"lines-around-comment": ["error", { "beforeBlockComment": true, "allowArrayStart": true }],
"max-len": [1, 120, 2], // 2 spaces per tab, max 80 chars per line
"new-cap": 1,
"newline-before-return": "error",
"no-array-constructor": 1,
"no-inner-declarations": [1, "both"],
"no-mixed-spaces-and-tabs": 1,
"no-multi-spaces": 2,
"no-new-object": 1,
"no-shadow-restricted-names": 1,
"object-curly-spacing": ["error", "always"],
"padded-blocks": ["error", { "blocks": "never", "switches": "always" }],
"prefer-const": "error",
"prefer-template": "error",
"one-var": 0,
"quote-props": ["error", "always"],
"quotes": [1, "single"],
"radix": 1,
"semi": [1, "always"],
"space-before-blocks": [1, "always"],
"space-infix-ops": 1,
"vars-on-top": 1,
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
"spaced-comment": ["error", "always", { "markers": ["/"] }]
}
}

147
.gitignore vendored Normal file
View File

@ -0,0 +1,147 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
/src/bundle.js
/src/bundle.js.map
/live/

BIN
fonts/GothamNarrSSm-Bold.otf Executable file

Binary file not shown.

BIN
fonts/GothamNarrSSm-Book.otf Executable file

Binary file not shown.

BIN
fonts/GothamNarrSSm-Light.otf Executable file

Binary file not shown.

BIN
fonts/GothamNarrSSm-Medium.otf Executable file

Binary file not shown.

BIN
fonts/GothamSSm-Black.otf Executable file

Binary file not shown.

BIN
fonts/GothamSSm-Bold.otf Executable file

Binary file not shown.

BIN
fonts/GothamSSm-Book.otf Executable file

Binary file not shown.

BIN
fonts/GothamSSm-Light.otf Executable file

Binary file not shown.

BIN
fonts/GothamSSm-Medium.otf Executable file

Binary file not shown.

39
fonts/gotham.css Normal file
View File

@ -0,0 +1,39 @@
@font-face {
font-family: 'Gotham';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Book.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Light';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Light.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Medium';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Medium.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Bold';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Bold.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Black';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Black.otf) format('opentype');
unicode-range: U+0-10FFFF;
}

37
gulp/backbone.js Normal file
View File

@ -0,0 +1,37 @@
'use strict';
const browserify = require('browserify');
const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const uglify = require('gulp-uglify-es').default;
const sourcemaps = require('gulp-sourcemaps');
const gutil = require('gulp-util');
const rename = require('gulp-rename');
const stripDebug = require('gulp-strip-debug');
gulp.task('bundleBackbone', function () {
// set up the browserify instance on a task basis
const b = browserify({
'debug': true,
'entries': './src/v1/js/app.js'
});
return b.bundle()
.pipe(source('app.js'))
.pipe(buffer())
// .pipe(stripDebug())
.pipe(rename('bundle.js'))
.pipe(sourcemaps.init({ 'loadMaps': true }))
// Add transformation tasks to the pipeline here.
// .pipe(uglify())
.on('error', gutil.log)
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./live/js'));
});
gulp.task('buildBackbone', ['bundleBackbone'], function() {
gulp.watch('src/v1/js/**/*.js', ['bundleBackbone']);
});

71
gulp/build.js Normal file
View File

@ -0,0 +1,71 @@
const gulp = require('gulp'),
autoprefixer = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
concat = require('gulp-concat'),
cache = require('gulp-cache'),
htmlmin = require('gulp-htmlmin'),
inject = require('gulp-inject'),
del = require('del'),
htmlreplace = require('gulp-html-replace');
const scss = require('gulp-scss');
const sass = require('gulp-sass');
const googleWebFonts = require('gulp-google-webfonts');
const fontOptions = { };
gulp.task('styles', function() {
return gulp.src(['node_modules/backbone.modal/backbone.modal.css', 'node_modules/backbone.modal/backbone.modal.theme.css'])
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
/* .pipe(gulp.dest('dist/css'))*/
/* .pipe(rename({suffix: '.min'}))*/
.pipe(concat('style.min.css'))
.pipe(cssnano())
.pipe(gulp.dest('live/css'));
});
gulp.task('copy', function() {
gulp.src(['src/img/**/*']).pipe(gulp.dest('live/img'));
gulp.src(['src/browserconfig.xml', 'src/site.webmanifest', 'src/service-worker.js', 'src/bridger.js']).pipe(gulp.dest('live'));
gulp.src(['src/index.html']).pipe(gulp.dest('live'));
});
gulp.task('clean', function() {
return del(['live']);
});
gulp.task('customMUI', function() {
return gulp.src(['src/css/custom.scss'])
.pipe(sass({ 'outputStyle': 'compressed' }).on('error', sass.logError))
// .pipe(cssnano())
.pipe(rename('mui.custom.css'))
// .pipe(gulp.dest(`${dest}/css`));
.pipe(gulp.dest('live/css'));
});
gulp.task('vendor', function() {
return gulp.src([
'node_modules/muicss/dist/js/mui.min.js'
])
.pipe(concat('vendor.js'))
/* .pipe(uglify({ 'mangle': false }))*/
.pipe(gulp.dest(`live/js`));
});
gulp.task('fonts', function() {
return gulp.src('src/fonts.list')
.pipe(googleWebFonts(fontOptions))
.pipe(gulp.dest(`live/fonts`))
;
});
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'));
});

7
gulpfile.js Normal file
View File

@ -0,0 +1,7 @@
const gulp = require('gulp');
const requireDir = require('require-dir');
requireDir('./gulp');
gulp.task('default', ['bundleBackbone', 'styles', 'copy', 'customMUI', 'vendor', 'fonts', 'gotham']);

11814
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

61
package.json Normal file
View File

@ -0,0 +1,61 @@
{
"name": "jubilee",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"apicache": "^1.2.0",
"backbone": "^1.3.3",
"browserify": "^16.1.0",
"debug-logger": "^0.4.1",
"eslint": "^4.18.0",
"express": "^4.16.2",
"fecha": "^2.3.2",
"feedme": "^1.1.2",
"geolocation": "^0.2.0",
"gulp-autoprefixer": "^4.1.0",
"gulp-cache": "^1.0.2",
"gulp-concat": "^2.6.1",
"gulp-cssnano": "^2.1.2",
"gulp-html-replace": "^1.6.2",
"gulp-htmlmin": "^4.0.0",
"gulp-inject": "^4.3.0",
"gulp-uglify": "^3.0.0",
"jquery": "^3.3.1",
"lodash": "^4.17.5",
"log4js": "^2.5.3",
"loggy": "^1.0.2",
"muicss": "^0.9.36",
"node-foursquare-venues": "^1.1.0",
"openweather-apis": "^3.3.5",
"uglifyify": "^4.0.5",
"underscore": "^1.8.3"
},
"devDependencies": {
"expect.js": "^0.3.1",
"gulp": "^3.9.1",
"gulp-google-webfonts": "0.0.14",
"gulp-rename": "^1.2.2",
"gulp-sass": "^3.1.0",
"gulp-scss": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-strip-debug": "^2.0.0",
"gulp-uglify-es": "^1.0.1",
"lodash.assign": "^4.2.0",
"mocha": "^5.0.1",
"node-fetch": "^2.0.0",
"node-geocoder": "^3.22.0",
"require-dir": "^1.0.0",
"requirejs": "^2.3.5",
"sinon": "^4.3.0",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.10.0",
"whatwg-fetch": "^2.0.3"
}
}

69
server.js Normal file
View File

@ -0,0 +1,69 @@
const express = require('express');
const path = require('path');
const apicache = require('apicache');
const logger = require('log4js').getLogger('Server');
const weather = require('./server/weather');
const euronews = require('./server/euronews');
const foursquare = require('./server/foursquare');
logger.level = 'debug';
const app = express();
const port = process.env.PORT || 3000;
const sitePath = 'live';
apicache.options({ 'debug': true });
const cache = apicache.middleware;
app.use(express.static(path.join(__dirname, sitePath)));
app.get('/weather', cache('20 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
weather.doGetOpenWeather(req.query.ll)
.then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
});
else {
// throw new Error('Weather: LL missing');
logger.warn('Weather: LL missing');
res.status(500).send('LL Missing');
}
});
app.get('/fsexplore', cache('30 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
foursquare.doGetFourSquareExplore(req.query.ll)
.then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
});
else {
// throw new Error('Weather: LL missing');
logger.warn('FS: LL missing');
res.status(500).send('LL Missing');
}
});
app.get('/news', cache('20 minutes'), (req, res) => {
euronews.getEuroNews().then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
});
});
app.listen(port, (err) => {
if (err)
return logger.error('Server error:', err);
logger.info(`Jubilee Server is listening on ${port}`);
});

77
server/euronews.js Normal file
View File

@ -0,0 +1,77 @@
const FeedMe = require('feedme');
const fecha = require('fecha');
const http = require('http');
const logger = require('log4js').getLogger('euronews');
module.exports = {
'getEuroNews': doGetEuroNews,
'render': render
};
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;
}
}
function doGetEuroNews() {
return new Promise((resolve, reject) => {
logger.info('Retrieving Euronews Headlines..');
http.get('http://feeds.feedburner.com/euronews/en/news/', (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200)
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
else if (!/^text\/xml/.test(contentType))
error = new Error('Invalid content-type.\n' +
`Expected text/xml but received ${contentType}`);
if (error) {
logger.error(error.message);
// consume response data to free up memory
res.resume();
return reject(error);
}
const parser = new FeedMe(true);
res.pipe(parser);
parser.on('end', () => {
return resolve(parser.done());
});
});
});
}
function render(data) {
logger.debug('Rendering euronews');
// logger.debug(JSON.stringify(data));
const html = [];
const items = data.slice(0, 10);
for (const item of items)
html.push(new Template(item).toString());
return(html.join(''));
}

26
server/foursquare.js Normal file
View File

@ -0,0 +1,26 @@
const logger = require('log4js').getLogger('FSQ');
const foursquare = require('node-foursquare-venues')('IXXFUGW3NC3DEVS2V5EU4NV4CL5E12AYGUPIR2D3U3B5DX4B', 'MZRIJDCEKUMVERA1OKVAIZI0TYAEBD3W2A2AGPTPI5TOLL1D', '20170801');
logger.level = 'debug';
function doGetFourSquareExplore(ll) {
const [lat, long ] = ll.split(',');
return new Promise((resolve, reject) => {
const fsObj = {
'll': ll,
'section': 'topPicks',
'v': '20170801',
'limit': 3
};
foursquare.venues.explore(fsObj, function(err, fsData) {
if (err)
return reject(err);
else
return resolve(fsData);
});
});
}
module.exports = { doGetFourSquareExplore };

42
server/weather.js Normal file
View File

@ -0,0 +1,42 @@
const logger = require('log4js').getLogger('Weather');
const weather = require('openweather-apis');
logger.level = 'debug';
const openWeatherApiKey = process.env.openweatherAPI || '';
weather.setAPPID(openWeatherApiKey);
weather.setLang('en');
// weather.setCity('Glasgow City');
function doGetOpenWeather(ll) {
const [lat, long ] = ll.split(',');
return new Promise((resolve, reject) => {
weather.setCoordinate(lat, long);
weather.getWeatherForecast( function(err, wData) {
if (err)
return reject(err);
else
return resolve(wData);
});
});
}
function doGetOpenWeatherForecast(ll) {
const [lat, long ] = ll.split(',');
return new Promise((resolve, reject) => {
weather.setCoordinate(lat, long);
weather.getWeatherForecastForDays(5, function(err, wData) {
if (err)
return reject(err);
else
return resolve(wData);
});
});
}
module.exports = { doGetOpenWeather, doGetOpenWeatherForecast };

0
src/bridger.js Normal file
View File

9
src/browserconfig.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/img/mstile-150x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

96
src/css/common.css Normal file
View File

@ -0,0 +1,96 @@
body {
background-color: #eee;
}
.card {
position: relative;
background-color: #fff;
min-height:48px;
margin: 8px;
border-bottom-color: #666666;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.itemRow {
position: relative;
background-color: #fff;
min-height:48px;
border-bottom-color: #666666;
}
.mui--text-display4, .mui--text-display3 {
font-family: "Roboto Slab", "Helvetica Neue", Helvetica, Arial;
}
.temp0, .temp1, .temp2, .temp3, .temp4, .temp5 {
color: rgb(80,181,221)
}
.temp6 {
color: rgb(78,178,206)
}
.temp7 {
color: rgb(76, 176, 190)
}
.temp8 {
color: rgb(73, 173, 175)
}
.temp9 {
color: rgb(72, 171, 159)
}
.temp10 {
color: rgb(70, 168, 142)
}
.temp11 {
color: rgb(68, 166, 125)
}
.temp12 {
color: rgb(66, 164, 108)
}
.temp13 {
color: rgb(102, 173, 94)
}
.temp14 {
color: rgb(135, 190, 64)
}
.temp15 {
color: rgb(179, 204, 26)
}
.temp16 {
color: rgb(214, 213, 28)
}
.temp17 {
color: rgb(249, 202, 3)
}
.temp18 {
color: rgb(246, 181, 3)
}
.temp19 {
color: rgb(244, 150, 26)
}
.temp20 {
color: rgb(236, 110, 5)
}
.day {
font-family: "Roboto Slab", "Helvetica Neue", Helvetica, Arial, sans-serif;
text-transform: uppercase;
}
.summary::first-letter {
text-transform: capitalize
}

301
src/css/custom.scss Normal file
View File

@ -0,0 +1,301 @@
// import MUI colors
@import "./node_modules/muicss/lib/sass/mui/colors";
// customize MUI variables
$mui-primary-color: mui-color('blue-grey', '500');
$mui-primary-color-dark: mui-color('blue-grey', '700');
$mui-primary-color-light: mui-color('blue-grey', '100');
$mui-accent-color: mui-color('deep-purple', '900');
$mui-accent-color-dark: mui-color('indigo', 'A100');
$mui-accent-color-light: mui-color('indigo', 'A400');
$mui-base-font-family: 'Roboto', "Helvetica Neue", Helvetica, Arial, Verdana, "Trebuchet MS";
// import MUI SASS
@import "./node_modules/muicss/lib/sass/mui";
@import "./src/css/horscroll";
////
body {
background-color: mui-color('grey', '100');
}
#header {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 2;
transition: left 0.2s;
}
ul {
margin: 0;
padding: 0;
}
li {
display: inline;
margin: 0;
padding: 0 4px 0 0;
}
.dates {
padding: 2px;
border: solid 1px #80007e;
background-color: #ffffff;
}
#btc, #fx, #trend {
font-size: 85%;
}
.up, .ontime, .trendUp {
color: mui-color('green') !important;
}
.down, .delayed, .trendDown {
color: $mui-text-danger !important;
}
.nochange {
color: #000000;
}
.password {
border: 1px solid mui-color('grey', '400');
background-color: mui-color('grey', '200');
font-family: monospace;
white-space: pre;
}
.trendUp:before {
content: "";
}
.trendDown:before {
content: ''
}
.card {
position: relative;
background-color: #fff;
min-height: 48px;
margin: 8px;
border-bottom-color: #666666;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.itemRow {
background-color: #fff;
min-height: 48px;
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
line-height: 48px;
}
.cardTitle {
border-bottom-color: mui-color('grey', '200');
border-bottom-width: 1px;
border-bottom-style: solid;
}
.entry {
height: 36px;
margin: 6px 0;
vertical-align: middle;
}
.time {
font-family: 'Roboto';
}
.titleBar {
font-family: 'Gotham Light';
font-size: 125%;
}
#trains, #trainResults {
overflow-y: auto;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.tableBody {
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.unsliced {
height: 455px;
}
.sliced {
height: 300px;
}
/* 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;
}
}
.temp0, .temp1, .temp2, .temp3, .temp4, .temp5 {
color: rgb(80, 181, 221)
}
.temp6 {
color: rgb(78, 178, 206)
}
.temp7 {
color: rgb(76, 176, 190)
}
.temp8 {
color: rgb(73, 173, 175)
}
.temp9 {
color: rgb(72, 171, 159)
}
.temp10 {
color: rgb(70, 168, 142)
}
.temp11 {
color: rgb(68, 166, 125)
}
.temp12 {
color: rgb(66, 164, 108)
}
.temp13 {
color: rgb(102, 173, 94)
}
.temp14 {
color: rgb(135, 190, 64)
}
.temp15 {
color: rgb(179, 204, 26)
}
.temp16 {
color: rgb(214, 213, 28)
}
.temp17 {
color: rgb(249, 202, 3)
}
.temp18 {
color: rgb(246, 181, 3)
}
.temp19 {
color: rgb(244, 150, 26)
}
.temp20 {
color: rgb(236, 110, 5)
}
.day {
font-family: "Roboto Slab", "Helvetica Neue", Helvetica, Arial, sans-serif;
text-transform: uppercase;
}
.summary::first-letter {
text-transform: capitalize
}
#weather{
margin-top: 15px;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
#newsShell {
height:225px;
}
#news{
height: 275px;
margin-top:15px;
}

29
src/css/horscroll.scss Normal file
View File

@ -0,0 +1,29 @@
.scrolling-wrapper-flexbox {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
.scrollCard {
flex: 0 0 auto;
margin-right: 3px;
}
}
.scrollCard {
border: 1px solid white;
width: 250px;
height: 175px;
background: mui-color('white-alpha-12');
}
.scrolling-wrapper, .scrolling-wrapper-flexbox {
height: 75px;
width: 100%;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
src/img/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/img/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/img/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,372 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M3195 4394 c-165 -80 -361 -175 -435 -211 -74 -36 -198 -96 -275
-133 -77 -37 -243 -118 -370 -180 -126 -62 -250 -121 -274 -131 -71 -31 -141
-81 -147 -105 -3 -12 0 -41 6 -64 20 -76 6 -127 -54 -190 -29 -30 -70 -80 -93
-110 -50 -68 -143 -241 -143 -266 0 -11 13 -58 29 -106 59 -172 78 -340 46
-402 -8 -15 -15 -36 -15 -47 0 -10 -19 -46 -43 -81 -24 -35 -53 -85 -66 -113
-37 -78 -120 -341 -132 -414 -20 -129 -42 -206 -65 -234 -13 -16 -24 -41 -24
-57 0 -39 -59 -111 -159 -194 -59 -48 -83 -75 -88 -96 -4 -24 -23 -41 -93 -85
-75 -47 -89 -60 -94 -88 -13 -67 60 -201 139 -258 25 -18 45 -36 45 -41 0 -16
-142 -293 -219 -426 -96 -169 -108 -190 -160 -284 l-42 -78 25 0 c28 0 26 -3
111 150 33 58 81 143 108 190 64 111 317 624 317 643 0 7 8 22 19 31 17 16 23
16 87 1 114 -26 160 -43 255 -98 102 -58 172 -126 189 -184 14 -46 15 -43
-116 -349 -93 -218 -155 -377 -149 -383 9 -9 22 13 51 88 33 85 44 83 14 -4
-24 -69 -25 -85 -6 -85 16 0 22 14 70 165 20 61 48 146 64 190 15 44 34 103
42 130 7 28 27 80 43 117 26 59 32 67 52 62 12 -3 26 1 33 8 11 14 15 27 52
187 11 47 27 92 36 102 8 9 30 67 49 128 21 70 42 121 58 138 14 16 36 40 49
55 13 14 48 36 77 47 l53 20 -6 -22 c-49 -173 -43 -336 17 -404 36 -41 95 -73
136 -73 78 0 204 72 328 187 74 69 113 119 113 147 0 11 17 54 39 95 21 42 46
97 56 124 20 52 25 55 47 30 14 -16 15 -38 6 -168 -11 -177 -44 -316 -83 -353
-33 -30 -34 -55 -2 -43 12 5 54 15 92 22 39 6 85 18 104 26 21 9 48 12 70 8
32 -5 45 0 100 36 35 23 81 60 102 81 l39 40 0 -29 c0 -15 -7 -59 -15 -97 -8
-38 -12 -73 -9 -78 3 -4 -10 -30 -29 -56 -37 -49 -49 -55 -229 -103 -38 -10
-61 -35 -210 -223 -42 -53 -73 -83 -97 -93 -55 -21 -66 -29 -70 -49 -6 -34
-34 -56 -128 -100 -57 -27 -94 -50 -97 -62 -6 -18 -40 -40 -229 -148 l-69 -39
40 -3 c30 -2 52 4 88 26 27 16 79 46 116 66 37 20 76 48 88 63 11 15 62 48
113 74 73 37 94 53 102 76 5 15 10 29 11 31 5 8 44 -24 40 -33 -2 -5 27 -20
65 -31 71 -22 99 -16 32 7 -32 10 -35 13 -18 19 18 7 18 9 -8 19 -15 6 -39 14
-52 17 -31 8 -32 20 -2 28 25 7 70 44 80 66 4 8 42 60 85 115 54 69 91 107
120 122 43 23 161 58 168 50 2 -2 -9 -21 -24 -43 -59 -82 -184 -267 -273 -405
-150 -234 -170 -269 -155 -269 8 0 40 44 72 97 32 54 88 144 126 201 37 57 68
106 68 108 0 7 165 243 191 274 l21 25 -16 -27 c-19 -32 -20 -52 -5 -76 9 -14
7 -25 -9 -52 -11 -19 -36 -64 -57 -100 -20 -36 -42 -72 -49 -80 -14 -17 -56
-89 -56 -96 0 -3 5 -2 12 2 7 4 8 3 4 -5 -4 -6 -11 -9 -16 -6 -7 4 -33 -33
-115 -165 -45 -73 -55 -89 -55 -95 0 -14 61 -1 172 35 68 23 188 54 268 70
158 33 206 44 233 55 21 9 22 -4 3 -71 -26 -93 -26 -94 -2 -94 20 0 24 11 50
128 15 70 32 131 37 137 5 5 9 17 9 26 0 42 146 477 180 537 5 9 24 53 41 97
17 44 39 96 50 115 10 19 32 73 49 119 16 46 40 98 53 115 24 32 147 250 147
260 0 11 190 297 228 345 119 148 280 336 343 401 105 108 372 356 398 369 19
11 21 9 21 -17 0 -58 -21 -90 -123 -182 -130 -120 -312 -302 -359 -360 -240
-298 -381 -497 -523 -735 -26 -44 -53 -87 -61 -95 -18 -21 -130 -249 -229
-466 -59 -130 -186 -465 -205 -541 -16 -67 7 -30 37 59 79 233 162 435 279
678 82 169 123 243 223 401 20 31 63 100 96 152 33 53 63 96 68 96 4 1 7 -12
7 -28 0 -17 4 -33 10 -36 6 -4 8 10 4 39 -6 40 -3 51 26 96 46 71 126 170 133
164 3 -4 12 9 20 27 20 46 62 100 71 91 4 -4 6 0 4 8 -1 8 3 14 10 12 6 -1 11
4 10 10 -2 7 2 12 9 11 7 -2 21 11 33 27 29 40 40 38 33 -7 -4 -24 -3 -34 4
-30 15 9 22 66 9 71 -7 3 -6 5 2 5 6 1 12 7 12 15 0 7 37 46 82 87 71 64 80
70 75 46 -4 -15 -10 -39 -13 -54 -11 -53 22 2 35 59 10 40 21 60 48 83 l36 30
-5 -25 c-3 -14 -16 -75 -28 -137 l-23 -111 -45 -17 c-52 -21 -177 -102 -224
-148 -18 -17 -60 -58 -93 -89 -95 -90 -204 -256 -232 -353 -7 -22 -15 -71 -19
-110 -6 -58 -13 -77 -39 -110 -52 -65 -229 -368 -310 -530 -108 -217 -219
-491 -261 -643 -8 -30 -19 -62 -24 -73 -8 -15 -7 -19 4 -19 10 0 21 20 32 57
9 32 20 65 24 73 4 8 15 38 23 65 39 131 129 349 220 532 71 144 74 126 5 -27
-51 -112 -228 -649 -228 -691 0 -6 10 -9 22 -7 15 2 26 16 38 48 31 87 162
359 236 491 l53 95 128 -17 c139 -20 508 -43 523 -34 6 4 10 -3 10 -14 0 -23
-19 -28 -38 -9 -9 9 -12 9 -12 0 0 -8 -25 -11 -82 -11 -46 0 -94 -5 -108 -11
l-25 -10 25 4 c14 3 76 6 137 7 85 1 118 -2 135 -13 37 -25 138 -130 138 -145
0 -34 -93 -63 -272 -83 -51 -6 -151 -20 -223 -30 -101 -16 -393 -50 -422 -50
-3 0 -2 8 1 18 4 9 -9 -1 -28 -23 -20 -22 -35 -45 -36 -51 0 -19 -70 -75 -88
-72 -20 4 -54 -54 -46 -77 9 -23 49 -18 62 8 22 42 52 91 83 137 l32 46 81 3
c44 1 110 6 146 11 107 16 158 12 214 -14 28 -13 98 -41 156 -61 100 -35 114
-37 299 -47 150 -7 205 -14 243 -29 27 -10 51 -19 53 -19 3 0 5 123 5 273 l0
274 -40 12 c-22 6 -40 16 -40 20 0 5 -12 12 -27 15 -38 9 -214 79 -210 83 2 2
30 -6 63 -17 93 -31 87 -24 -27 33 -185 93 -360 230 -435 340 -14 21 -42 73
-62 114 -34 74 -36 81 -36 187 0 109 1 113 39 188 39 79 160 239 194 258 11 5
29 26 41 46 31 49 141 138 217 176 46 22 86 33 150 40 169 18 177 19 162 29
-10 6 -10 9 -1 9 6 0 12 7 12 15 0 18 -13 19 -60 5 -19 -6 -60 -14 -90 -19
-30 -5 -64 -13 -74 -17 -15 -6 -18 -4 -13 8 3 9 18 74 32 144 29 137 53 194
91 214 17 10 25 25 30 59 4 25 13 63 20 85 8 21 14 50 14 63 0 22 -3 24 -25
19 -33 -8 -33 10 0 36 25 19 65 86 65 108 0 6 -12 3 -27 -8 -46 -32 -129 -47
-258 -46 -103 0 -195 14 -195 29 0 3 41 44 91 92 50 48 105 108 122 133 39 56
55 74 98 107 31 23 28 25 -15 7 -9 -3 -15 5 -19 25 -10 47 14 109 75 199 17
24 27 46 24 49 -3 3 -25 -3 -48 -14 -62 -29 -173 -49 -183 -33 -3 6 23 41 59
79 61 65 73 81 62 81 -2 0 -27 -20 -55 -43 -39 -34 -74 -51 -149 -75 -106 -35
-213 -49 -252 -34 -24 9 -23 10 23 30 26 11 47 25 47 30 0 6 22 26 48 45 85
61 222 269 209 318 -3 11 1 36 9 56 25 58 11 46 -24 -21 -37 -71 -107 -148
-207 -228 -79 -63 -129 -90 -196 -106 -50 -12 -123 -1 -84 13 157 56 308 185
371 318 30 63 79 225 71 234 -2 1 -18 -19 -37 -44 -42 -57 -138 -107 -231
-122 -35 -6 -64 -9 -66 -7 -2 2 19 34 47 71 49 63 61 96 23 63 -10 -9 -29 -18
-43 -21 -14 -3 -56 -15 -95 -26 -103 -31 -317 -30 -394 2 -54 22 -55 22 -225
276 -94 140 -175 256 -181 258 -5 1 -145 -63 -310 -143z m389 -148 c20 -55 41
-105 45 -110 11 -13 61 -144 61 -159 0 -6 -6 -3 -12 6 -18 24 -219 62 -256 48
-16 -5 -20 -10 -10 -10 12 -1 15 -7 11 -23 -8 -32 -24 -50 -38 -44 -7 2 -21
-11 -31 -30 -10 -19 -25 -34 -34 -35 -35 -2 79 -25 118 -24 23 1 42 -2 42 -7
0 -4 6 -8 14 -8 8 0 18 -4 21 -10 4 -6 -2 -7 -17 -1 -13 5 -41 6 -63 2 l-40
-7 57 -8 c32 -4 60 -11 63 -16 3 -4 21 -11 40 -14 19 -4 35 -11 35 -15 0 -4
-39 -27 -87 -50 -49 -22 -95 -46 -103 -51 -8 -5 -37 -20 -65 -32 -27 -12 -74
-34 -104 -49 l-53 -27 -102 24 c-55 13 -137 35 -181 48 -117 36 -163 41 -70 8
72 -26 80 -31 78 -53 -1 -20 9 -30 60 -57 52 -27 64 -30 73 -18 5 8 16 11 22
7 9 -5 4 -11 -13 -19 -21 -10 -30 -9 -48 2 -49 32 -198 86 -236 86 -52 0 -62
-16 -91 -140 -26 -115 -36 -140 -52 -139 -10 0 -10 2 0 6 6 2 12 13 12 24 0
10 -6 19 -12 20 -10 0 -10 2 0 6 6 2 12 14 12 24 0 11 4 18 9 15 15 -9 20 46
6 61 -18 18 -28 16 -21 -3 5 -13 1 -15 -20 -11 -17 3 -29 -1 -36 -13 -7 -10
-18 -16 -25 -13 -7 3 -13 0 -13 -6 0 -6 -19 -11 -45 -11 -25 0 -45 -4 -45 -10
0 -5 -4 -10 -10 -10 -5 0 -10 8 -10 17 0 16 -1 16 -12 0 -9 -11 -32 -17 -78
-18 -69 -3 -110 11 -110 38 0 12 -3 13 -14 4 -11 -9 -15 -9 -19 1 -4 9 -6 8
-6 -3 -1 -26 -45 -19 -49 9 -3 17 -10 22 -31 22 -15 0 -32 5 -38 11 -6 6 -23
12 -38 14 -28 3 -45 21 -55 60 -5 17 -13 20 -57 17 -33 -3 -62 2 -83 13 -38
19 -35 30 4 15 25 -9 27 -8 21 11 -5 15 -3 20 6 17 8 -3 15 -14 17 -26 3 -22
13 -28 25 -16 3 3 1 14 -5 24 -12 19 -4 55 11 46 5 -4 11 -3 13 2 1 4 31 22
66 40 93 50 148 82 139 82 -4 0 -49 -21 -100 -48 -51 -26 -125 -62 -165 -81
-40 -19 -74 -37 -76 -40 -4 -6 55 -271 73 -331 6 -19 23 -91 37 -160 14 -69
32 -142 39 -162 9 -28 10 -41 1 -53 -9 -13 -19 10 -53 117 -22 73 -55 189 -72
258 -17 69 -42 166 -55 215 -36 130 -32 162 23 180 13 4 63 27 110 50 93 46
369 181 601 293 80 38 219 106 310 150 227 110 414 199 485 232 33 16 93 44
134 63 l74 35 28 -77 c15 -42 43 -121 63 -175z m-174 -356 c0 -5 -4 -10 -10
-10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m330 -36 c0 -8 -4
-14 -9 -14 -6 0 -9 9 -8 19 1 22 17 18 17 -5z m-200 -4 c0 -5 -2 -10 -4 -10
-3 0 -8 5 -11 10 -3 6 -1 10 4 10 6 0 11 -4 11 -10z m155 -23 c-11 -6 -22 -12
-25 -12 -3 0 -11 -4 -18 -9 -7 -4 -15 -6 -18 -3 -5 5 56 37 71 36 6 0 1 -5
-10 -12z m-555 -272 c-14 -8 -29 -14 -35 -14 -5 0 1 6 15 14 14 8 30 14 35 14
6 0 -1 -6 -15 -14z m224 -36 l46 -31 -57 -28 c-32 -15 -73 -39 -91 -54 -29
-25 -35 -27 -64 -17 -55 19 -108 45 -108 51 0 4 17 12 38 19 20 6 69 29 107
50 39 21 73 39 76 40 3 0 27 -13 53 -30z m-484 -4 c14 -7 41 -16 60 -20 19 -4
44 -14 55 -21 11 -8 26 -14 35 -14 8 0 20 -10 25 -22 7 -16 33 -31 83 -49 40
-15 79 -33 87 -39 9 -7 25 -14 38 -16 12 -1 50 -9 84 -17 34 -8 64 -13 67 -10
7 7 69 -18 78 -31 5 -7 8 -5 8 4 0 13 2 13 10 0 7 -11 0 -17 -32 -28 -36 -12
-41 -12 -52 4 -12 16 -14 15 -19 -8 -4 -14 -25 -37 -47 -52 -45 -29 -100 -108
-100 -140 1 -17 5 -14 21 14 l20 35 -5 -35 c-3 -19 -8 -54 -12 -76 -3 -23 -2
-50 3 -59 8 -14 13 -9 24 30 29 100 133 177 239 178 68 1 111 -20 145 -72 37
-56 42 -102 16 -147 -12 -20 -20 -38 -18 -40 2 -2 15 2 30 9 19 8 25 17 21 30
-4 13 0 20 15 24 24 6 25 8 11 33 -8 15 -6 21 7 29 17 10 17 10 1 11 -25 0
-42 68 -23 90 19 21 19 30 1 30 -24 0 -29 20 -10 41 22 25 14 32 -20 19 -43
-16 -53 -11 -26 11 24 20 24 20 3 14 -14 -4 -23 -2 -23 4 0 6 -12 11 -26 11
-16 0 -24 5 -22 13 5 14 48 23 48 10 0 -5 28 -7 63 -5 52 3 66 0 85 -16 13
-12 53 -31 88 -44 35 -12 64 -29 64 -36 0 -7 9 -25 19 -40 41 -57 91 -295 91
-430 0 -53 4 -81 10 -77 6 3 10 16 10 29 0 12 14 48 31 79 16 31 36 85 43 119
l13 63 12 -45 c6 -25 21 -76 32 -115 11 -38 29 -99 40 -135 24 -83 24 -121 -1
-153 -36 -46 -53 -100 -66 -217 -18 -155 -17 -288 1 -320 8 -14 14 -30 15 -35
0 -6 -42 -74 -92 -151 -51 -77 -120 -190 -153 -252 -33 -61 -82 -146 -110
-188 -42 -65 -59 -82 -107 -107 -90 -47 -159 -47 -343 -3 -82 20 -188 48 -235
62 -112 34 -281 119 -339 171 -71 63 -162 199 -247 367 -42 83 -82 158 -89
166 -7 8 -72 134 -145 280 -72 146 -136 269 -141 275 -22 23 6 277 34 300 7 6
23 34 37 64 44 95 135 138 274 129 69 -4 81 5 35 30 -16 9 -55 12 -122 10
l-98 -4 3 46 3 45 160 81 160 82 26 110 c14 61 31 118 38 126 14 17 57 13 101
-9z m605 -74 c3 -5 55 -40 116 -77 62 -38 106 -70 100 -72 -7 -2 -33 -1 -57 3
-34 5 -44 10 -40 21 7 19 -4 18 -20 -3 -13 -17 -15 -17 -27 -1 -10 12 -17 14
-24 7 -11 -11 -76 0 -196 32 l-37 10 27 22 c70 57 142 83 158 58z m-1705 -268
c0 -34 -5 -74 -11 -90 -7 -15 -15 -62 -19 -103 -11 -99 -76 -459 -85 -468 -2
-2 -13 -1 -25 3 -19 6 -21 12 -15 48 7 43 31 165 59 292 23 106 37 225 46 380
5 77 9 142 10 145 6 18 39 -157 40 -207z m383 190 c-7 -2 -21 -2 -30 0 -10 3
-4 5 12 5 17 0 24 -2 18 -5z m-479 -50 c-9 -82 -26 -207 -30 -223 -3 -12 -17
-14 -66 -12 -52 3 -61 1 -50 -10 7 -7 34 -13 62 -13 56 0 56 4 17 -115 -11
-36 -33 -114 -48 -175 -16 -64 -27 -98 -28 -80 -2 39 -24 128 -46 185 -9 25
-22 66 -28 91 -10 45 -9 49 41 142 28 52 66 115 84 139 49 63 93 118 95 118 2
0 0 -21 -3 -47z m895 -17 c-10 -9 -69 -36 -69 -32 0 7 52 35 64 36 5 0 7 -2 5
-4z m977 -43 c-10 -10 -19 5 -10 18 6 11 8 11 12 0 2 -7 1 -15 -2 -18z m-1137
-47 c-2 -2 -15 -9 -29 -15 -24 -11 -24 -11 -6 3 16 13 49 24 35 12z m-59 -30
c0 -2 -7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 25 14 25 6z m-154 -91 c-11 -8
-25 -15 -30 -15 -6 1 0 7 14 15 32 19 40 18 16 0z m170 -51 c-4 -15 -14 -24
-26 -24 -25 0 -25 1 2 47 18 29 24 34 26 20 2 -10 1 -29 -2 -43z m-549 -6 c-3
-8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m293 7 c0 -2 -13 -11 -30
-20 -38 -19 -40 -11 -2 9 31 17 32 18 32 11z m2232 -16 c16 -9 19 -19 17 -62
-2 -35 4 -70 18 -109 29 -76 39 -192 23 -268 -16 -77 -30 -96 -56 -82 -26 14
-51 130 -35 160 17 32 13 84 -9 117 -28 42 -25 65 14 132 19 32 32 60 30 62
-9 10 -64 -66 -83 -114 -20 -52 -22 -51 -40 20 -17 64 -13 106 11 132 24 25
75 31 110 12z m-2505 -131 c-2 -18 -4 -6 -4 27 0 33 2 48 4 33 2 -15 2 -42 0
-60z m464 55 c-5 -10 -17 -27 -26 -38 -16 -19 -17 -19 -3 5 26 46 29 50 34 50
3 0 0 -8 -5 -17z m-645 -60 c-18 -108 -70 -342 -82 -368 -14 -32 -4 -45 20
-26 7 6 21 9 29 5 15 -5 15 -9 1 -35 -16 -33 -18 -33 -44 -19 -18 10 -19 8
-14 -20 5 -23 0 -40 -20 -70 -14 -22 -26 -49 -26 -60 0 -26 -38 -168 -52 -198
-6 -12 -12 -37 -12 -56 -1 -19 -4 -32 -8 -29 -5 2 -8 -3 -8 -13 0 -9 -19 -71
-41 -137 -36 -103 -40 -123 -29 -140 19 -32 -26 -67 -86 -67 -24 0 -44 -4 -44
-8 0 -4 17 -6 37 -4 34 4 36 3 24 -12 -18 -21 1 -21 34 0 14 9 32 14 40 10 15
-6 13 -14 -21 -64 -2 -2 -26 1 -54 7 -55 12 -96 1 -86 -24 2 -7 -6 -22 -18
-34 l-23 -21 5 32 c3 18 15 45 27 60 16 19 28 59 44 150 21 115 103 400 139
478 8 19 35 63 59 98 24 34 53 84 63 110 25 62 86 263 100 327 27 125 62 221
46 128z m589 -11 c-6 -4 -28 -23 -50 -42 l-40 -35 40 43 c21 23 44 42 49 42 6
0 6 -3 1 -8z m-325 -70 c0 -5 -7 -17 -15 -28 -14 -18 -14 -18 -15 9 0 17 5 27
15 27 8 0 15 -4 15 -8z m200 -36 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4
13 16 21 21 21 13z m-303 -38 c-3 -7 -5 -2 -5 12 0 14 2 19 5 13 2 -7 2 -19 0
-25z m2483 -17 c0 -9 -4 -22 -9 -29 -10 -16 -21 16 -21 58 1 24 1 24 15 6 8
-11 15 -27 15 -35z m-2270 -45 c0 -2 -8 -10 -17 -17 -16 -13 -17 -12 -4 4 13
16 21 21 21 13z m2885 -49 c-22 -18 -68 -56 -103 -85 -35 -30 -85 -63 -110
-74 -26 -12 -71 -36 -100 -54 -29 -19 -55 -34 -58 -34 -3 0 -19 -9 -36 -21
-33 -24 -33 -24 -42 13 -5 22 -1 30 24 48 74 53 429 240 454 240 6 -1 -7 -15
-29 -33z m-3111 -141 c-3 -21 -8 -35 -11 -32 -2 2 -1 20 3 40 3 21 8 35 11 32
2 -2 1 -20 -3 -40z m-73 -47 c-5 -13 -10 -19 -10 -12 -1 15 10 45 15 40 3 -2
0 -15 -5 -28z m56 -21 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z
m8 -124 c-7 -12 -16 -43 -18 -70 -2 -27 -2 -22 -1 11 2 33 6 85 10 115 l7 55
8 -45 c7 -32 5 -50 -6 -66z m2803 44 c-18 -22 -45 -54 -59 -69 -15 -15 -29
-35 -32 -44 -13 -33 -28 -14 -25 32 3 52 24 70 31 26 l4 -28 8 32 c5 24 20 41
54 62 25 16 47 30 49 30 2 1 -12 -18 -30 -41z m-2674 5 c-7 -30 -24 -55 -24
-35 0 12 21 62 27 62 2 0 0 -12 -3 -27z m2359 -45 c7 -24 -8 -48 -29 -48 -15
0 -18 27 -8 54 9 23 31 20 37 -6z m-2556 -10 c-3 -8 -6 -5 -6 6 -1 11 2 17 5
13 3 -3 4 -12 1 -19z m-180 -25 c-3 -10 -5 -4 -5 12 0 17 2 24 5 18 2 -7 2
-21 0 -30z m3223 34 c0 -1 -30 -32 -67 -67 l-68 -65 65 68 c60 62 70 72 70 64z
m-2866 -22 c-4 -8 -8 -15 -10 -15 -2 0 -4 7 -4 15 0 8 4 15 10 15 5 0 7 -7 4
-15z m-74 -23 c0 -15 -2 -15 -10 -2 -13 20 -13 33 0 25 6 -3 10 -14 10 -23z
m-293 -54 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m794 -53 c41
-14 48 -22 78 -82 l32 -66 -46 41 c-25 22 -53 42 -63 44 -26 7 -62 -9 -62 -27
0 -12 3 -13 19 -4 10 7 26 9 36 5 14 -5 15 -9 5 -21 -8 -9 -8 -15 -2 -15 6 0
12 5 14 12 3 8 15 5 40 -10 40 -25 48 -39 48 -86 0 -21 8 -40 21 -52 22 -19
22 -19 12 1 -13 27 -24 97 -14 90 12 -7 82 -127 86 -145 3 -16 78 -172 98
-205 7 -11 25 -39 40 -62 29 -44 21 -61 -9 -20 -16 21 -16 21 -12 1 3 -11 -1
-29 -8 -39 -8 -10 -14 -29 -14 -41 0 -12 -11 -29 -25 -38 -27 -18 -34 -46 -11
-46 8 0 23 -3 33 -6 16 -5 16 -4 -2 12 -16 13 -17 16 -3 11 20 -7 34 9 16 17
-9 3 -8 9 6 20 10 8 19 26 20 40 0 14 7 28 14 31 6 2 12 3 12 2 -1 -19 -44
-134 -64 -172 -34 -63 -53 -84 -26 -28 11 23 20 47 20 53 0 18 -22 11 -27 -10
-6 -24 -16 -25 -34 -4 -11 13 -10 22 8 57 42 81 39 257 -5 310 -36 41 -150 6
-205 -64 l-25 -32 -32 30 -33 31 59 56 c68 65 107 86 159 86 24 0 34 4 29 11
-4 8 -27 9 -70 5 -62 -6 -63 -6 -78 23 -9 16 -21 35 -29 43 -27 28 -47 63 -47
85 0 12 -13 38 -30 56 -34 39 -38 71 -13 99 20 22 27 22 84 3z m2243 -59 c-37
-46 -45 -37 -9 9 15 20 29 34 32 31 3 -2 -8 -20 -23 -40z m-2797 -53 c-3 -10
-5 -4 -5 12 0 17 2 24 5 18 2 -7 2 -21 0 -30z m-50 -220 c-3 -10 -5 -4 -5 12
0 17 2 24 5 18 2 -7 2 -21 0 -30z m10 -75 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13
3 -3 4 -12 1 -19z m802 -74 c11 -14 23 -40 26 -58 6 -33 -17 -162 -32 -177
-12 -12 -153 154 -153 179 0 21 21 42 65 65 43 23 71 21 94 -9z m-1135 -5 c-3
-5 -10 -7 -15 -3 -5 3 -7 10 -3 15 3 5 10 7 15 3 5 -3 7 -10 3 -15z m905 -66
c29 -38 71 -95 95 -128 23 -33 66 -93 95 -133 32 -45 44 -69 32 -63 -10 6 -21
14 -23 18 -2 5 -16 19 -32 33 -23 20 -27 21 -19 6 9 -16 6 -23 -13 -39 -13
-10 -24 -25 -24 -34 0 -12 -3 -13 -14 -4 -7 6 -21 8 -30 5 -16 -6 -16 -8 0
-25 10 -11 15 -22 12 -25 -3 -3 -19 14 -35 38 -16 24 -66 97 -111 162 -45 66
-82 125 -82 131 0 7 14 31 30 53 17 23 30 49 30 57 0 35 39 12 89 -52z m-127
35 c-7 -7 -12 -8 -12 -2 0 6 3 14 7 17 3 4 9 5 12 2 2 -3 -1 -11 -7 -17z
m-852 -91 c0 -14 -76 -124 -116 -167 -12 -14 -39 -44 -59 -67 -38 -44 -55 -47
-55 -12 0 36 30 78 84 119 30 23 59 54 66 71 15 34 4 54 -13 24 -10 -20 -23
-32 -105 -107 -30 -27 -43 -48 -49 -80 -7 -37 -13 -44 -38 -49 -25 -6 -26 -7
-7 -8 14 -1 22 -6 19 -13 -2 -9 -24 -13 -62 -13 -72 0 -83 18 -34 54 19 14 72
62 119 108 125 120 168 151 213 152 24 1 37 -4 37 -12z m843 -75 c12 -25 8
-35 -45 -137 -31 -60 -58 -105 -59 -100 -1 6 -5 -3 -9 -20 -7 -29 -8 -29 -9 9
-1 50 24 144 59 224 31 66 41 70 63 24z m67 -131 c0 -5 23 -43 50 -84 28 -41
50 -81 50 -89 0 -26 -73 -85 -120 -96 -60 -15 -73 1 -67 78 5 64 55 200 74
200 7 0 13 -4 13 -9z m230 -71 c0 -5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5
10 10 10 6 0 10 -4 10 -10z m1771 -133 l11 -87 -39 -48 c-21 -26 -61 -80 -88
-121 -28 -40 -56 -76 -62 -78 -7 -3 -13 -13 -13 -22 -1 -9 -11 -34 -24 -56
-20 -34 -28 -40 -55 -38 -18 0 -34 3 -37 6 -12 11 140 326 235 487 45 77 56
90 58 70 2 -14 8 -64 14 -113z m-3128 108 c0 -6 -7 -11 -16 -13 -12 -3 -15 1
-11 13 7 17 25 17 27 0z m-100 -36 c-12 -12 -103 -21 -103 -10 0 16 15 21 64
21 35 0 47 -3 39 -11z m81 -5 c26 -10 18 -24 -13 -24 -30 0 -46 12 -34 24 8 8
27 8 47 0z m1320 -8 c-3 -8 -1 -17 5 -21 6 -3 11 -11 11 -18 0 -15 -29 25 -30
41 0 6 4 12 10 12 5 0 7 -6 4 -14z m1958 -220 c64 -73 297 -263 372 -303 l61
-32 -40 5 c-22 3 -61 12 -87 20 -25 8 -53 11 -61 8 -28 -10 -361 73 -460 115
l-47 20 47 73 c26 40 60 90 77 111 l30 38 29 -57 c40 -79 163 -198 265 -258
30 -18 7 6 -50 52 -136 109 -200 192 -232 301 -25 84 -18 92 17 24 18 -34 53
-87 79 -117z m11 65 c-2 -6 1 -11 5 -11 5 0 9 -7 9 -17 0 -13 -4 -11 -19 7
-21 26 -24 44 -5 37 6 -2 11 -10 10 -16z m-648 -26 c-7 -16 -27 -61 -45 -99
l-32 -69 -18 41 c-11 23 -30 57 -43 75 -13 17 -32 44 -42 60 l-18 27 106 -2
105 -2 -13 -31z m-445 -40 c-17 -25 -33 -45 -36 -45 -3 0 9 20 26 45 17 25 33
45 36 45 3 0 -9 -20 -26 -45z m-80 -114 c0 -12 -41 -63 -47 -57 -4 4 37 66 44
66 2 0 3 -4 3 -9z m1505 -73 c39 -21 67 -38 62 -38 -15 0 -142 68 -167 90
l-25 22 30 -18 c17 -10 62 -35 100 -56z m-1555 -2 c0 -3 -4 -8 -10 -11 -5 -3
-10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z m-30 -36 c0 -5 -5 -10 -11 -10
-5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m1180 -65 c52 -13 104 -26
115 -29 11 -3 -44 -3 -122 0 -132 4 -250 17 -261 28 -2 3 6 21 17 40 l22 35
67 -25 c37 -14 110 -36 162 -49z m-348 -25 c30 0 30 0 15 -24 -42 -64 -167
-305 -167 -323 0 -6 -4 -14 -9 -18 -11 -6 20 96 79 263 25 68 46 119 48 113 2
-6 17 -11 34 -11z m-689 -33 c39 -4 97 -4 128 0 58 6 134 -2 156 -15 9 -6 4
-33 -21 -107 -18 -55 -45 -146 -61 -202 l-28 -101 -86 -12 c-100 -14 -304 -56
-341 -70 -14 -5 -59 -19 -100 -31 -41 -11 -81 -26 -88 -31 -9 -8 -11 -6 -5 9
17 42 193 335 219 364 15 17 110 37 344 75 102 16 153 34 97 34 -46 0 -239
-29 -322 -49 -44 -11 -81 -18 -83 -16 -5 5 13 38 67 124 34 54 47 68 49 54 3
-17 13 -21 75 -26z m-158 -47 c-9 -16 -18 -30 -21 -30 -2 0 2 14 11 30 9 17
18 30 21 30 2 0 -2 -13 -11 -30z m1248 -327 c-7 -2 -19 -2 -25 0 -7 3 -2 5 12
5 14 0 19 -2 13 -5z m-543 -153 c0 -5 -2 -10 -4 -10 -3 0 -8 5 -11 10 -3 6 -1
10 4 10 6 0 11 -4 11 -10z"/>
<path d="M3420 4420 c-19 -11 -28 -19 -20 -20 9 0 26 7 38 16 12 8 27 12 32 9
6 -3 10 -1 10 4 0 16 -20 13 -60 -9z"/>
<path d="M3184 4304 c-18 -14 -18 -14 6 -3 31 14 36 19 24 19 -6 0 -19 -7 -30
-16z"/>
<path d="M3530 4296 c0 -2 7 -7 16 -10 8 -3 12 -2 9 4 -6 10 -25 14 -25 6z"/>
<path d="M3408 4258 c5 -5 16 -8 23 -6 8 3 3 7 -10 11 -17 4 -21 3 -13 -5z"/>
<path d="M3546 4251 c-4 -7 -5 -15 -2 -18 9 -9 19 4 14 18 -4 11 -6 11 -12 0z"/>
<path d="M3118 4203 c12 -2 32 -2 45 0 12 2 2 4 -23 4 -25 0 -35 -2 -22 -4z"/>
<path d="M3203 4193 c9 -2 25 -2 35 0 9 3 1 5 -18 5 -19 0 -27 -2 -17 -5z"/>
<path d="M3340 4175 c83 -13 168 -19 159 -11 -5 5 -88 15 -174 20 l-70 5 85
-14z"/>
<path d="M3575 4150 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M3178 4053 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M3268 4043 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M3303 4025 c0 -8 4 -15 9 -15 4 0 8 4 8 9 0 6 13 6 33 1 29 -9 30 -8
13 5 -26 20 -63 19 -63 0z"/>
<path d="M2978 3943 c6 -2 18 -2 25 0 6 3 1 5 -13 5 -14 0 -19 -2 -12 -5z"/>
<path d="M3045 3930 c17 -4 44 -8 60 -8 25 0 24 2 -10 9 -51 10 -92 10 -50 -1z"/>
<path d="M3155 3910 c27 -12 43 -12 25 0 -8 5 -22 9 -30 9 -10 0 -8 -3 5 -9z"/>
<path d="M3235 3898 c22 -6 44 -16 48 -22 5 -7 7 -4 5 8 -2 15 -13 20 -48 22
l-45 3 40 -11z"/>
<path d="M3368 3843 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M2638 3703 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M2685 3690 c11 -5 29 -8 40 -8 16 0 15 2 -5 8 -34 11 -60 11 -35 0z"/>
<path d="M1950 3646 c0 -2 7 -7 16 -10 8 -3 12 -2 9 4 -6 10 -25 14 -25 6z"/>
<path d="M2020 3531 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
<path d="M2620 3529 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
-5 -10 -11z"/>
<path d="M3199 3292 c-6 -21 -9 -49 -7 -62 3 -23 6 -20 35 27 31 50 32 53 13
63 -27 14 -28 14 -41 -28z"/>
<path d="M3456 2997 c-49 -49 -23 -147 43 -162 66 -14 111 25 111 98 0 44 -35
77 -88 84 -34 5 -45 1 -66 -20z m100 -12 c4 -8 3 -23 -2 -32 -4 -10 -6 -22 -5
-26 0 -4 -12 -1 -29 8 -16 9 -30 21 -30 28 0 33 55 51 66 22z"/>
<path d="M3690 2850 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
-4 -4 -4 -10z"/>
<path d="M2582 2805 c-23 -7 -53 -20 -65 -28 -29 -19 -58 -24 -74 -14 -7 5
-13 4 -13 0 0 -5 6 -15 13 -22 10 -10 7 -16 -13 -32 -14 -10 -30 -23 -36 -28
-5 -4 -26 -6 -45 -4 -32 4 -33 3 -15 -11 11 -9 28 -16 39 -16 10 0 15 -4 12
-10 -3 -5 -26 -10 -51 -10 -28 0 -43 -4 -39 -10 3 -5 17 -10 31 -10 30 0 31
-15 2 -25 -21 -6 -21 -7 10 -16 40 -12 38 -29 -6 -48 -41 -19 -41 -32 1 -24
29 6 31 5 18 -10 -15 -19 -1 -23 17 -5 6 6 15 9 19 5 3 -4 -3 -16 -15 -27 -30
-28 -28 -42 3 -30 28 11 39 0 30 -30 -6 -18 -3 -19 25 -14 24 5 36 2 55 -16
14 -13 25 -30 25 -37 0 -8 5 -11 12 -7 6 4 17 -1 24 -11 11 -15 16 -16 29 -5
12 10 18 10 30 0 12 -10 18 -8 34 11 l19 24 -40 3 c-50 4 -82 24 -119 76 -54
74 -36 161 49 238 60 53 117 72 195 62 l52 -6 -29 21 c-17 11 -51 25 -78 31
-55 14 -63 30 -15 31 30 1 31 1 7 9 -35 11 -49 11 -98 -5z"/>
<path d="M2684 2617 c-23 -20 -29 -32 -28 -66 1 -22 9 -52 17 -65 34 -52 136
-41 156 16 17 47 14 65 -16 98 -40 43 -90 50 -129 17z m80 -3 c18 -7 22 -54 5
-54 -5 0 -7 -5 -4 -10 4 -6 -5 -5 -19 2 -28 12 -36 52 -13 61 6 3 13 6 14 6 1
1 8 -2 17 -5z"/>
<path d="M3444 2382 c4 -6 2 -30 -4 -52 -12 -43 -3 -79 11 -44 12 30 10 92 -3
100 -7 4 -8 3 -4 -4z"/>
<path d="M2671 2324 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
<path d="M3335 2316 c-10 -14 -21 -23 -26 -20 -15 9 -10 -13 7 -27 8 -7 13
-21 11 -31 -3 -10 -1 -18 4 -18 5 0 9 16 9 35 0 33 2 35 33 33 28 -3 32 0 32
22 0 37 -47 41 -70 6z"/>
<path d="M3050 2273 c0 -21 43 -74 70 -88 44 -23 47 -13 5 16 -19 13 -35 27
-35 31 0 20 122 -28 141 -55 9 -13 32 21 25 38 -9 25 -76 48 -133 47 -29 0
-53 4 -53 9 0 5 -4 9 -10 9 -5 0 -10 -3 -10 -7z"/>
<path d="M3425 2090 c-92 -22 -187 -92 -227 -167 -32 -57 -44 -141 -30 -198
38 -150 100 -234 217 -289 68 -32 84 -36 160 -36 51 0 97 6 117 14 18 8 49 20
68 27 19 7 59 38 90 69 74 74 102 152 101 275 -1 93 -6 108 -57 171 -39 49
-172 119 -253 134 -61 11 -142 11 -186 0z m144 -96 c17 -4 31 -11 31 -15 0 -5
-40 -36 -90 -68 -85 -56 -151 -81 -215 -81 -30 0 -31 13 -6 54 51 82 175 131
280 110z m296 -254 c0 -22 -39 -19 -43 3 -3 15 1 18 20 15 12 -2 23 -10 23
-18z m-85 -26 c0 -11 -23 -50 -53 -87 -50 -64 -23 -52 38 17 31 36 61 63 66
60 31 -18 -29 -109 -109 -167 -67 -48 -123 -65 -218 -68 -64 -2 -80 1 -82 13
-2 11 8 18 35 23 34 7 36 8 16 16 -36 13 -25 39 20 46 10 2 33 14 50 27 18 13
66 40 108 60 42 20 82 43 89 51 12 14 40 21 40 9z"/>
<path d="M4381 2664 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
<path d="M2330 1941 c0 -5 5 -13 10 -16 6 -3 10 -2 10 4 0 5 -4 13 -10 16 -5
3 -10 2 -10 -4z"/>
<path d="M2570 1360 c0 -5 7 -10 16 -10 8 0 12 5 9 10 -3 6 -10 10 -16 10 -5
0 -9 -4 -9 -10z"/>
<path d="M2086 1274 c-4 -14 -5 -28 -3 -31 3 -2 8 8 11 23 4 14 5 28 3 31 -3
2 -8 -8 -11 -23z"/>
<path d="M4590 680 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
-4 -4 -4 -10z"/>
<path d="M4420 1850 c0 -5 5 -10 10 -10 6 0 10 5 10 10 0 6 -4 10 -10 10 -5 0
-10 -4 -10 -10z"/>
<path d="M4472 1585 c0 -16 2 -22 5 -12 2 9 2 23 0 30 -3 6 -5 -1 -5 -18z"/>
<path d="M4473 1455 c0 -22 2 -30 4 -17 2 12 2 30 0 40 -3 9 -5 -1 -4 -23z"/>
<path d="M4481 1370 c0 -8 4 -24 9 -35 5 -13 9 -14 9 -5 0 8 -4 24 -9 35 -5
13 -9 14 -9 5z"/>
<path d="M4550 1185 c0 -5 5 -17 10 -25 5 -8 10 -10 10 -5 0 6 -5 17 -10 25
-5 8 -10 11 -10 5z"/>
<path d="M4570 1131 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
<path d="M4645 1013 c34 -53 45 -67 45 -60 0 10 -51 87 -57 87 -3 0 3 -12 12
-27z"/>
<path d="M4238 543 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M4338 533 c6 -2 18 -2 25 0 6 3 1 5 -13 5 -14 0 -19 -2 -12 -5z"/>
<path d="M2755 370 c39 -26 55 -26 22 0 -14 11 -31 20 -39 20 -7 0 1 -9 17
-20z"/>
<path d="M2738 293 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
<path d="M3517 180 c-3 -11 -1 -20 4 -20 5 0 9 2 9 4 0 2 3 11 6 20 3 9 2 16
-4 16 -5 0 -12 -9 -15 -20z"/>
<path d="M1705 180 c-3 -5 1 -10 9 -10 8 0 16 -6 19 -12 2 -7 3 -5 2 4 -2 9 2
19 9 22 6 2 1 5 -11 5 -12 1 -25 -3 -28 -9z"/>
<path d="M2490 177 c29 -16 124 -31 116 -18 -3 5 -25 11 -48 15 -24 4 -54 9
-68 12 l-25 5 25 -14z"/>
<path d="M2423 163 c9 -2 23 -2 30 0 6 3 -1 5 -18 5 -16 0 -22 -2 -12 -5z"/>
<path d="M1690 139 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
-5 -10 -11z"/>
<path d="M3497 109 c-7 -28 -6 -31 10 -14 6 6 8 18 3 24 -5 9 -9 5 -13 -10z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

46
src/manifest.json Normal file
View File

@ -0,0 +1,46 @@
{
"name": "Train Times",
"short_name": "Train Times",
"icons": [
{
"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",
"background_color": "#ffffff",
"start_url": ".",
"imgdisplay": "standalone",
"display": "standalone"
}

105
src/service-worker.js Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const CACHE_VERSION = 5;
const dataCacheName = 'jubileeData-v1';
const cacheName = 'jubilee-final-1';
const filesToCache = [
'/',
'/index.html',
'/service-worker.js',
'/site.webmanifest',
'/browserconfig.xml',
'/css/mui.custom.css',
'/js/bundle.js',
'/js/vendor.js',
'/img/favicon-16x16.png',
'/img/favicon-32x32.png',
'/img/android-chrome-192.192.png',
'/img/android-chrome-512x512.png'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
/*
* Fixes a corner case in which the app wasn't returning the latest data.
* You can reproduce the corner case by commenting out the line below and
* then doing the following steps: 1) load app for first time so that the
* initial New York City data is shown 2) press the refresh button on the
* app 3) go offline 4) reload the app. You expect to see the newer NYC
* data, but you actually see the initial data. This happens because the
* service worker is not yet activated. The code below essentially lets
* you activate the service worker faster.
*/
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.warn('[Service Worker] Fetch', e.request.url);
const dataUrl = '/getnexttraintimes?';
if (e.request.url.indexOf(dataUrl) > -1) {
console.log('!');
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response) {
cache.put(e.request.url, response.clone());
return response;
});
})
);
}
else
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});

19
src/site.webmanifest Normal file
View File

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

50
src/v1/index.html Normal file
View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Jubilee</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/img/safari-pinned-tab.svg" color="#5bbad5">
<meta name="apple-mobile-web-app-title" content="Train Times">
<meta name="application-name" content="Train Times">
<meta name="theme-color" content="#ffffff">
</head>
<body>
<div class="mui-container">
<div id="greet"></div>
<div id="nearbyShell" class="mui-panel" style="display: none;">
<div class="mui--text-title cardTitle">Around me</div>
<div id="nearby"></div>
</div>
<div id="newsShell" class="mui-panel" style="display: none;">
<div class="mui--text-title cardTitle">Latest news</div>
<div id="news" class="scrolling-wrapper-flexbox"></div>
</div>
<div id="weatherShell" class="mui-panel" style="display: none;">
<div class="mui--text-title cardTitle">Weather</div>
<div id="weather"></div>
</div>
</div>
<script src="js/vendor.js" async></script>
<script type='module' src="js/bundle.js" async></script>
</body>
</html>

95
src/v1/js/Greet.js Normal file
View File

@ -0,0 +1,95 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { partOfDay, toHour } = require('./utils');
const GreetModel = Backbone.Model.extend({
'initialize': function() {
this.set({ 'date': new Date(), 'todaySegment': partOfDay(), 'atHome': false, 'atWork': false, 'locationName':'' });
const delay = toHour();
this.timerID = setTimeout(
() => this.tick(),
delay);
},
'tick': function() {
this.set({ 'date': new Date(), 'todaySegment': partOfDay() });
this.timerID = setTimeout(
() => this.tick(),
toHour()
);
}
});
const GreetView = Backbone.View.extend({
'tagName': 'div',
'initialize': function (options) {
_.bindAll(this, 'render');
this.eventBus = options.eventBus;
this.location = options.location;
this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.$greet = $('#greet');
this.homeIcon = '<span role="img" aria-label="Home">🏠</span>';
this.workIcon = '<span role="img" aria-label="Home">🏢</span>';
// this.initView();
// console.log(this.get('routeView'));
// console.log(this);
this.render();
},
'updateLocation': function(l) {
console.log('>> Location has changed...');
if (l.has('location')) {
const location = l.get('location');
// console.log('location:', location);
console.log('city', location.city);
if (location.hasOwnProperty('atHome'))
this.model.set({ 'atHome':location.atHome, 'atWork':location.atWork, 'locationName':location.city });
}
else
console.log('>> No location yet');
},
'render': function() {
console.log('>> Render greet');
const todaySegment = this.model.get('todaySegment');
const atHome = this.model.get('atHome');
const atWork = this.model.get('atWork');
const locationName = this.model.get('locationName') || '';
console.log('>> model', todaySegment, this.model.get('date'));
let icon = '';
let place = '';
if (atHome) {
icon = this.homeIcon;
place = 'At home';
}
if (atWork) {
icon = this.workIcon;
place = 'At work';
}
if (locationName !== '') {
if (place !== '') place = `${place}, `;
place = place + locationName;
}
const html = `<div class="mui-panel mui--text-center">
<h2>${todaySegment}</h2>
<div>${icon} ${place}</div>
</div>`;
this.$greet.empty().html(html);
}
});
module.exports = { GreetModel, GreetView };

113
src/v1/js/Location.js Normal file
View File

@ -0,0 +1,113 @@
const _ = require('underscore');
const Backbone = require('backbone');
const geolocation = require('geolocation');
const { distance } = require('./utils');
const NodeGeocoder = require('node-geocoder');
const LocationModel = Backbone.Model.extend({
'defaults': {
// defaults go here - "children" may be contained here
},
'initialize': function() {
// this.listenTo(this, 'change sync reset', this.onChange);
const geoOptions = {
'maximumAge': 5 * 60 * 1000,
'timeout': 10 * 1000
};
geolocation.options = geoOptions;
geolocation.on('error', function(err) {
console.warn('Geolocation error');
console.error(err);
});
geolocation.on('change', function( position) {
console.log('Location update');
const location = { 'latitude': position.coords.latitude, 'longitude': position.coords.longitude, 'timestamp': position.timestamp };
this.processPosition(location);
// this.set('location', location);
}.bind(this));
this.watcher = geolocation.createWatcher();
this.listenTo(this, 'change:location', this.onChange);
},
'onChange': function() {
console.log('>> Location updated');
console.log(this.get('location'));
},
'processPosition': function(pos) {
console.log('processPosition');
const { latitude, longitude, timestamp } = pos;
const current = this.get('location');
const options = {
'provider': 'google',
// Optional depending on the providers
'httpAdapter': 'https', // Default
'apiKey': 'AIzaSyA7oGP6QS28tTwtT6UzA7hzh0b3MWwMYB8', // for Mapquest, OpenCage, Google Premier
'formatter': null // 'gpx', 'string', ...
};
const geocoder = NodeGeocoder(options);
const homeDistance = distance(55.942673, -4.556334, latitude, longitude);
const workDistance = distance(55.861939, -4.259338, latitude, longitude);
const atHome = (homeDistance < 0.10);
const atWork = (workDistance < 0.10);
const latlong = { 'lat':latitude, 'lon':longitude };
const ll = `${latitude},${longitude}`;
const llFixed = `${Number.parseFloat(latitude).toFixed(3)},${Number.parseFloat(longitude).toFixed(3)}`;
const newLocation = { homeDistance, workDistance, latitude, longitude, atHome, atWork, timestamp, ll, llFixed, 'city' : '', 'cityCC':'' };
// console.log('>> NewLocation', JSON.stringify(newLocation));
// const distanceFromLast = distance(current.latitude, current.longitude, latitude, longitude);
if (!current /* || distanceFromLast > 1.5*/)
geocoder.reverse(latlong)
.then(function(res) {
newLocation.city = res[0].city;
newLocation.cityCC = `${res[0].city},${res[0].countryCode}`;
this.set('location', newLocation);
}.bind(this))
.catch(function(err) {
console.error(err);
this.set('location', newLocation);
});
else {
newLocation.city = current.city;
const distanceFromLast = distance(current.latitude, current.longitude, latitude, longitude);
if (distanceFromLast > 0.5 && distanceFromLast < 2.0) {
// dont bother re geocoding
console.log('Slightly moved from previous');
this.set('location', newLocation);
}
else if (distanceFromLast >= 2.0 || (timestamp - current.timestamp > 1.8e+6) ) {
console.log('Moved from previous', (timestamp - current.timestamp > 1.8e+6));
geocoder.reverse(latlong)
.then(function(res) {
newLocation.city = res[0].city;
newLocation.cityCC = `${res[0].city},${res[0].countryCode}`;
this.set('location', newLocation);
}.bind(this))
.catch(function(err) {
console.error(err);
this.set('location', newLocation);
});
}
}
}
});
module.exports = { LocationModel };
// https://www.npmjs.com/package/node-geocoder

127
src/v1/js/Nearby.js Normal file
View File

@ -0,0 +1,127 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceNearby } = require('./reducers');
// console.notificationsTitle = 'Nearby';
const fsItem = Backbone.Model.extend({
});
const FSCollection = Backbone.Collection.extend({
'model': fsItem
});
const fsCollection = new FSCollection();
const fsItemView = Backbone.View.extend({
'tagName': 'div',
'className': 'itemRow mui--align-middle',
'template': _.template(`
<span class="mui--text-dark mui--text-subhead"><%= name %></span> <span class="mui--text-caption mui--text-dark-secondary"><%= category %></span> `),
'initialize': function() {
this.render();
},
'attributes': function() {
return {
'data-id': this.model.id
};
},
'render': function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
const NearbyModel = Backbone.Model.extend({
'initialize': function() {
this.fsCollection = fsCollection;
this.listenTo(this, 'change:llFixed', this.onChange);
},
'onChange': function() {
this.getNearby();
},
'getNearby': function() {
const llFixed = this.get('llFixed');
request({
'url': 'http://localhost:3000/fsexplore',
'method': 'GET',
'qs': {
'll': llFixed,
'section': 'topPicks'
}
}, function(err, res, body) {
if (err)
console.error(err);
else {
const fsJSON = JSON.parse(body);
const groups = get(fsJSON, 'response.groups');
const items = groups[0].items;
const newItems = [];
for(const item of items)
newItems.push(reduceNearby(item));
this.fsCollection.reset(newItems);
}
}.bind(this));
}
});
const NearbyView = Backbone.View.extend({
'id':'nearby',
'className': '',
'initialize': function(options) {
this.eventBus = options.eventBus;
this.location = options.location;
this.fsCollection = fsCollection;
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.$nearby = $('#nearby');
/* this.fsCollection.on('all', function(eventName) {
console.log(`${eventName } was triggered!`);
});*/
this.fsCollection.bind('reset', this.render, this);
},
'events': {
'click': 'doClick'
},
'updateLocation': function(l) {
console.log('>> Nearby Location has changed...');
if (l.has('location')) {
const location = l.get('location');
if (location.hasOwnProperty('atHome'))
this.model.set(location);
}
else
console.log('>> Nearby No location yet');
},
'blah': function() {
console.log('nearby render');
},
'render': function() {
console.log('Nearby:Render');
this.$el.empty();
this.fsCollection.each(function(item) {
const fsView = new fsItemView({ 'model': item });
this.$el.append(fsView.el);
// this.$el.append(personView.el); // adding all the person objects.
}, this);
this.$el.parent().show();
},
'doClick': function(d) {
console.log('Do click', d);
}
});
module.exports = { NearbyModel, NearbyView };
55.949443, -4.570721

135
src/v1/js/News.js Normal file
View File

@ -0,0 +1,135 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceEuronews } = require('./reducers');
const NewsItem = Backbone.Model.extend({
});
const NewsCollection = Backbone.Collection.extend({
'model': NewsItem
});
const newsCollection = new NewsCollection();
const newsItemView = Backbone.View.extend({
'tagName': 'div',
'className' : 'scrollCard',
'template': _.template(`
<div class="mui--text-subhead mui--text-accent"><%=title%></div>
<div class="published mui--text-dark-secondary mui--text-caption"><%=pubdate%></div>
<p class="mui--text-body1"><%=description%></p>
`),
'initialize': function() {
this.render();
},
'attributes': function() {
return {
'data-id': this.model.id
};
},
'render': function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
const NewsModel = Backbone.Model.extend({
'defaults' : function (obj) {
// return a new object
return {
'update' : new Date().getTime()
};
}, 'initialize': function() {
this.newsCollection = newsCollection;
this.listenTo(this, 'change:update', this.onChange);
this.getNews();
},
'onChange': function() {
this.getNews();
},
'getNews': function() {
// const ll = this.get('llShort');
request({
'url': 'http://localhost:3000/news',
'method': 'GET'
}, function(err, res, body) {
if (err)
console.error(err);
else {
const fsJSON = JSON.parse(body);
// console.log(body);
const newItems = [];
const items = fsJSON.items.slice(0, 10);
for (const item of items)
newItems.push(reduceEuronews(item));
this.newsCollection.reset(newItems);
this.logUpdate();
}
}.bind(this));
}, 'logUpdate': function() {
console.log('News logging:');
const time = new Date().getTime() ;
this.set('time', time);
this.timerID = setTimeout(
() => this.tick(),
3.6e+6 + 1000
);
// console.log(this);
},
'tick': function() {
console.log('Set update');
this.set('update', new Date().getTime());
}
});
const NewsView = Backbone.View.extend({
'initialize': function(options) {
this.eventBus = options.eventBus;
this.newsCollection = newsCollection;
this.newsCollection.on('all', function(eventName) {
console.log(`${eventName } was triggered!`);
});
this.newsCollection.bind('reset', this.render, this);
},
'attributes': function() {
return {
'class': 'horizontal-scroll-wrapper squares'
};
},
'events': {
'click': 'doClick'
},
'render': function() {
console.log('News:Render');
this.$el.empty();
this.newsCollection.each(function(item) {
const niView = new newsItemView({ 'model': item });
this.$el.append(niView.el);
}, this);
this.$el.parent().show();
// console.log(this.$el.parent());
}, 'doClick': function(d) {
console.log('Do click', d);
}
});
module.exports = { NewsModel, NewsView };

191
src/v1/js/Weather.js Normal file
View File

@ -0,0 +1,191 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceOpenWeather } = require('./reducers');
const { distance } = require('./utils');
const weatherItem = Backbone.Model.extend({
});
const WeatherCollection = Backbone.Collection.extend({
'model': weatherItem
});
const weatherCollection = new WeatherCollection();
const weatherItemView = Backbone.View.extend({
'tagName': 'div',
'template': _.template(`
<div>
<div class="mui-col-md-4 mui-col-xs-4">
<div class="mui--text-display1 mui--text-center temp<%=temp %>"><%= temp %>°</div>
</div>
<div class="mui-col-md-4 mui-col-xs-4 mui--text-center">
<i class="mui--text-display1 wi <%= icon %>"></i>
</div>
<div class="mui-col-md-4 mui-col-xs-4">
<div>
<div class="mui--text-subhead summary"><%= summary %></div>
<span class="mui--text-body2 temp<%=~~(tempHigh) %>"><%= tempHigh %>°</span> /
<span class="mui--text-body1 temp<%=~~(tempLow) %>"><%= tempLow %>°</span></div>
</div>
</div>
<div class="mui--text-dark-secondary mui--text-caption mui--text-right">
For <%=city%> to <%= readdate %>
</div>
`),
'initialize': function() {
this.render();
},
'attributes': function() {
return {
'data-id': this.model.id
};
},
'render': function() {
this.$el.html(this.template(this.model.toJSON()));
}
});
const WeatherModel = Backbone.Model.extend({
'defaults' : function (obj) {
// return a new object
return {
'update' : new Date().getTime()
};
},
'initialize': function() {
this.weatherCollection = weatherCollection;
this.run = false;
this.listenTo(this, 'change:ll', this.onChange);
this.listenTo(this, 'change:update', this.onChange);
},
'onChange': function() {
console.log('Weather LL has changed');
// if distance change > 10km
// if its been an hour since last update
if (!this.has('log')) {
console.info('First run');
this.getWeather();
}
else {
const log = this.get('log');
const timeDiff = new Date().getTime() - log.time;
const dist = distance(log.lat, log.long, this.get('latitude'), this.get('longitude'));
console.log('Weather distance:', dist);
if ((dist > 5.0) && (timeDiff > 1.8e+6))
this.getWeather();
else if (timeDiff > 3.6e+6) {
console.log('Weather hourly update');
this.getWeather();
}
}
},
'getWeather': function() {
// const ll = this.get('llShort');
const llFixed = this.get('llFixed');
request({
'url': 'http://localhost:3000/weather',
'method': 'GET',
'qs': {
'll': llFixed
}
}, function(err, res, body) {
if (err)
console.error(err);
else {
// console.log(body);
const fsJSON = JSON.parse(body);
const city = get(fsJSON, 'city.name', '');
const list = get(fsJSON, 'list', []);
const newItems = [];
for(const item of list)
newItems.push(reduceOpenWeather(item, city));
this.weatherCollection.reset(newItems);
this.logUpdate();
}
}.bind(this));
},
'logUpdate': function() {
console.log('Weather logging:');
const log = { 'lat' : this.get('latitude'), 'long': this.get('longitude'), 'time': new Date().getTime() };
this.set('log', log);
this.timerID = setTimeout(
() => this.tick(),
3.6e+6 + 1000
);
// console.log(this);
},
'tick': function() {
console.log('Set update');
this.set('update', new Date().getTime());
}
});
const WeatherView = Backbone.View.extend({
'className': 'mui--align-middle',
'initialize': function(options) {
this.eventBus = options.eventBus;
this.location = options.location;
this.wCollection = weatherCollection;
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
/* this.wCollection.on('all', function(eventName) {
console.log(`${eventName } was triggered!`);
});*/
this.wCollection.bind('reset', this.render, this);
},
'attributes': function() {
return {
'class': 'mui--align-middle'
};
},
'events': {
'click': 'doClick'
},
'updateLocation': function(l) {
console.log('>> Weather Location has changed...');
if (l.has('location')) {
const location = l.get('location');
if (location.hasOwnProperty('atHome'))
this.model.set(location);
}
else
console.log('>> Weather No location yet');
},
'render': function() {
console.log('Weather:Render');
this.$el.empty();
const item = this.wCollection.first();
const wView = new weatherItemView({ 'model': item });
this.$el.append(wView.el);
this.$el.parent().show();
// console.log(wView.el);
}
});
module.exports = { WeatherModel, WeatherView };

36
src/v1/js/app.js Normal file
View File

@ -0,0 +1,36 @@
require('muicss');
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { LocationModel } = require('./Location');
const { GreetModel, GreetView } = require('./Greet');
const { NearbyModel, NearbyView } = require('./Nearby');
const { WeatherModel, WeatherView } = require('./Weather');
const { NewsModel, NewsView } = require('./News');
var app = app || {};
(function () {
if ('serviceWorker' in navigator)
navigator.serviceWorker
.register('./service-worker.js')
.then(function() {
console.log('Service Worker Registered');
});
const offline = false;
app.eventBus = _.extend({}, Backbone.Events);
app.locationModel = new LocationModel();
// new TrainView({ 'model': new TrainModel(route), 'eventBus': app.eventBus });
app.greetView = new GreetView({ 'model': new GreetModel(), 'eventBus': app.eventBus, 'location': app.locationModel });
app.nearby = new NearbyView({ 'model': new NearbyModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#nearby' });
app.weather = new WeatherView({ 'model': new WeatherModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#weather' });
app.news = new NewsView({ 'model': new NewsModel(), 'eventBus': app.eventBus, 'el':'#news' });
})();

91
src/v1/js/reducers.js Normal file
View File

@ -0,0 +1,91 @@
const fecha = require('fecha');
const { get } = require('lodash');
function reduceOpenWeather(item, city) {
// Openweather returns timestamps in seconds. Moment requires them in milliseconds.
// Replaced Moment with Fecha.
// Too many issues trying to get Moment packaged into the bundle.
if (!item || item === null) return {};
const fts = new Date(item.dt * 1000);
const weatherBlock = item.weather[0];
return {
'timestamp': item.dt,
'icon': `wi-owm-${weatherBlock.id}`,
'summary': weatherBlock.description,
'temp': parseInt(item.main.temp, 10),
'tempHigh': parseFloat(item.main.temp_max, 10),
'tempLow': parseFloat(item.main.temp_min, 10),
'datelong': fecha.format(fts, 'YYYY-MM-DDTHH:mm:ss.SSSZZ'),
'readdate': fecha.format(fts, 'default'),
'time': item.dt,
'date': fecha.format(fts, 'D/M'),
'day': fecha.format(fts, 'ddd'),
'city': city
};
}
function reduceOpenWeather5DayForecast(item, city) {
// Openweather returns timestamps in seconds. Moment requires them in milliseconds.
// Replaced Moment with Fecha.
// Too many issues trying to get Moment packaged into the bundle.
if (!item || item === null) return {};
const fts = new Date(item.dt * 1000);
const weatherBlock = item.weather[0];
return {
'timestamp': item.dt,
'icon': `wi-owm-${weatherBlock.id}`,
'summary': weatherBlock.description,
'tempHigh': parseInt(item.temp.max, 10),
'tempLow': parseInt(item.temp.min, 10),
'datelong': fecha.format(fts, 'YYYY-MM-DDTHH:mm:ss.SSSZZ'),
'time': item.dt,
'date': fecha.format(fts, 'D/M'),
'day': fecha.format(fts, 'ddd'),
'city': city
};
}
const reduceNearby = function(item) {
const obj = {};
const categories = get(item, 'venue.categories');
obj.id = get(item, 'venue.id', '');
obj.name = get(item, 'venue.name', '');
obj.category = get(categories[0], 'name', '');
obj.icon = `${get(categories[0], 'icon.prefix', '')}${get(categories[0], 'icon.suffix', '')}`;
return obj;
};
const reduceEuronews = function(item) {
const obj = {};
const pubdateSrc = fecha.parse(item.pubdate, 'ddd, DD MMM YYYY HH:mm:SS ZZ');
obj.pubdate = fecha.format(pubdateSrc, 'dddd MMMM Do, YYYY HH:mm');
obj.description = item.description.replace(/(<script(\s|\S)*?<\/script>)|(<style(\s|\S)*?<\/style>)|(<!--(\s|\S)*?-->)|(<\/?(\s|\S)*?>)/g, '');
obj.guid = item.guid.text;
obj.title = item.title;
return obj;
};
/*
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>`;
*/
module.exports = { reduceOpenWeather, reduceNearby, reduceEuronews };

62
src/v1/js/utils.js Normal file
View File

@ -0,0 +1,62 @@
function partOfDay(timeString, today) {
console.log(new Date());
if (timeString === undefined || timeString === null)
timeString = (new Date()).getHours().toString();
if (today === undefined)
today = false;
const hours = timeString.substring(0, 2);
let dayBit = '';
console.log('Hours', hours);
if (hours >= 0 && hours < 4)
dayBit = 'Late Night';
else if (hours >= 4 && hours < 7)
dayBit = 'Early Morning';
else if (hours >= 7 && hours < 12)
dayBit = 'Morning';
else if (hours >= 12 && hours < 17)
dayBit = 'Afternoon';
else if (hours < 21)
dayBit = 'Evening';
else
dayBit = 'Night';
if (today)
if (dayBit === 'night') {
dayBit = 'tonight';
}
else {
dayBit = `this ${ dayBit}`;
}
console.log('partOfDay', dayBit);
return dayBit;
}
function toHour() {
const now = new Date();
return 3600000 - (now.getTime() % 3600000);
}
function distance(lat1, lon1, lat2, lon2) {
const p = 0.017453292519943295; // Math.PI / 180
const c = Math.cos;
const a = 0.5 - c((lat2 - lat1) * p) / 2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p)) / 2;
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}
module.exports = { partOfDay, toHour, distance };