diff --git a/build.js b/build.js
index fcd2f8dde243b..f882e76cade1b 100755
--- a/build.js
+++ b/build.js
@@ -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 **/
@@ -179,6 +180,7 @@ function copystatic () {
})
})
})
+ fs.writeFileSync(path.join(__dirname, 'build', 'static', 'event-geo.json'), JSON.stringify(eventGeo()))
}
function fullbuild () {
diff --git a/events/pull-meetup.js b/events/pull-meetup.js
new file mode 100644
index 0000000000000..05749c13bf810
--- /dev/null
+++ b/events/pull-meetup.js
@@ -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)
diff --git a/events/pull-nodeschool.js b/events/pull-nodeschool.js
new file mode 100644
index 0000000000000..2dd99330509d9
--- /dev/null
+++ b/events/pull-nodeschool.js
@@ -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()
+})
diff --git a/events/yaml-sync.js b/events/yaml-sync.js
new file mode 100644
index 0000000000000..f11fbeb8b8e98
--- /dev/null
+++ b/events/yaml-sync.js
@@ -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()
diff --git a/layouts/css/_variables.styl b/layouts/css/_variables.styl
index b9c31a2653cf9..71093215e67c8 100644
--- a/layouts/css/_variables.styl
+++ b/layouts/css/_variables.styl
@@ -6,7 +6,11 @@ $node-green = #80bd01
$light-green = #f1fbda
$hover-green = #d9ebb3
-$hover-red = #CC2828
+$yellow = #f7da03
+
+$hover-red = #cc2828
+
+$blue = #3887be
$light-gray3 = #f0f0f0
$light-gray2 = #ccc
diff --git a/layouts/css/base.styl b/layouts/css/base.styl
index d4339fef9a2a7..a471db0a717c6 100644
--- a/layouts/css/base.styl
+++ b/layouts/css/base.styl
@@ -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'
diff --git a/layouts/css/page-modules/_events.styl b/layouts/css/page-modules/_events.styl
new file mode 100644
index 0000000000000..9d5694a9c6bf6
--- /dev/null
+++ b/layouts/css/page-modules/_events.styl
@@ -0,0 +1,96 @@
+#events-map
+ height 400px
+ width 100%
+ margin-top 20px
+
+ .marker-image img
+ max-width 320px
+ max-height 100px
+
+.events-map-key
+ padding 8px 0
+ text-align center
+ @media screen and (max-width: 600px)
+ margin-bottom 1em
+ padding 2px 0
+ text-align left
+
+ span
+ padding-right 12px
+ @media screen and (max-width: 600px)
+ display block
+ width 100%
+ padding-right 0
+
+ span::before
+ content ""
+ width 12px
+ height 12px
+ display inline-block
+ border-radius 3px
+ margin-right 8px
+
+ .key-meetup::before
+ background $node-green
+
+ .key-nodeschool::before
+ background $yellow
+
+ .key-conference::before
+ background $blue
+
+.region
+ margin-bottom 2em
+
+ h1
+ margin 0 0 0.5em
+
+ h2
+ margin 0 0 0.5em
+ line-height 1
+ &.is-displayed
+ .arrow
+ transform rotate(90deg)
+
+ .arrow
+ display inline-block
+
+ h3
+ margin 0 0 0.5em
+
+ .conference
+ h3
+ margin 0 0 0.2em
+ p
+ margin-top 0
+
+ .events-number:not(:empty)
+ position relative
+ top -3px
+ display inline-block
+ background $gray
+ border-radius 3px
+ padding 3px 4px
+ font-size 0.6em
+ color: $white
+
+ .events-list
+ display flex
+ flex-wrap wrap
+ list-style none
+ padding 0
+ margin 0 0 10px
+
+ html.has-js &
+ display none
+ &.is-displayed
+ display flex
+
+ li
+ width 31%
+ padding 0
+ margin 0 2% 1rem 0
+
+ @media screen and (max-width: 600px)
+ li
+ width 100%
diff --git a/layouts/events.hbs b/layouts/events.hbs
new file mode 100644
index 0000000000000..005a27be004df
--- /dev/null
+++ b/layouts/events.hbs
@@ -0,0 +1,208 @@
+
+
+{{> html-head }}
+
+
+ {{> header }}
+
+
+
+
+
+
+
+
+
+
+ Meetup
+ NodeSchool
+ Conference
+
+
+ {{{contents}}}
+
+ {{#each regions}}
+
+
{{region}}
+ {{#if conferences}}
Conferences
{{/if}}
+
+ {{#each conferences}}
+
+
{{name}}
+ {{#if location}}
+ - map
+ {{/if}}
+
+ {{#if desc}}
+
{{desc}}
+ {{/if}}
+
+ {{/each}}
+
+
+
+ {{#each nodeschools}}
+ -
+ {{#if website}}
+ {{name}}
+ {{else}}
+ {{#if repo}}
+ {{name}}
+ {{else}}
+ {{name}}
+ {{/if}}
+ {{/if}}
+
+ {{/each}}
+
+
+
+
+ {{#each meetups}}
+ - {{name}}
+ {{/each}}
+
+
+
+ {{/each}}
+
+
+
+
+
+ {{> footer }}
+
+
+
+
+
+
diff --git a/layouts/partials/html-head.hbs b/layouts/partials/html-head.hbs
index d402afa081c7c..69a3fd29cc84e 100644
--- a/layouts/partials/html-head.hbs
+++ b/layouts/partials/html-head.hbs
@@ -20,6 +20,8 @@