diff --git a/README.md b/README.md
index 1a4c53941..c53fe1d72 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,20 @@
This is the source of the parisjs.org website.
+## Utils
+
+Before everything else:
+
+ npm install
+
+### Parsing all meetups and talks
+
+ node utils/meetups.js parse
+
+### Writing all meetups
+
+ node utils/meetups.js update
+
## License
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
diff --git a/index.html b/index.html
index 1363c2838..6d8eb9b8b 100644
--- a/index.html
+++ b/index.html
@@ -66,12 +66,12 @@
Seen and heard in the meetups
@@ -173,7 +173,7 @@
Seen and heard in the meetups
RxJS
By: @jbrwk
-
Here are the Slides!
+
Here are the Slides!
@@ -396,14 +396,14 @@ Seen and heard in the meetups
- Cross-Browser JS Synthesizer
- By: @hexapode
- - Here are the
Slides!
+ - Here are the
Slides!
@@ -417,7 +417,7 @@ Seen and heard in the meetups
- Overview of awesome Mozilla Labs projects
- By: @tbassetto
- - Here are the
Slides!
+ - Here are the
Slides!
@@ -449,7 +449,7 @@
Seen and heard in the meetups
- - Video cpature using HTML5
+ - Video capture using HTML5
- By: @tbassetto
- Here are the Slides!
@@ -458,7 +458,7 @@
Seen and heard in the meetups
CouchDB user feedback
By: @challet
-
Here are the Slides!
+
Here are the Slides!
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..2c2060eae
--- /dev/null
+++ b/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "parisjs-website",
+ "description": "The website of parisjs.org",
+ "version": "0.0.1",
+ "homepage": "http://parisjs.org/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/parisjs/parisjs-website.git"
+ },
+ "dependencies": {
+ "jsdom": "",
+ "underscore": ""
+ },
+ "devDependencies": {}
+}
diff --git a/utils/meetups.js b/utils/meetups.js
new file mode 100755
index 000000000..351aec1b1
--- /dev/null
+++ b/utils/meetups.js
@@ -0,0 +1,177 @@
+#!/usr/bin/env node
+/**
+ * You don't want to manage the list of events/talks/people by hands? This script can help you.
+ *
+ * It's a two way parser:
+ * - read index.html and extract the list of events/talks/people and output JSON on stdout
+ * - read a JSON from stdin and update index.html
+ *
+ * Why JSON?
+ * Because there is no good yaml parser in javascript, and it was out of the scope for now
+ *
+ * How to use it?
+ * node utils/meetups.js parse > meetup.json
+ * node utils/meetups.js update < meetup.json
+ */
+
+var jsdom = require('jsdom')
+ , fs = require('fs')
+ , _ = require('underscore')
+;
+
+if (process.argv.length == 3) {
+ if (process.argv[2] == 'parse') {
+ return parseMeetups();
+ } else if (process.argv[2] == 'update') {
+ return updateMeetups();
+ }
+}
+console.log('usage');
+console.log('node utils/meetups.js parse');
+console.log('node utils/meetups.js update');
+return;
+
+function readStdin(callback) {
+ var data = "";
+ process.stdin.resume();
+ process.stdin.setEncoding('utf8');
+
+ process.stdin.on('data', function (chunk) {
+ data += chunk;
+ });
+ process.stdin.on('end', function () {
+ callback(data);
+ });
+}
+
+function updateMeetups() {
+ readStdin(function(data) {
+ var meetups = JSON.parse(data);
+ var html = generateHTMLFor(meetups);
+ var website = fs.readFileSync(__dirname + '/../index.html', 'utf8');
+
+ var jquery = 'http://code.jquery.com/jquery-1.5.min.js';
+ jsdom.env(website, [jquery],
+ function(errors, window) {
+ if (errors) {
+ return console.error('jsdom error', errors)
+ }
+ var $ = window.$;
+ var $meetups = $('#meetups > ul').empty().append("\n ").append($(html)).append("\n ");
+ var document = window.document;
+ // remove the jquery inserted by jsdom
+ $('script:last', document).remove();
+ var output = document.doctype + document.innerHTML;
+ console.log(output.trim());
+ });
+ });
+}
+
+function generateHTMLFor(meetups) {
+ var template = _.template(fs.readFileSync(__dirname +'/template_meetup.html', 'utf8'));
+ var html = meetups.map(function(meetup) {
+ return template(meetup);
+ });
+ return "\n"+ html.join('')+"\n\n";
+}
+
+//
+
+function parseMeetups() {
+ var website = fs.readFileSync(__dirname + '/../index.html', 'utf8');
+
+ var jquery = 'http://code.jquery.com/jquery-1.5.min.js';
+ jsdom.env(website, [jquery],
+ function(errors, window) {
+ if (errors) {
+ return console.error('jsdom error', errors)
+ }
+ var $ = window.$;
+ var meetups = extractMeetups($);
+ console.log(JSON.stringify(meetups));
+ //var normalization = normalizeMeetups(meetups);
+ //console.log(JSON.stringify(normalization));
+ });
+}
+
+/**
+ * The hard part
+ * find same authors to have a ref to each ones
+ */
+function normalizeMeetups(meetups) {
+ var authors = {};
+ // don't worry, this is only the first level of indentation
+ meetups.forEach(function(meetup) {
+ // don't worry, this is only the second level of indentation
+ meetup.talks.forEach(function(talk) {
+ // don't worry, this is only the last level of indentation
+ talk.authors = talk.authors.map(function(author) {
+ if (authors[author.name]) {
+ var previous = authors[author.name];
+ ['url', 'avatar'].forEach(function(property) {
+ if (author[property] != previous[property])
+ console.error('grrrr! '+ property +' mismatch for '+ author.name);
+ });
+ }
+ authors[author.name] = author;
+ return author.name;
+ });
+ })
+ });
+ return {
+ meetups: meetups,
+ authors: authors
+ }
+}
+
+/**
+ * Return a raw object of all events and talks
+ */
+function extractMeetups($) {
+ var $meetups = $('#meetups li.meetup');
+ var meetups = [];
+ $meetups.each(function() {
+ var $meetup = $(this);
+ var meetup = {
+ title: $(this).text(),
+ talks: []
+ };
+ var $talks = $meetup.next('.meetup-content').find('> ul');
+ meetup.talks = extractTalks($, $talks);
+ meetups.push(meetup);
+ });
+ return meetups;
+}
+
+function extractTalks($, $talks) {
+ return $talks.toArray().map(function(talk) {
+ var $talk = $(talk);
+
+ function getLinks(regexp) {
+ return $talk.find('.descTalk a').filter(function() {
+ return $(this).text().match(regexp);
+ }).map(function() {
+ return $(this).attr('href');
+ }).toArray();
+ }
+
+ var slides = getLinks(/slide/i);
+ var videos = getLinks(/(video|part)/i);
+ var projects = getLinks(/project/i);
+
+ return {
+ title: $talk.find('.titleTalk').text().trim(),
+ slides: slides,
+ videos: videos,
+ projects: projects,
+ authors: $talk.find('.authorTalk a').map(function() {
+ return {
+ name: $(this).text(),
+ url: $(this).attr('href'),
+ // sorry boy, in case of multiple authors, we have only one avatar
+ avatar: $talk.find('.avatar img').attr('src')
+ }
+ }).toArray()
+ }
+ });
+}
diff --git a/utils/template_meetup.html b/utils/template_meetup.html
new file mode 100644
index 000000000..83b36595f
--- /dev/null
+++ b/utils/template_meetup.html
@@ -0,0 +1,14 @@
+
<%= title %>
+
+
<% talks.forEach(function(talk) { %>
+
+
+ - <% if (talk.authors[0].avatar) { %><% } %>
+ - <%= talk.title %>
+ - By: <% for (var i=0; i<%= author.name %><% if (!end) print(" and "); } %>
+ - Here are the <% if (talk.projects.length == 1) { %>project<% } %><% if (talk.slides.length == 1) { if (talk.slides[0] == "#") print('
'); %>Slides<%if (talk.slides[0] == "#") print(''); } %><% if (talk.videos.length > 0) { %>
+ and the <% for (var j=0; jvideo<% if (talk.videos.length > 1) print(' part.'+ (j+1)); %><% } }%>!
+
<% }) %>
+
+
+