Added news articles and server processing

This commit is contained in:
Martin Donnelly 2018-03-05 19:14:37 +00:00
parent 9f13a3e391
commit 855138c767
22 changed files with 666 additions and 106 deletions

129
package-lock.json generated
View File

@ -77,6 +77,11 @@
}
}
},
"@types/node": {
"version": "9.4.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz",
"integrity": "sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ=="
},
"JSONStream": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz",
@ -801,6 +806,11 @@
}
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"boom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
@ -1220,6 +1230,19 @@
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
},
"cheerio": {
"version": "1.0.0-rc.2",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz",
"integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=",
"requires": {
"css-select": "1.2.0",
"dom-serializer": "0.1.0",
"entities": "1.1.1",
"htmlparser2": "3.9.2",
"lodash": "4.17.5",
"parse5": "3.0.3"
}
},
"chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@ -1768,6 +1791,22 @@
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"requires": {
"boolbase": "1.0.0",
"css-what": "2.1.0",
"domutils": "1.5.1",
"nth-check": "1.0.1"
}
},
"css-what": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
"integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0="
},
"cssnano": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
@ -2131,11 +2170,49 @@
"esutils": "2.0.2"
}
},
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
"integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
"requires": {
"domelementtype": "1.1.3",
"entities": "1.1.1"
},
"dependencies": {
"domelementtype": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
}
}
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
},
"domelementtype": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
"integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
},
"domhandler": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz",
"integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=",
"requires": {
"domelementtype": "1.3.0"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0.1.0",
"domelementtype": "1.3.0"
}
},
"double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
@ -2221,6 +2298,11 @@
}
}
},
"entities": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
},
"error": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
@ -5376,6 +5458,19 @@
"resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
"integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
},
"htmlparser2": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
"integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
"requires": {
"domelementtype": "1.3.0",
"domhandler": "2.4.1",
"domutils": "1.5.1",
"entities": "1.1.1",
"inherits": "2.0.3",
"readable-stream": "2.3.4"
}
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
@ -6870,6 +6965,11 @@
"timers-ext": "0.1.2"
}
},
"memory-cache": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo="
},
"meow": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
@ -7634,6 +7734,14 @@
"set-blocking": "2.0.0"
}
},
"nth-check": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
"integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
"requires": {
"boolbase": "1.0.0"
}
},
"num2fraction": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@ -8029,6 +8137,14 @@
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
"dev": true
},
"parse5": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"requires": {
"@types/node": "9.4.6"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
@ -9279,6 +9395,13 @@
"requires": {
"object-assign": "4.1.1",
"strict-uri-encode": "1.1.0"
},
"dependencies": {
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
}
}
},
"querystring": {
@ -10567,9 +10690,9 @@
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
},
"string-template": {
"version": "1.0.0",

View File

@ -12,6 +12,7 @@
"apicache": "^1.2.0",
"backbone": "^1.3.3",
"browserify": "^16.1.0",
"cheerio": "^1.0.0-rc.2",
"debug-logger": "^0.4.1",
"eslint": "^4.18.0",
"express": "^4.16.2",
@ -31,10 +32,12 @@
"lodash": "^4.17.5",
"log4js": "^2.5.3",
"loggy": "^1.0.2",
"memory-cache": "^0.2.0",
"muicss": "^0.9.36",
"node-foursquare-venues": "^1.1.0",
"openweather-apis": "^3.3.5",
"request-json": "^0.6.3",
"strict-uri-encode": "^2.0.0",
"twitter": "^1.7.1",
"uglifyify": "^4.0.5",
"underscore": "^1.8.3",

View File

@ -19,7 +19,7 @@ const cache = apicache.middleware;
app.use(express.static(path.join(__dirname, sitePath)));
app.get('/weather', cache('45 minutes'), (req, res) => {
app.get('/weather', cache('15 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
weather.doGetOpenWeather(req.query.ll)
@ -37,7 +37,7 @@ app.get('/weather', cache('45 minutes'), (req, res) => {
}
});
app.get('/weatheralert', cache('45 minutes'), (req, res) => {
app.get('/weatheralert', cache('15 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
// weather.doGetOpenWeather(req.query.ll)
// doGetDarkSkyWeather
@ -56,7 +56,7 @@ app.get('/weatheralert', cache('45 minutes'), (req, res) => {
}
});
app.get('/fsexplore', cache('30 minutes'), (req, res) => {
app.get('/fsexplore', cache('15 minutes'), (req, res) => {
if (req.query.hasOwnProperty('ll'))
foursquare.doGetFourSquareExplore(req.query.ll)
.then((d) => {
@ -90,15 +90,40 @@ app.get('/rightbyme', cache('12 hour'), (req, res) => {
}
});
app.get('/news', cache('30 minutes'), (req, res) => {
app.get('/news', cache('15 minutes'), (req, res) => {
euronews.getEuroNews().then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
if (e.message === '304:Not changed') {
logger.info('Euronews ', e.message);
res.status(304).send();
}
else {
logger.error(e);
res.status(500).send('There was an error!');
}
});
});
app.get('/article', (req, res) => {
if (req.query.hasOwnProperty('guid')) {
logger.debug('Beofre', req.query.guid);
euronews.getArticle(req.query.guid)
.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: GUID missing');
res.status(500).send('GUID Missing');
}
});
app.listen(port, (err) => {
if (err)
return logger.error('Server error:', err);

View File

@ -1,15 +1,24 @@
const FeedMe = require('feedme');
const fecha = require('fecha');
const request = require('request');
const http = require('http');
const cheerio = require('cheerio');
const { reduceArticle } = require('./reducers/euronews');
const logger = require('log4js').getLogger('euronews');
logger.level = 'debug';
module.exports = {
'getEuroNews': doGetEuroNews,
'render': render
'render': render,
'getArticle' : doGetArticle
};
const headers = {};
const lastGood = {};
class Template {
constructor(item) {
// "pubdate": "Tue, 06 Feb 2018 17:05:00 +0100",
@ -34,16 +43,28 @@ function doGetEuroNews() {
return new Promise((resolve, reject) => {
logger.info('Retrieving Euronews Headlines..');
http.get('http://feeds.feedburner.com/euronews/en/news/', (res) => {
const options = {
'hostname': 'feeds.feedburner.com',
'path': '/euronews/en/news/',
'method': 'GET',
'headers': headers
};
// http.get('http://feeds.feedburner.com/euronews/en/news/', (res) => {
http.get(options, (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
const reqLastModified = res.headers['last-modified'];
logger.debug(res.headers);
let error;
if (statusCode !== 200)
logger.debug('contentType', contentType);
if (statusCode !== 200 && statusCode !== 304)
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
else if (!/^text\/xml/.test(contentType))
else if (!/^text\/xml/.test(contentType) && statusCode === 200)
error = new Error('Invalid content-type.\n' +
`Expected text/xml but received ${contentType}`);
@ -55,11 +76,51 @@ function doGetEuroNews() {
return reject(error);
}
const parser = new FeedMe(true);
res.pipe(parser);
parser.on('end', () => {
return resolve(parser.done());
});
if ( statusCode === 200) {
headers['If-Modified-Since'] = reqLastModified;
const parser = new FeedMe(true);
res.pipe(parser);
parser.on('end', () => {
lastGood.page = parser.done();
logger.info('Euronews page cached');
// return resolve(parser.done());
return resolve(lastGood.page);
});
}
else if (statusCode === 304) {
logger.info('Euronews not changed');
return resolve(lastGood.page);
}
});
});
}
function doGetArticle(guid = '') {
const splitURL = /([--:\w?@%&+~#=]*\.[a-z]{2,4}\/{0,2})((?:[?&](?:\w+)=(?:\w+))+|[--:\w?@%&+~#=]+)?/g;
const url = splitURL.exec(decodeURI(guid));
logger.debug('Converting:', guid);
// logger.debug('Split', url);
const ampURL = `${url[1]}amp/${url[2]}`;
return new Promise(function(resolve, reject) {
request(ampURL, function(err, resp, body) {
if (err)
// Logger.error(err);
return reject(err);
// Throw err;
const output = reduceArticle(body);
return resolve(output);
}, function(error, response, body) {
if (response.statusCode !== 200) {
logger.error(response.statusCode);
logger.error(body);
return reject(error);
}
});
});
}

View File

@ -0,0 +1,37 @@
const cheerio = require('cheerio');
const logger = require('log4js').getLogger('Euronews Reducer');
const { get, isEmpty } = require('lodash');
logger.level = 'debug';
const htmlTidy = /<(\/*?)(?!(em|p|br\s*\/|strong|h1|h2|h3))\w+?.+?>/gim;
function reduceArticle(body = '') {
if (body === '') return {};
const obj = {};
const $ = cheerio.load(body);
const title = $('meta[property="og:title"]').attr('content');
const image = `http://image.silvrtree.co.uk/640,fit,q80/${ $('meta[property="og:image"]').attr('content')}`;
const stuff = $('[itemprop="articleBody"]');
const html = [];
// stuff.children().each(function () {
stuff.each(function () {
html.push($(this).html());
});
const outputHTML = html.join('').replace(htmlTidy, '');
obj.title = title;
obj.image = image;
obj.html = outputHTML;
return obj;
}
module.exports = { reduceArticle };

View File

@ -43,7 +43,6 @@ function doGetOpenWeatherForecast(ll) {
function doGetDarkSkyWeather(ll) {
const query = `${ll}?units=uk2&exclude=daily,flags,minutely,hourly`;
logger.debug(query);
return new Promise((resolve, reject) => {
DSclient.get(query, function(err, res, body) {
if (err || !body || !body.currently)

View File

@ -2,7 +2,7 @@
@import "./node_modules/muicss/lib/sass/mui/colors";
// customize MUI variables
$mui-primary-color: mui-color('blue-grey', '500');
$mui-primary-color: mui-color('yellow', '500');
$mui-primary-color-dark: mui-color('blue-grey', '700');
$mui-primary-color-light: mui-color('blue-grey', '100');
@ -12,6 +12,9 @@ $mui-accent-color-light: mui-color('indigo', 'A400');
$mui-base-font-family: 'Roboto', "Helvetica Neue", Helvetica, Arial, Verdana, "Trebuchet MS";
$mui-appbar-font-color: mui-color('black') !default;
// import MUI SASS
@import "./node_modules/muicss/lib/sass/mui";
@import "./src/css/horscroll";
@ -308,3 +311,60 @@ li {
max-height: 100%;
max-width: 100%;
}
.stop-scrolling {
height: 100%;
overflow: hidden;
}
.fullscreen {
position: absolute;
z-index: 5000;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background-color: mui-color('grey','300');
background-repeat: no-repeat;
background-position: center top;
}
.fillpanel {
width:100%;
height:100%;
background-color: mui-color('amber', '50');
}
.fullscreen .header {
/*position: fixed;
top: 0;
right: 0;
left: 0;
z-index: +1;
transition: left 0.2s;*/
position:sticky;
}
.box {
display: flex;
flex-flow: column;
height:100%;
// overflow:auto;
}
.box .headerSpacer {
flex: 0 1 66px;
}
.box .content {
flex: 1 1 auto;
background-color: mui-color('white');
overflow: auto;
margin-bottom: 15px;
}
.newsarticle img {
max-height:100%;
max-width:100%;
}

View File

@ -7,6 +7,7 @@
<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="//fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="/css/mui.custom.css" rel="stylesheet" type="text/css"/>
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">

View File

@ -3,8 +3,8 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceNearby } = require('./reducers');
const { partOfDay } = require('./utils');
const { reduceNearby } = require('./libs/reducers');
const { partOfDay } = require('./libs/utils');
const FSDetailView = Backbone.View.extend({
'initialize': function(options) {

View File

@ -1,7 +1,7 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const { partOfDay, toHour } = require('./utils');
const { partOfDay, toHour } = require('./libs/utils');
const GreetModel = Backbone.Model.extend({
'initialize': function() {
@ -46,8 +46,8 @@ const GreetView = Backbone.View.extend({
console.log('>> Location has changed...');
console.log(JSON.stringify(l.changed));
if (l.has('location')) {
const location = l.get('location');
if (l.has('atHome')) {
const location = l.toJSON();
// console.log('location:', location);
console.log('city', location.city);
if (location.hasOwnProperty('atHome'))

View File

@ -3,7 +3,7 @@ const Backbone = require('backbone');
const geolocation = require('geolocation');
const TimeFormat = require('hh-mm-ss');
const { distance } = require('./utils');
const { distance } = require('./libs/utils');
const NodeGeocoder = require('node-geocoder');
@ -56,7 +56,7 @@ const LocationModel = Backbone.Model.extend({
// this.set('location', location);
}.bind(this));
this.watcher = geolocation.createWatcher();
this.watcher = geolocation.createWatcher();
this.listenTo(this, 'change:location', this.onChange);
// this.tick();
@ -69,8 +69,9 @@ const LocationModel = Backbone.Model.extend({
console.log('processPosition');
const { latitude, longitude, timestamp } = pos;
const current = this.get('location');
// const current = this.get('location');
const current = this.toJSON();
// console.log('># current', current);
const options = {
'provider': 'google',
@ -90,14 +91,15 @@ const LocationModel = Backbone.Model.extend({
const ll = `${latitude},${longitude}`;
const llFixed = `${Number.parseFloat(latitude).toFixed(3)},${Number.parseFloat(longitude).toFixed(3)}`;
const llSix = `${Number.parseFloat(latitude).toFixed(6)},${Number.parseFloat(longitude).toFixed(6)}`;
const llShort = `${Number.parseFloat(latitude).toFixed(1)},${Number.parseFloat(longitude).toFixed(1)}`;
const moving = true;
const newLocation = { homeDistance, workDistance, latitude, longitude, atHome, atWork, atHomeOrWork, timestamp, ll, llFixed, llSix, moving, 'city' : '', 'cityCC':'' };
const newLocation = { homeDistance, workDistance, latitude, longitude, atHome, atWork, atHomeOrWork, timestamp, ll, llFixed, llSix, llShort, moving, 'city' : '', 'cityCC':'' };
// console.log('>> NewLocation', JSON.stringify(newLocation));
// const distanceFromLast = distance(current.latitude, current.longitude, latitude, longitude);
if (!current /* || distanceFromLast > 1.5*/) {
if (!_.has(current, 'atHomeOrWork')) {
console.info('>> Location:geocoder request');
geocoder.reverse(latlong)
.then(function(res) {
@ -105,9 +107,12 @@ const LocationModel = Backbone.Model.extend({
newLocation.city = res[0].city;
newLocation.cityCC = `${res[0].city},${res[0].countryCode}`;
newLocation.address = res[0].formattedAddress;
this.set('location', newLocation);
this.set('moving', moving);
this.set('lastGeocode', { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp });
// this.set('location', newLocation);
newLocation.lastGeocode = { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp };
this.set( newLocation);
// this.set('moving', moving);
// this.set('lastGeocode', { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp });
console.log('### location', this);
}.bind(this))
.catch(function(err) {
console.error(err);
@ -124,14 +129,15 @@ const LocationModel = Backbone.Model.extend({
console.log('>> distance from last record', distanceFromLast);
// console.log('>> distanceFromLastGeocode', distanceFromLastGeocode, TimeFormat.fromMs(timestamp - lastGeocode.timestamp, 'hh:mm:ss'));
console.log('>> distanceFromLastGeocode', distanceFromLastGeocode, TimeFormat.fromMs(currentTime - lastGeocode.timestamp, 'hh:mm:ss'));
console.log(`(currentTime:${currentTime}, timestamp:${timestamp}, lastGeocode.timestamp:${lastGeocode.timestamp})`);
console.log('(currentTime - current.timestamp > 900000) ', (currentTime - current.timestamp > 900000));
// console.log(`(currentTime:${currentTime}, timestamp:${timestamp}, lastGeocode.timestamp:${lastGeocode.timestamp})`);
// console.log('(currentTime - current.timestamp > 900000) ', (currentTime - current.timestamp > 900000));
// if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || (timestamp - current.timestamp > 900000)) {
if ((distanceFromLast > 0.5 && distanceFromLast < 2.0) || ((currentTime - current.timestamp > 900000) && (currentTime - lastGeocode.timestamp < 1.8e+6))) {
// dont bother re geocoding
console.log('Slightly moved from previous');
this.set('location', newLocation);
this.set('moving', moving);
// this.set('location', newLocation);
// this.set('moving', moving);
this.set( newLocation);
}
else if (distanceFromLastGeocode >= 2.0 || (currentTime - lastGeocode.timestamp >= 1.8e+6) ) {
console.log('Moved from previous', (currentTime - lastGeocode.timestamp >= 1.8e+6));
@ -142,15 +148,16 @@ const LocationModel = Backbone.Model.extend({
newLocation.cityCC = `${res[0].city},${res[0].countryCode}`;
newLocation.address = res[0].formattedAddress;
console.log('!!! Setting location...');
this.set('location', newLocation);
this.set('lastGeocode', { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp });
this.set('moving', moving);
// this.set('location', newLocation);
// this.set('lastGeocode', { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp });
// this.set('moving', moving);
newLocation.lastGeocode = { 'lat':latitude, 'lng':longitude, 'timestamp':timestamp };
this.set( newLocation);
console.log(this);
}.bind(this))
.catch(function(err) {
console.error(err);
this.set('location', newLocation);
this.set( newLocation);
});
}
}

View File

@ -3,8 +3,8 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceNearby } = require('./reducers');
const { toHour } = require('./utils');
const { reduceNearby } = require('./libs/reducers');
const { toHour } = require('./libs/utils');
const { FSDetailView } = require('./Foursquare');
@ -102,14 +102,10 @@ const NearbyView = Backbone.View.extend({
this.fsCollection = fsCollection;
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.location.bind('change:llFixed', 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': {
@ -118,10 +114,9 @@ const NearbyView = Backbone.View.extend({
'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);
if (l.has('atHome')) {
const llFixed = l.get('llFixed');
this.model.set('llFixed', llFixed);
}
else
console.log('>> Nearby No location yet');

View File

@ -3,7 +3,7 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceEuronews } = require('./reducers');
const { reduceEuronews } = require('./libs/reducers');
const NewsItem = Backbone.Model.extend({
@ -31,7 +31,7 @@ const newsItemView = Backbone.View.extend({
},
'attributes': function() {
return {
'data-id': this.model.id
'data-guid': this.model.get('guid')
};
},
@ -99,6 +99,7 @@ const NewsModel = Backbone.Model.extend({
});
const NewsView = Backbone.View.extend({
'className': '',
'initialize': function(options) {
this.eventBus = options.eventBus;
this.newsCollection = newsCollection;
@ -128,9 +129,12 @@ const NewsView = Backbone.View.extend({
this.$el.parent().show();
// console.log(this.$el.parent());
}, 'events': {
'click': 'doClick'
'click .scrollCard': 'doClick'
}, 'doClick': function(d) {
console.log('Do click', d);
const id = get(d, 'currentTarget.dataset.guid', '');
console.log(id);
this.eventBus.trigger('showNews', id);
}
});

96
src/v1/js/NewsViewer.js Normal file
View File

@ -0,0 +1,96 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get, isEmpty } = require('lodash');
const { createPanel, addPanel } = require('./libs/panel');
const NewsCardModel = Backbone.Model.extend({
'initialize': function() {
this.listenTo(this, 'change:guid', this.newGuid);
},
'newGuid': function() {
console.log('>> GUID changed:', this.get('guid'));
this.getNews();
},
'getNews': function() {
const guid = this.get('guid');
request({
'url': `${window.loc}/article`,
'method': 'GET',
'qs': {
'guid': guid
}
}, function(err, res, body) {
if (err)
console.error(err);
else{
// console.log(body);
const fsJSON = JSON.parse(body);
// console.log(fsJSON);
this.set('article', fsJSON);
console.log(body);
}
}.bind(this));
}
});
const NewsCardView = Backbone.View.extend({
'initialize': function(options) {
this.eventBus = options.eventBus;
/* this.model.on('all', function(eventName) {
console.log(`${eventName } was triggered!`);
});*/
this.model.bind('change:article', this.doRender, this);
this.eventBus.on('showNews', this.showNewsPanel, this);
},
'template': _.template(`
<div class="newsarticle">
<div><img src="<%= image %>"></div>
<div class="mui-container">
<div><h1><%= title %></h1></div>
<div><%= html %></div>
</div>
</div>
`),
'showNewsPanel': function(guid) {
console.log('Showing news', guid);
const prevGuid = this.model.get('guid');
this.model.set('guid', guid);
this.$newPanel = createPanel({ 'title':'News', 'divId':'newsP' });
this.$el = addPanel(this.$newPanel);
this.$el.empty();
this.$newPanel.show();
if (prevGuid === guid)
this.doRender();
},
'events': {
'click .closebutton': 'doClick'
},
'doClick': function(d) {
console.log('Do click', d);
const id = get(d, 'currentTarget', '');
console.log(id);
// this.eventBus.trigger('showNews', id);
},
'doClose': function(d) {
console.log('close??');
},
'doRender': function() {
this.$el.html(this.template(this.model.get('article')));
}
});
module.exports = { NewsCardModel, NewsCardView };

View File

@ -3,7 +3,7 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get, isEmpty } = require('lodash');
const { maybePluralize } = require('./utils');
const { maybePluralize } = require('./libs/utils');
const ByMeModel = Backbone.Model.extend({
'initialize': function() {
@ -17,25 +17,37 @@ const ByMeModel = Backbone.Model.extend({
},
'onChange': function() {
// this.getByMe(); trigger when not moved for 30 seconds
if (this.get('atHomeOrWork') !== true)
this.timerID = setTimeout(
() => this.getByMe(),
30000
);
console.log('>> changedAttributes:', this.changedAttributes());
if (this.get('atHomeOrWork') !== true) {
if ( this.timerID === 0) {
console.log('setting timeout....');
clearInterval(this.timerID);
this.timerID = setTimeout(
() => this.getByMe(),
30000
);
}
}
else {
clearInterval(this.timerID);
console.log('>> ByMe: atHomeOrWork');
}
},
'getByMe': function() {
const llSix = this.get('llFixed');
const ll = this.get('ll');
console.info('>> ByMe:request');
clearInterval(this.timerID);
this.timerID = 0;
console.log(this.toJSON());
// const section = (partOfDay >= 11 && partOfDay <= 14) ? 'food' : 'topPicks';
request({
'url': `${window.loc}/rightbyme`,
'method': 'GET',
'qs': {
'll': llSix
'll': ll
}
}, function(err, res, body) {
if (err)
@ -109,7 +121,7 @@ const ByMeView = Backbone.View.extend({
this.location = options.location;
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.location.bind('change:llSix', this.updateLocation, this);
this.location.bind('change:atHomeOrWork', this.atHomeOrWork, this);
this.model.bind('change:byme', this.render, this);
},
@ -117,21 +129,25 @@ const ByMeView = Backbone.View.extend({
'click .itemRow': 'doClick'
},
'atHomeOrWork': function(m) {
console.log('>> atHomeOrWork', m);
const atHomeOrWork = m.get('atHomeOrWork');
console.log('>> atHomeOrWork', atHomeOrWork);
if (atHomeOrWork)
// We're at home so hide
this.$el.parent().hide();
},
'updateLocation': function(l) {
console.log('>> ByMe Location has changed...');
if (l.has('location')) {
const location = l.get('location');
if (location.hasOwnProperty('atHomeOrWork')) {
this.model.set(location);
if (location.atHomeOrWork)
// We're at home so hide
this.$el.parent().hide();
}
if (l.has('atHome')) {
const llSix = l.get('llSix');
const ll = l.get('ll');
const atHomeOrWork = l.get('atHomeOrWork');
this.model.set({ llSix, atHomeOrWork, ll });
if (atHomeOrWork)
// We're at home so hide
this.$el.parent().hide();
}
else
console.log('>> ByMe No location yet');
},

View File

@ -3,8 +3,8 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const { get } = require('lodash');
const { reduceOpenWeather } = require('./reducers');
const { distance } = require('./utils');
const { reduceOpenWeather } = require('./libs/reducers');
const { distance } = require('./libs/utils');
const weatherItem = Backbone.Model.extend({
@ -63,7 +63,7 @@ const WeatherModel = Backbone.Model.extend({
'initialize': function() {
this.weatherCollection = weatherCollection;
this.run = false;
this.listenTo(this, 'change:ll', this.onChange);
this.listenTo(this, 'change:llFixed', this.onChange);
this.listenTo(this, 'change:update', this.onChange);
},
'onChange': function() {
@ -78,7 +78,7 @@ const WeatherModel = Backbone.Model.extend({
else {
const log = this.get('log');
const timeDiff = new Date().getTime() - log.time;
console.log('this', this);
const dist = distance(log.lat, log.long, this.get('latitude'), this.get('longitude'));
console.log('Weather distance:', dist);
@ -104,7 +104,7 @@ const WeatherModel = Backbone.Model.extend({
if (err)
console.error(err);
else {
// console.log(body);
// console.log(body);
const fsJSON = JSON.parse(body);
const city = get(fsJSON, 'city.name', '');
const list = get(fsJSON, 'list', []);
@ -147,7 +147,7 @@ const WeatherView = Backbone.View.extend({
this.wCollection = weatherCollection;
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.location.bind('change:llFixed', this.updateLocation, this);
/* this.wCollection.on('all', function(eventName) {
console.log(`${eventName } was triggered!`);
@ -166,10 +166,11 @@ const WeatherView = Backbone.View.extend({
'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);
if (l.has('atHome')) {
const llFixed = l.get('llFixed');
const latitude = l.get('latitude');
const longitude = l.get('longitude');
this.model.set({ llFixed, latitude, longitude });
}
else
console.log('>> Weather No location yet');

View File

@ -3,9 +3,10 @@ const _ = require('underscore');
const Backbone = require('backbone');
const request = require('request');
const fecha = require('fecha');
const TimeFormat = require('hh-mm-ss');
const { get } = require('lodash');
const { reduceOpenWeather } = require('./reducers');
const { distance, toHour } = require('./utils');
const { reduceOpenWeather } = require('./libs/reducers');
const { distance, toHour } = require('./libs/utils');
const WeatherAlertModel = Backbone.Model.extend({
'defaults': function (obj) {
@ -19,10 +20,19 @@ const WeatherAlertModel = Backbone.Model.extend({
this.listenTo(this, 'change:ll', this.onChange);
this.listenTo(this, 'change:update', this.onChange);
},
'onChange': function () {
console.log('Weather LL has changed');
'onChange': function (l) {
console.log('WeatherAlert LL has changed');
// if distance change > 10km
// if its been an hour since last update
const doUpdate = _.has(this.changedAttributes(), 'update');
console.log('doUpdate?', doUpdate);
/*
console.log(m.changedAttributes());
console.log(_.keys(m.changedAttributes()));
_.has()
*/
if (!this.has('log')) {
console.info('First run');
@ -34,11 +44,11 @@ const WeatherAlertModel = Backbone.Model.extend({
const ll = this.get('ll').split(',');
const dist = distance(log.lat, log.long, ll[0], ll[1]);
console.log('Weather distance:', dist);
console.log('WeatherAlert distance:', dist);
if ((dist > 5.0) && (timeDiff > 1.8e+6))
this.getWeatherAlert();
else if (timeDiff > 3.6e+6) {
else if (timeDiff > 3.6e+6 || doUpdate) {
console.log('WeatherAlert hourly update');
this.getWeatherAlert();
}
@ -79,18 +89,17 @@ const WeatherAlertModel = Backbone.Model.extend({
const log = { 'lat': ll[0], 'long': ll[1], 'time': new Date().getTime() };
console.log('>>WeatherAlert log', log);
this.set('log', log);
console.log('Next update in', TimeFormat.fromMs(toHour(), 'hh:mm:ss'));
this.timerID = setTimeout(
() => this.tick(),
toHour
toHour(1000)
);
console.log('>>WeatherAlert log', log);
this.set('log', log);
// console.log(this);
},
'tick': function () {
console.log('Set update');
console.log('WeatherAlert:Force update', fecha.format(new Date(), 'mediumTime'));
this.set('update', new Date().getTime());
}
@ -108,7 +117,7 @@ const WeatherAlertView = Backbone.View.extend({
this.$title = $('#weatherAlertTitle');
// this.model.bind('change', this.render, this);
this.location.bind('change', this.updateLocation, this);
this.location.bind('change:llShort', this.updateLocation, this);
this.model.bind('change:alert', this.render, this);
},
'template': _.template(`
@ -129,11 +138,11 @@ const WeatherAlertView = Backbone.View.extend({
},
'updateLocation': function(l) {
console.log('>> WeatherAlert Location has changed...');
console.log('changedAttributes:', this.location.changedAttributes());
if (l.has('location')) {
const location = l.get('location');
if (location.hasOwnProperty('atHome'))
this.model.set('ll', location.llFixed);
if (l.has('atHome')) {
const llShort = l.get('llShort');
this.model.set('ll', llShort);
}
else
console.log('>> Weather No location yet');

View File

@ -1,3 +1,4 @@
/* eslint-disable max-len */
require('muicss');
const $ = require('jquery');
const _ = require('underscore');
@ -9,6 +10,7 @@ const { NearbyModel, NearbyView } = require('./Nearby');
const { WeatherModel, WeatherView } = require('./Weather');
const { WeatherAlertModel, WeatherAlertView } = require('./WeatherAlert');
const { NewsModel, NewsView } = require('./News');
const { NewsCardModel, NewsCardView } = require('./NewsViewer');
const { ByMeModel, ByMeView } = require('./RightByMe');
var app = app || {};
@ -61,6 +63,8 @@ else
app.weatherAlert = new WeatherAlertView({ 'model': new WeatherAlertModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#weatherAlert', 'viewFrame':'#viewFrame' });
app.newsCard = new NewsCardView({ 'model': new NewsCardModel(), 'eventBus': app.eventBus });
app.news = new NewsView({ 'model': new NewsModel(), 'eventBus': app.eventBus, 'el':'#news' });
app.byMe = new ByMeView({ 'model': new ByMeModel(), 'eventBus': app.eventBus, 'location': app.locationModel, 'el':'#byme' });

93
src/v1/js/libs/panel.js Normal file
View File

@ -0,0 +1,93 @@
const $ = require('jquery');
let panelCount = 0;
function createPanel(params) {
const { title, divId } = params;
const newPanel = `
<div id="card_${divId}" class="fullscreen" style="display:none;">
<header class="header">
<div class="mui-appbar mui--appbar-line-height mui--z1" style="vertical-align:middle;">
<span>
<button class="mui-btn mui-btn--large mui-btn--primary mui-btn--flat closebutton" id="close_${divId}">
<i class="large material-icons mui--align-middle" style="color:black;">arrow_back</i>
</button>
</span>
<span class="mui--text-title mui--align-middle" id="title_${divId}">
${title}
</span>
</div>
</header>
<div class="box">
<div class="content" id="${divId}"></div>
</div>
<!-- end fullscreen-->
</div>
`;
const $newPanel = $(newPanel);
const $closeButton = $newPanel.find(`button#close_${divId}`);
$closeButton.on('click', () => {
doClose();
});
$newPanel.offset({ 'top': $(window).scrollTop(), 'left': 0 });
function doClose() {
const $body = $('body');
$newPanel.hide().remove();
console.log('panelCount', panelCount);
panelCount--;
if (panelCount === 0)
console.log('Removing panel stuff');
$body.removeClass('stop-scrolling').unbind('touchmove');
}
return $newPanel;
}
function addPanel($newPanel) {
const $body = $('body');
const $content = $newPanel.find('.content');
$body.append($newPanel);
if (panelCount === 0) {
$body.addClass('stop-scrolling');
$body.bind('touchmove', function(e) {
e.preventDefault();
});
}
panelCount++;
return $content;
}
module.exports = { createPanel, addPanel };
/*
class Template {
constructor(item) {
// "pubdate": "Tue, 06 Feb 2018 17:05:00 +0100",
const pubdateSrc = fecha.parse(item.pubdate, 'ddd, DD MMM YYYY HH:mm:SS ZZ');
const pubdate = fecha.format(pubdateSrc, 'dddd MMMM Do, YYYY');
const description = item.description.replace(/(<script(\s|\S)*?<\/script>)|(<style(\s|\S)*?<\/style>)|(<!--(\s|\S)*?-->)|(<\/?(\s|\S)*?>)/g, '');
this.data = `<article>
<header>
<a href="${item.guid.text}">${item.title}</a>
<time class="published">${pubdate}</time>
</header>
<p class="description">${description}</p>
</article>`;
}
toString() {
return this.data;
}
}
*/

View File

@ -1,4 +1,5 @@
const fecha = require('fecha');
const strictUriEncode = require('strict-uri-encode');
const { get } = require('lodash');
function reduceDarksky(item, city) {
@ -99,7 +100,7 @@ const reduceEuronews = function(item) {
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.guid = encodeURI(item.guid.text);
obj.title = item.title;
return obj;

View File

@ -42,10 +42,10 @@ function partOfDay(timeString, today) {
return dayBit;
}
function toHour() {
function toHour(extra = 0) {
const now = new Date();
return 3600000 - (now.getTime() % 3600000);
return (3600000 - (now.getTime() % 3600000)) + extra;
}
function distance(lat1, lon1, lat2, lon2) {
@ -58,6 +58,10 @@ function distance(lat1, lon1, lat2, lon2) {
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}
function splitURL(url) {
}
const maybePluralize = (count, noun, suffix = 's') =>
`${count} ${noun}${count !== 1 ? suffix : ''}`;

21
test/euronews.spec.js Normal file

File diff suppressed because one or more lines are too long