Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comprehensive Events Page #378

Merged
merged 16 commits into from
Nov 28, 2015
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const filterStylusPartials = require('./scripts/plugins/filter-stylus-partials')
const anchorMarkdownHeadings = require('./scripts/plugins/anchor-markdown-headings')
const loadVersions = require('./scripts/load-versions')
const latestVersion = require('./scripts/helpers/latestversion')
const eventGeo = require('./scripts/event-geo.js')

/** Build **/

Expand Down Expand Up @@ -179,6 +180,7 @@ function copystatic () {
})
})
})
fs.writeFileSync(path.join(__dirname, 'build', 'static', 'event-geo.json'), JSON.stringify(eventGeo()))
}

function fullbuild () {
Expand Down
58 changes: 58 additions & 0 deletions events/pull-meetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
var request = require('request').defaults({json: true, headers: {'user-agent': 'pull-meeting-0.1'}}),
url = 'https://api.meetup.com/2/groups',
auth = process.env.MEETUP_TOKEN,
qs = require('querystring'),
yml = require('./yaml-sync'),
opts =
{ topic: 'nodejs', category: 34, upcoming_events: true, key: auth
},
allresults = [],
u = url + '?' + qs.stringify(opts)

var countryMap =
{ ES: 'Africa', MU: 'Africa', NG: 'Africa', KE: 'Africa', ZA: 'Africa', MA: 'Africa', EG: 'Africa', IL: 'Asia', TH: 'Asia', KR: 'Asia', RU: 'Asia', ID: 'Asia', PH: 'Asia', IN: 'Asia', HK: 'Asia', CN: 'Asia', VN: 'Asia', TW: 'Asia', LK: 'Asia', NP: 'Asia', JP: 'Asia', AE: 'Asia', BD: 'Asia', LT: 'Europe', RS: 'Europe', HR: 'Europe', CZ: 'Europe', PT: 'Europe', TR: 'Europe', GR: 'Europe', DE: 'Europe', RO: 'Europe', MT: 'Europe', GH: 'Europe', IE: 'Europe', FI: 'Europe', SE: 'Europe', UA: 'Europe', AT: 'Europe', HU: 'Europe', CH: 'Europe', IS: 'Europe', GB: 'Europe', DK: 'Europe', EE: 'Europe', BE: 'Europe', NO: 'Europe', NL: 'Europe', FR: 'Europe', PL: 'Europe', SK: 'Europe', IT: 'Europe', SI: 'Europe', LU: 'Europe', BY: 'Europe', ME: 'Europe', CA: 'North America', US: 'North America', DO: 'Latin America', AR: 'Latin America', PE: 'Latin America', MX: 'Latin America', BR: 'Latin America', VE: 'Latin America', CL: 'Latin America', CO: 'Latin America', UY: 'Latin America', PA: 'Latin America', GT: 'Latin America', EC: 'Latin America', AU: 'South Pacific', SG: 'South Pacific', NZ: 'South Pacific'
}

function clean (event) {
delete event.topics
delete event.urlname
delete event.category
delete event.id
}

function finish (events) {
// return console.log(JSON.stringify(events))
events.forEach(function (event) {
if (!countryMap[event.country]) {
console.log(event)
throw new Error('Do not have map for ' + event.country)
}
var region = yml.getRegion(countryMap[event.country])
if (!region.meetups) region.meetups = []
clean(event)
yml.replace(region.meetups, 'name', event.name, event)
})
yml.save()
}
// This is nice when testing if you cache the response
// finish(JSON.parse(require('fs').readFileSync('./meetup.json').toString()))

function _go (u) {
request(u, function (e, resp, body) {
var results = body.results
results.forEach(function (result) {
var title = result.name.toLowerCase()
if (title.indexOf('nodeschool') !== -1) return
if (title.indexOf('mongodb') !== -1 && title.indexOf('node') === -1) return
if (title.indexOf('find a tech job') !== -1 && title.indexOf('node') === -1) return
// if (title.indexOf('node') !== -1) return allresults.push(result)
allresults.push(result)
})
if (body.meta.next) {
_go(body.meta.next)
} else {
finish(allresults)
}
})
}
_go(u)
42 changes: 42 additions & 0 deletions events/pull-nodeschool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
var request = require('request'),
yml = require('./yaml-sync'),
nodeGeocoder = require('node-geocoder'),
geoOpts =
{ apiKey: process.env.MAPS_TOKEN, formatter: null
},
geocoder = nodeGeocoder('google', 'https', geoOpts)

request('http://nodeschool.io/chapters/list.json', {json: true}, function (e, resp, list) {
if (e || resp.statusCode !== 200) throw (e || new Error('response not 200' + resp.statusCode))
var count = 0
var chapters = []

list.regions.forEach(function (reg) {
var store = yml.getRegion(reg.region)
if (!store.nodeschools) {
store.nodeschools = []
}

reg.chapters.forEach(function (chapter) {
delete chapter.region
chapter.location = chapter.location + ', ' + chapter.country
delete chapter.country
yml.replace(store.nodeschools, 'name', chapter.name, chapter)
count += 1
chapters.push(chapter)
})
})

function _geo () {
if (chapters.length === 0) return yml.save()
var chapter = chapters.shift()
geocoder.geocode(chapter.location, function (err, res) {
console.log(err, res)
if (err || !res.length) return _geo()
chapter.lat = res[0].latitude
chapter.lon = res[0].longitude
_geo()
})
}
_geo()
})
52 changes: 52 additions & 0 deletions events/yaml-sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
var yaml = require('js-yaml'),
fs = require('fs'),
path = require('path'),
p = path.join(__dirname, '..', 'locale', 'en', 'get-involved', 'events.md'),
buf = fs.readFileSync(p),
lines = buf.toString().split('\n'),
str = lines.slice(lines.indexOf('---') + 1, lines.indexOf('---', lines.indexOf('---') + 1)).join('\n'),
store = yaml.safeLoad(str)

exports.getRegion = function (region) {
for (var reg in store.regions) {
if (store.regions[reg].region === region) return store.regions[reg]
}
var reg = {region: region}
store.regions.push(reg)
return reg
}

exports.removeEmpty = function (dict) {
for (var i in dict) {
if (!dict[i]) delete dict[i]
}
}

exports.replace = function (list, key, keyValue, value) {
exports.removeEmpty(value)
for (var i = 0;i < list.length;i++) {
if (list[i][key] === keyValue) {
list[i] = value
return
}
}
list.push(value)
}

exports.save = function () {
var str = ['---', yaml.dump(store), '---'].join('\n')
fs.writeFileSync(p, str)
}

function rebalance () {
store.regions = store.regions.slice(0, 6)
exports.save()
}

function clearMeetups () {
store.regions.forEach(function (reg) {
delete reg.meetups
})
exports.save()
}
// clearMeetups()
1 change: 1 addition & 0 deletions layouts/css/base.styl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pre
@import 'page-modules/_foundation-members'
@import 'page-modules/_in-the-news'
@import 'page-modules/_download'
@import 'page-modules/_events'
@import 'page-modules/_interactiveBanner'
@import 'page-modules/_scrollToTop'
@import 'page-modules/_anchorLinks'
Expand Down
15 changes: 15 additions & 0 deletions layouts/css/page-modules/_events.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.events-list
display flex
flex-wrap wrap
list-style none
padding 0
margin 0

li
width 31%
padding 0
margin 0 2% 1rem 0

@media screen and (max-width: 600px)
li
width 100%
188 changes: 188 additions & 0 deletions layouts/events.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="{{site.locale}}">
{{> html-head }}

<body>
{{> header }}

<div id="main">
<div class="container has-side-nav">

<aside>
<ul>
<li{{#equals path site.getinvolved.link}} class="active"{{/equals}}>
<a href="/{{site.locale}}/{{site.getinvolved.link}}/">{{site.getinvolved.text}}</a>
</li>
<li{{#equals path site.getinvolved.code-and-learn.link}} class="active"{{/equals}}>
<a href="/{{site.locale}}/{{site.getinvolved.code-and-learn.link}}/">{{site.getinvolved.code-and-learn.text}}</a>
</li>
<li{{#equals path site.getinvolved.contribute.link}} class="active"{{/equals}}>
<a href="/{{site.locale}}/{{site.getinvolved.contribute.link}}/">{{site.getinvolved.contribute.text}}</a>
</li>
<li{{#equals path site.getinvolved.development.link}} class="active"{{/equals}}>
<a href="/{{site.locale}}/{{site.getinvolved.development.link}}/">{{site.getinvolved.development.text}}</a>
</li>
<li{{#equals path site.getinvolved.events.link}} class="active"{{/equals}}>
<a href="/{{site.locale}}/{{site.getinvolved.events.link}}/">{{site.getinvolved.events.text}}</a>
</li>
<li>
<a href="https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-of-conduct">{{site.getinvolved.conduct.text}}</a>
</li>
</ul>
</aside>

<article>
<script src='https://api.mapbox.com/mapbox.js/v2.2.3/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v2.2.3/mapbox.css' rel='stylesheet' />
<style>
div.map {
height: 400px;
width:100%;
margin-top:10px;
}
.key {
padding: 8px 0;
text-align: center;
}
.key span {padding-right: 12px;}
.key-meetup::before {
content: "";
width: 12px;
height: 12px;
background: #80bd01;
display: inline-block;
border-radius: 3px;
margin-right: 8px;
}
.key-nodeschool::before {
content: "";
width: 12px;
height: 12px;
background: #f7da03;
display: inline-block;
border-radius: 3px;
margin-right: 8px;
}
.key-conference::before {
content: "";
width: 12px;
height: 12px;
background: #3887be;
display: inline-block;
border-radius: 3px;
margin-right: 8px;
}
.events #map {
margin: 0;
padding: 0;
}
</style>
<div id="map" class="map"></div>
<div class="key">
<span class="key-meetup" data-i18n="events-meetu">Meetup</span>
<span class="key-nodeschool" data-i18n="events-nodeschool">NodeSchool</span>
<span class="key-conference" data-i18n="events-conference">Conference</span>
</div>
<script>
function haversine() {
var radians = Array.prototype.map.call(arguments, function(deg) { return deg/180.0 * Math.PI; });
var lat1 = radians[0], lon1 = radians[1], lat2 = radians[2], lon2 = radians[3];
var R = 6372.8; // km
var dLat = lat2 - lat1;
var dLon = lon2 - lon1;
var a = Math.sin(dLat / 2) * Math.sin(dLat /2) + Math.sin(dLon / 2) * Math.sin(dLon /2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}

L.mapbox.accessToken = 'pk.eyJ1IjoibWlrZWFsIiwiYSI6ImNpaGY5ajk5ZTA0ZTN0cmo3MzZ5NnV5eHIifQ.YBfpOTUwcbaZrNvAfDaDcQ';
var map = L.mapbox.map('map', 'mapbox.streets-basic')
var featureLayer = L.mapbox.featureLayer()

featureLayer.on('ready', function () {
if ("geolocation" in navigator) {
/* geolocation is available */
navigator.geolocation.getCurrentPosition(function(position) {
var lat = position.coords.latitude
, lon = position.coords.longitude
;

var points = featureLayer.getGeoJSON().features.map(function (feature) {
var _lat = feature.geometry.coordinates[1]
, _lon = feature.geometry.coordinates[0]
;
return [feature.geometry.coordinates[0], feature.geometry.coordinates[1], haversine(lat, lon, _lat, _lon)]
})
.sort(function (a, b) {
return a[2] > b[2]
})

function _bounds () {
for (var i=0;i<points.length;i++) {
var hav = points[i][2]
if (i > 10 && hav > .1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the 10th point the one to return?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's how I do bounds detection, it could be a regular for loop as well, though where is the fun in that?

function getBounds(points) { 
    return points.reduce(function(bounds, point) {
        bounds.minLat = bounds.minLat < point.lat ? bounds.minLat : point.lat; 
        bounds.maxLat = bounds.maxLat < point.lat ? point.lat : bounds.maxLat; 
        bounds.minLon = bounds.minLon < point.lon ? bounds.minLon : point.lon; 
        bounds.maxLon = bounds.maxLon < point.lon ? point.lon : bounds.maxLon; 
        return bounds;
    }, {minLat: 180, maxLat: -180, minLon: 90, maxLon: -90}) 
}

return [points[i][1], points[i][0]]
}
}
}

map.fitBounds([[lat, lon], _bounds()])
map.setView([lat, lon], map.getZoom()-1)
})
}
})

featureLayer.loadURL('/static/event-geo.json').addTo(map)
</script>

{{#each regions}}
<div class="region">
<h1>{{region}}</h1>
{{#if conferences}}<h2>Conferences</h2>{{/if}}
{{#each conferences}}
<div class="conference">
<h3><a href="{{link}}">{{name}}</a>
{{#if location}}
- <small class="map-link"><a href="https://www.google.com/maps/place/{{location}}">map</a></small>
{{/if}}
</h3>
{{#if desc}}
<div class="conference-description">{{desc}}</div>
{{/if}}
</div>
{{/each}}

<h2>NodeSchools</h2>
<ul class="events-list">
{{#each nodeschools}}
<li>
{{#if website}}
<a href="{{website}}">{{name}}</a>
{{else}}
{{#if repo}}
<a href="{{repo}}">{{name}}</a>
{{else}}
{{name}}
{{/if}}
{{/if}}
</li>
{{/each}}
</ul>

<h2>Meetups</h2>
<ul class="events-list">
{{#each meetups}}
<li><a href="{{link}}">{{name}}</a></li>
{{/each}}
</ul>

</div>
{{/each}}
</article>

</div>
</div>

{{> footer }}
</body>
</html>
Loading