#!/usr/bin/env node //# vi: ft=javascript var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var async = require('async'); var express = require('express'); var handlebars = require('handlebars'); var app = express(); var staticHandler = express['static'].bind(express); app.use((function log() { var text = '{{date}} {{req.method}} {{req.url}}'; var logTemplate = handlebars.compile(text); return function (req, res, next) { console.log(logTemplate({date: new Date().toISOString(), req: req})); next(); }; }())); function renderError(res, err) { console.error(err); res.writeHead(500, {'Content-Type': 'text/plain'}); res.write(err); } function pathToFile() { return path.normalize( path.join.apply(null, [__dirname, '..'].concat(_.toArray(arguments))) ); } function index(req, res) { var indexFile = pathToFile('assets', 'index.html'); function compileServe(err, text) { if (err) { renderError(res, err); return; } var tmpl = handlebars.compile(text); res.send(tmpl()); } fs.readFile(indexFile, {encoding: 'utf8'}, compileServe); } app.get('/', index); function serveIfExists(path) { return function _serveIfExists(cb) { fs.exists(path, function (exists) { if (!exists) { cb(null, null); return; } fs.readFile(path, {encoding: 'utf8'}, cb); }); }; } app.get('/slides', function showHandler(req, res) { fs.readdir(pathToFile('data', 'slide'), function (err, files) { files.sort(); var jsonFiles = _.filter(files, function (file) { return /^\d+\.json$/.test(file); }); res.send(_.map(jsonFiles, function (file) { return { id: parseInt(file.replace(/\.json$/, ''), 10) }; })); }); }); app.get(/slide\/(\d+)/, function showHandler(req, res, next) { if (!req.xhr) { index(req, res); return; } var slideId = req.params[0]; var slidePath = pathToFile('data', 'slide', slideId + '.json'); fs.exists(slidePath, function (exists) { if (!exists) { next(); return; } fs.readFile(slidePath, {encoding: 'utf8'}, function (err, content) { if (err) { renderError(res, err); return; } try { var slide = JSON.parse(content); } catch (err) { renderError(res, err); return; } async.parallel({ copy: serveIfExists(pathToFile('data', 'copy', slide.copy + '.md')), code: serveIfExists(pathToFile('data', 'code', slide.code + '.js')) }, function (err, slideContent) { if (err) { renderError(res, err); return; } slideContent.id = parseInt(slideId, 10); slideContent.title = slide.title || ''; res.send(slideContent); }); }); }); }); app.use('/data', staticHandler('data')); app.use('/vendor', staticHandler('vendor')); app.use('/static', staticHandler('static')); app.listen(8000);