node powered mobile
TRANSCRIPT
Node Powered MobileBy Tim Caswell
Saturday, June 5, 2010
Node Powered MobileBy Tim Caswell
Saturday, June 5, 2010
Simple but Different
Saturday, June 5, 2010
What is needed
Saturday, June 5, 2010
What is needed• Simple Interface
• Light Code
• Networked Data
• Real-Time Data
• Free Deployment
• Open Workflow
Saturday, June 5, 2010
What is needed• Simple Interface
• Light Code
• Networked Data
• Real-Time Data
• Free Deployment
• Open Workflow
• HTML, SVG, CSS
• JavaScript
• HTTP Services
• PubSub
• Browser Apps
• It’s just text!
Saturday, June 5, 2010
ConnectWe’ll use a new node
framework that “connects” the mobile browser to data
on the server.
Saturday, June 5, 2010
It’s like Japanese LegosSaturday, June 5, 2010
Connect.createServer([ {filter: "log"}, {filter: "body-decoder"}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root}]);
Pre-Built Blocks
Saturday, June 5, 2010
And easy too!Saturday, June 5, 2010
method-override.jsvar key;// Initialize any state (on server startup)exports.setup = function (env) { key = this.key || "_method";};// Modify the request stream (on request)exports.handle = function(err, req, res, next){ if (key in req.body) { req.method = req.body[key].toUpperCase(); } next();};
Saturday, June 5, 2010
response-time.jsexports.handle = function(err, req, res, next){ var start = new Date, writeHead = res.writeHead;
res.writeHead = function(code, headers){ res.writeHead = writeHead; headers['X-Response-Time'] = (new Date - start) + "ms"; res.writeHead(code, headers); };
next();};
Saturday, June 5, 2010
Well, actually, it’s not always easy.
Saturday, June 5, 2010
static.js
Saturday, June 5, 2010
static.jsvar fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js // Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js // Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
} next(err); return; }
// Serve the file directly using buffers fs.readFile(filename, function (err, data) { if (err) { next(err); return; } res.writeHead(200, { "Content-Type": Mime.type(filename), "Content-Length": data.length, "Last-Modified": stat.mtime.toUTCString(), // Cache in browser for 1 year "Cache-Control": "public max-age=" + 31536000 }); res.end(data); }); }); }
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js // Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
} next(err); return; }
// Serve the file directly using buffers fs.readFile(filename, function (err, data) { if (err) { next(err); return; } res.writeHead(200, { "Content-Type": Mime.type(filename), "Content-Length": data.length, "Last-Modified": stat.mtime.toUTCString(), // Cache in browser for 1 year "Cache-Control": "public max-age=" + 31536000 }); res.end(data); }); }); }
};
// Mini mime module for static file servingvar Mime = {
type: function getMime(path) { var index = path.lastIndexOf("."); if (index < 0) { return DEFAULT_MIME; } var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; return (/(text|javascript)/).test(type) ? type + "; charset=utf-8" : type; },
TYPES : { ".3gp" : "video/3gpp", ".a" : "application/octet-stream", ".ai" : "application/postscript", ".aif" : "audio/x-aiff", ".aiff" : "audio/x-aiff", ".asc" : "application/pgp-signature", ".asf" : "video/x-ms-asf", ".asm" : "text/x-asm", ".asx" : "video/x-ms-asf", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".avi" : "video/x-msvideo", ".bat" : "application/x-msdownload", ".bin" : "application/octet-stream",
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js ".flv" : "video/x-flv", ".for" : "text/x-fortran", ".gem" : "application/octet-stream", ".gemspec" : "text/x-script.ruby", ".gif" : "image/gif", ".gz" : "application/x-gzip", ".h" : "text/x-c", ".hh" : "text/x-c", ".htm" : "text/html", ".html" : "text/html", ".ico" : "image/vnd.microsoft.icon", ".ics" : "text/calendar", ".ifb" : "text/calendar", ".iso" : "application/octet-stream", ".jar" : "application/java-archive", ".java" : "text/x-java-source", ".jnlp" : "application/x-java-jnlp-file", ".jpeg" : "image/jpeg", ".jpg" : "image/jpeg", ".js" : "application/javascript", ".json" : "application/json", ".log" : "text/plain", ".m3u" : "audio/x-mpegurl", ".m4v" : "video/mp4", ".man" : "text/troff", ".mathml" : "application/mathml+xml", ".mbox" : "application/mbox", ".mdoc" : "text/troff", ".me" : "text/troff", ".mid" : "audio/midi", ".midi" : "audio/midi", ".mime" : "message/rfc822", ".mml" : "application/mathml+xml", ".mng" : "video/x-mng", ".mov" : "video/quicktime", ".mp3" : "audio/mpeg", ".mp4" : "video/mp4", ".mp4v" : "video/mp4", ".mpeg" : "video/mpeg", ".mpg" : "video/mpeg", ".ms" : "text/troff", ".msi" : "application/x-msdownload", ".odp" : "application/vnd.oasis.opendocument.presentation", ".ods" : "application/vnd.oasis.opendocument.spreadsheet", ".odt" : "application/vnd.oasis.opendocument.text", ".ogg" : "application/ogg", ".p" : "text/x-pascal", ".pas" : "text/x-pascal", ".pbm" : "image/x-portable-bitmap", ".pdf" : "application/pdf", ".pem" : "application/x-x509-ca-cert", ".pgm" : "image/x-portable-graymap", ".pgp" : "application/pgp-encrypted", ".pkg" : "application/octet-stream", ".pl" : "text/x-script.perl", ".pm" : "text/x-script.perl-module", ".png" : "image/png", ".pnm" : "image/x-portable-anymap", ".ppm" : "image/x-portable-pixmap", ".pps" : "application/vnd.ms-powerpoint", ".ppt" : "application/vnd.ms-powerpoint", ".ps" : "application/postscript", ".psd" : "image/vnd.adobe.photoshop", ".py" : "text/x-script.python", ".qt" : "video/quicktime", ".ra" : "audio/x-pn-realaudio", ".rake" : "text/x-script.ruby", ".ram" : "audio/x-pn-realaudio", ".rar" : "application/x-rar-compressed", ".rb" : "text/x-script.ruby", ".rdf" : "application/rdf+xml", ".roff" : "text/troff", ".rpm" : "application/x-redhat-package-manager", ".rss" : "application/rss+xml", ".rtf" : "application/rtf", ".ru" : "text/x-script.ruby", ".s" : "text/x-asm", ".sgm" : "text/sgml", ".sgml" : "text/sgml", ".sh" : "application/x-sh", ".sig" : "application/pgp-signature", ".snd" : "audio/basic", ".so" : "application/octet-stream", ".svg" : "image/svg+xml", ".svgz" : "image/svg+xml", ".swf" : "application/x-shockwave-flash", ".t" : "text/troff", ".tar" : "application/x-tar", ".tbz" : "application/x-bzip-compressed-tar", ".tci" : "application/x-topcloud", ".tcl" : "application/x-tcl", ".tex" : "application/x-tex", ".texi" : "application/x-texinfo", ".texinfo" : "application/x-texinfo", ".text" : "text/plain", ".tif" : "image/tiff", ".tiff" : "image/tiff", ".torrent" : "application/x-bittorrent", ".tr" : "text/troff", ".ttf" : "application/x-font-ttf", ".txt" : "text/plain", ".vcf" : "text/x-vcard", ".vcs" : "text/x-vcalendar", ".vrml" : "model/vrml", ".war" : "application/java-archive", ".wav" : "audio/x-wav", ".wma" : "audio/x-ms-wma", ".wmv" : "video/x-ms-wmv", ".wmx" : "video/x-ms-wmx", ".wrl" : "model/vrml", ".wsdl" : "application/wsdl+xml", ".xbm" : "image/x-xbitmap", ".xhtml" : "application/xhtml+xml", ".xls" : "application/vnd.ms-excel", ".xml" : "application/xml", ".xpm" : "image/x-xpixmap", ".xsl" : "application/xml", ".xslt" : "application/xslt+xml", ".yaml" : "text/yaml", ".yml" : "text/yaml", ".zip" : "application/zip" }};
// Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
} next(err); return; }
// Serve the file directly using buffers fs.readFile(filename, function (err, data) { if (err) { next(err); return; } res.writeHead(200, { "Content-Type": Mime.type(filename), "Content-Length": data.length, "Last-Modified": stat.mtime.toUTCString(), // Cache in browser for 1 year "Cache-Control": "public max-age=" + 31536000 }); res.end(data); }); }); }
};
// Mini mime module for static file servingvar Mime = {
type: function getMime(path) { var index = path.lastIndexOf("."); if (index < 0) { return DEFAULT_MIME; } var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; return (/(text|javascript)/).test(type) ? type + "; charset=utf-8" : type; },
TYPES : { ".3gp" : "video/3gpp", ".a" : "application/octet-stream", ".ai" : "application/postscript", ".aif" : "audio/x-aiff", ".aiff" : "audio/x-aiff", ".asc" : "application/pgp-signature", ".asf" : "video/x-ms-asf", ".asm" : "text/x-asm", ".asx" : "video/x-ms-asf", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".avi" : "video/x-msvideo", ".bat" : "application/x-msdownload", ".bin" : "application/octet-stream",
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js ".flv" : "video/x-flv", ".for" : "text/x-fortran", ".gem" : "application/octet-stream", ".gemspec" : "text/x-script.ruby", ".gif" : "image/gif", ".gz" : "application/x-gzip", ".h" : "text/x-c", ".hh" : "text/x-c", ".htm" : "text/html", ".html" : "text/html", ".ico" : "image/vnd.microsoft.icon", ".ics" : "text/calendar", ".ifb" : "text/calendar", ".iso" : "application/octet-stream", ".jar" : "application/java-archive", ".java" : "text/x-java-source", ".jnlp" : "application/x-java-jnlp-file", ".jpeg" : "image/jpeg", ".jpg" : "image/jpeg", ".js" : "application/javascript", ".json" : "application/json", ".log" : "text/plain", ".m3u" : "audio/x-mpegurl", ".m4v" : "video/mp4", ".man" : "text/troff", ".mathml" : "application/mathml+xml", ".mbox" : "application/mbox", ".mdoc" : "text/troff", ".me" : "text/troff", ".mid" : "audio/midi", ".midi" : "audio/midi", ".mime" : "message/rfc822", ".mml" : "application/mathml+xml", ".mng" : "video/x-mng", ".mov" : "video/quicktime", ".mp3" : "audio/mpeg", ".mp4" : "video/mp4", ".mp4v" : "video/mp4", ".mpeg" : "video/mpeg", ".mpg" : "video/mpeg", ".ms" : "text/troff", ".msi" : "application/x-msdownload", ".odp" : "application/vnd.oasis.opendocument.presentation", ".ods" : "application/vnd.oasis.opendocument.spreadsheet", ".odt" : "application/vnd.oasis.opendocument.text", ".ogg" : "application/ogg", ".p" : "text/x-pascal", ".pas" : "text/x-pascal", ".pbm" : "image/x-portable-bitmap", ".pdf" : "application/pdf", ".pem" : "application/x-x509-ca-cert", ".pgm" : "image/x-portable-graymap", ".pgp" : "application/pgp-encrypted", ".pkg" : "application/octet-stream", ".pl" : "text/x-script.perl", ".pm" : "text/x-script.perl-module", ".png" : "image/png", ".pnm" : "image/x-portable-anymap", ".ppm" : "image/x-portable-pixmap", ".pps" : "application/vnd.ms-powerpoint", ".ppt" : "application/vnd.ms-powerpoint", ".ps" : "application/postscript", ".psd" : "image/vnd.adobe.photoshop", ".py" : "text/x-script.python", ".qt" : "video/quicktime", ".ra" : "audio/x-pn-realaudio", ".rake" : "text/x-script.ruby", ".ram" : "audio/x-pn-realaudio", ".rar" : "application/x-rar-compressed", ".rb" : "text/x-script.ruby", ".rdf" : "application/rdf+xml", ".roff" : "text/troff", ".rpm" : "application/x-redhat-package-manager", ".rss" : "application/rss+xml", ".rtf" : "application/rtf", ".ru" : "text/x-script.ruby", ".s" : "text/x-asm", ".sgm" : "text/sgml", ".sgml" : "text/sgml", ".sh" : "application/x-sh", ".sig" : "application/pgp-signature", ".snd" : "audio/basic", ".so" : "application/octet-stream", ".svg" : "image/svg+xml", ".svgz" : "image/svg+xml", ".swf" : "application/x-shockwave-flash", ".t" : "text/troff", ".tar" : "application/x-tar", ".tbz" : "application/x-bzip-compressed-tar", ".tci" : "application/x-topcloud", ".tcl" : "application/x-tcl", ".tex" : "application/x-tex", ".texi" : "application/x-texinfo", ".texinfo" : "application/x-texinfo", ".text" : "text/plain", ".tif" : "image/tiff", ".tiff" : "image/tiff", ".torrent" : "application/x-bittorrent", ".tr" : "text/troff", ".ttf" : "application/x-font-ttf", ".txt" : "text/plain", ".vcf" : "text/x-vcard", ".vcs" : "text/x-vcalendar", ".vrml" : "model/vrml", ".war" : "application/java-archive", ".wav" : "audio/x-wav", ".wma" : "audio/x-ms-wma", ".wmv" : "video/x-ms-wmv", ".wmx" : "video/x-ms-wmx", ".wrl" : "model/vrml", ".wsdl" : "application/wsdl+xml", ".xbm" : "image/x-xbitmap", ".xhtml" : "application/xhtml+xml", ".xls" : "application/vnd.ms-excel", ".xml" : "application/xml", ".xpm" : "image/x-xpixmap", ".xsl" : "application/xml", ".xslt" : "application/xslt+xml", ".yaml" : "text/yaml", ".yml" : "text/yaml", ".zip" : "application/zip" }};
// Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
} next(err); return; }
// Serve the file directly using buffers fs.readFile(filename, function (err, data) { if (err) { next(err); return; } res.writeHead(200, { "Content-Type": Mime.type(filename), "Content-Length": data.length, "Last-Modified": stat.mtime.toUTCString(), // Cache in browser for 1 year "Cache-Control": "public max-age=" + 31536000 }); res.end(data); }); }); }
".cc" : "text/x-c", ".chm" : "application/vnd.ms-htmlhelp", ".class" : "application/octet-stream", ".com" : "application/x-msdownload", ".conf" : "text/plain", ".cpp" : "text/x-c", ".crt" : "application/x-x509-ca-cert", ".css" : "text/css", ".csv" : "text/csv", ".cxx" : "text/x-c", ".deb" : "application/x-debian-package", ".der" : "application/x-x509-ca-cert", ".diff" : "text/x-diff", ".djv" : "image/vnd.djvu", ".djvu" : "image/vnd.djvu", ".dll" : "application/x-msdownload", ".dmg" : "application/octet-stream", ".doc" : "application/msword", ".dot" : "application/msword", ".dtd" : "application/xml-dtd", ".dvi" : "application/x-dvi", ".ear" : "application/java-archive", ".eml" : "message/rfc822", ".eps" : "application/postscript", ".exe" : "application/x-msdownload", ".f" : "text/x-fortran", ".f77" : "text/x-fortran", ".f90" : "text/x-fortran",
};
// Mini mime module for static file servingvar Mime = {
type: function getMime(path) { var index = path.lastIndexOf("."); if (index < 0) { return DEFAULT_MIME; } var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; return (/(text|javascript)/).test(type) ? type + "; charset=utf-8" : type; },
TYPES : { ".3gp" : "video/3gpp", ".a" : "application/octet-stream", ".ai" : "application/postscript", ".aif" : "audio/x-aiff", ".aiff" : "audio/x-aiff", ".asc" : "application/pgp-signature", ".asf" : "video/x-ms-asf", ".asm" : "text/x-asm", ".asx" : "video/x-ms-asf", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".avi" : "video/x-msvideo", ".bat" : "application/x-msdownload", ".bin" : "application/octet-stream",
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
static.js ".flv" : "video/x-flv", ".for" : "text/x-fortran", ".gem" : "application/octet-stream", ".gemspec" : "text/x-script.ruby", ".gif" : "image/gif", ".gz" : "application/x-gzip", ".h" : "text/x-c", ".hh" : "text/x-c", ".htm" : "text/html", ".html" : "text/html", ".ico" : "image/vnd.microsoft.icon", ".ics" : "text/calendar", ".ifb" : "text/calendar", ".iso" : "application/octet-stream", ".jar" : "application/java-archive", ".java" : "text/x-java-source", ".jnlp" : "application/x-java-jnlp-file", ".jpeg" : "image/jpeg", ".jpg" : "image/jpeg", ".js" : "application/javascript", ".json" : "application/json", ".log" : "text/plain", ".m3u" : "audio/x-mpegurl", ".m4v" : "video/mp4", ".man" : "text/troff", ".mathml" : "application/mathml+xml", ".mbox" : "application/mbox", ".mdoc" : "text/troff", ".me" : "text/troff", ".mid" : "audio/midi", ".midi" : "audio/midi", ".mime" : "message/rfc822", ".mml" : "application/mathml+xml", ".mng" : "video/x-mng", ".mov" : "video/quicktime", ".mp3" : "audio/mpeg", ".mp4" : "video/mp4", ".mp4v" : "video/mp4", ".mpeg" : "video/mpeg", ".mpg" : "video/mpeg", ".ms" : "text/troff", ".msi" : "application/x-msdownload", ".odp" : "application/vnd.oasis.opendocument.presentation", ".ods" : "application/vnd.oasis.opendocument.spreadsheet", ".odt" : "application/vnd.oasis.opendocument.text", ".ogg" : "application/ogg", ".p" : "text/x-pascal", ".pas" : "text/x-pascal", ".pbm" : "image/x-portable-bitmap", ".pdf" : "application/pdf", ".pem" : "application/x-x509-ca-cert", ".pgm" : "image/x-portable-graymap", ".pgp" : "application/pgp-encrypted", ".pkg" : "application/octet-stream", ".pl" : "text/x-script.perl", ".pm" : "text/x-script.perl-module", ".png" : "image/png", ".pnm" : "image/x-portable-anymap", ".ppm" : "image/x-portable-pixmap", ".pps" : "application/vnd.ms-powerpoint", ".ppt" : "application/vnd.ms-powerpoint", ".ps" : "application/postscript", ".psd" : "image/vnd.adobe.photoshop", ".py" : "text/x-script.python", ".qt" : "video/quicktime", ".ra" : "audio/x-pn-realaudio", ".rake" : "text/x-script.ruby", ".ram" : "audio/x-pn-realaudio", ".rar" : "application/x-rar-compressed", ".rb" : "text/x-script.ruby", ".rdf" : "application/rdf+xml", ".roff" : "text/troff", ".rpm" : "application/x-redhat-package-manager", ".rss" : "application/rss+xml", ".rtf" : "application/rtf", ".ru" : "text/x-script.ruby", ".s" : "text/x-asm", ".sgm" : "text/sgml", ".sgml" : "text/sgml", ".sh" : "application/x-sh", ".sig" : "application/pgp-signature", ".snd" : "audio/basic", ".so" : "application/octet-stream", ".svg" : "image/svg+xml", ".svgz" : "image/svg+xml", ".swf" : "application/x-shockwave-flash", ".t" : "text/troff", ".tar" : "application/x-tar", ".tbz" : "application/x-bzip-compressed-tar", ".tci" : "application/x-topcloud", ".tcl" : "application/x-tcl", ".tex" : "application/x-tex", ".texi" : "application/x-texinfo", ".texinfo" : "application/x-texinfo", ".text" : "text/plain", ".tif" : "image/tiff", ".tiff" : "image/tiff", ".torrent" : "application/x-bittorrent", ".tr" : "text/troff", ".ttf" : "application/x-font-ttf", ".txt" : "text/plain", ".vcf" : "text/x-vcard", ".vcs" : "text/x-vcalendar", ".vrml" : "model/vrml", ".war" : "application/java-archive", ".wav" : "audio/x-wav", ".wma" : "audio/x-ms-wma", ".wmv" : "video/x-ms-wmv", ".wmx" : "video/x-ms-wmx", ".wrl" : "model/vrml", ".wsdl" : "application/wsdl+xml", ".xbm" : "image/x-xbitmap", ".xhtml" : "application/xhtml+xml", ".xls" : "application/vnd.ms-excel", ".xml" : "application/xml", ".xpm" : "image/x-xpixmap", ".xsl" : "application/xml", ".xslt" : "application/xslt+xml", ".yaml" : "text/yaml", ".yml" : "text/yaml", ".zip" : "application/zip" }};
// Buffer any events that fire while waiting on the stat. var events = []; function onData() { events.push(["data"].concat(Array.prototype.slice.call(arguments))); } function onEnd() { events.push(["end"].concat(Array.prototype.slice.call(arguments))); } req.addListener("data", onData); req.addListener("end", onEnd);
fs.stat(filename, function (err, stat) {
// Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd);
// Fall through for missing files, thow error for other problems if (err) { if (err.errno === process.ENOENT) { next(); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); }); return;
} next(err); return; }
// Serve the file directly using buffers fs.readFile(filename, function (err, data) { if (err) { next(err); return; } res.writeHead(200, { "Content-Type": Mime.type(filename), "Content-Length": data.length, "Last-Modified": stat.mtime.toUTCString(), // Cache in browser for 1 year "Cache-Control": "public max-age=" + 31536000 }); res.end(data); }); }); }
".bmp" : "image/bmp", ".bz2" : "application/x-bzip2", ".c" : "text/x-c", ".cab" : "application/vnd.ms-cab-compressed",
".cc" : "text/x-c", ".chm" : "application/vnd.ms-htmlhelp", ".class" : "application/octet-stream", ".com" : "application/x-msdownload", ".conf" : "text/plain", ".cpp" : "text/x-c", ".crt" : "application/x-x509-ca-cert", ".css" : "text/css", ".csv" : "text/csv", ".cxx" : "text/x-c", ".deb" : "application/x-debian-package", ".der" : "application/x-x509-ca-cert", ".diff" : "text/x-diff", ".djv" : "image/vnd.djvu", ".djvu" : "image/vnd.djvu", ".dll" : "application/x-msdownload", ".dmg" : "application/octet-stream", ".doc" : "application/msword", ".dot" : "application/msword", ".dtd" : "application/xml-dtd", ".dvi" : "application/x-dvi", ".ear" : "application/java-archive", ".eml" : "message/rfc822", ".eps" : "application/postscript", ".exe" : "application/x-msdownload", ".f" : "text/x-fortran", ".f77" : "text/x-fortran", ".f90" : "text/x-fortran",
};
// Mini mime module for static file servingvar Mime = {
type: function getMime(path) { var index = path.lastIndexOf("."); if (index < 0) { return DEFAULT_MIME; } var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; return (/(text|javascript)/).test(type) ? type + "; charset=utf-8" : type; },
TYPES : { ".3gp" : "video/3gpp", ".a" : "application/octet-stream", ".ai" : "application/postscript", ".aif" : "audio/x-aiff", ".aiff" : "audio/x-aiff", ".asc" : "application/pgp-signature", ".asf" : "video/x-ms-asf", ".asm" : "text/x-asm", ".asx" : "video/x-ms-asf", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".avi" : "video/x-msvideo", ".bat" : "application/x-msdownload", ".bin" : "application/octet-stream",
var fs = require('fs'), Url = require('url'), Path = require('path');
var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime
var DEFAULT_MIME = 'application/octet-stream';
module.exports = {
setup: function (env) { this.root = this.root || process.cwd(); },
handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url);
var pathname = url.pathname.replace(/\.\.+/g, '.'), filename = Path.join(this.root, pathname);
if (filename[filename.length - 1] === "/") { filename += "index.html"; }
Saturday, June 5, 2010
• Authentication
• Authorization
• Body Decoder
• Cache
• Conditional Get
• Debug
• Error Handler
• Gzip
• Log
• Method Override
• Response Time
• Session
Built-in Filter Modules
Saturday, June 5, 2010
• Static
• Rest
• Router
• PubSub
• Cache Manifest
• Direct
• JSON-RPC
• More...
Built-in Data Providers
Saturday, June 5, 2010
Raphaël JSRaphaël is a small JavaScript library that should simplify your work with vector graphics on the web.
Saturday, June 5, 2010
Multi-Touch Vectors
http://vimeo.com/11610421
Saturday, June 5, 2010
multitouch-demo.jswindow.onload = function () { var R = Raphael(0, 0, "100%", "100%"), r = R.circle(100, 100, 50), g = R.circle(210, 100, 50), b = R.circle(320, 100, 50), p = R.circle(430, 100, 50); var start = function () { this.ox = this.attr("cx"); this.oy = this.attr("cy"); this.animate({r: 70, opacity: .25}, 500, ">"); }, move = function (dx, dy) { this.attr({cx: this.ox + dx, cy: this.oy + dy}); }, up = function () { this.animate({r: 50, opacity: .5}, 500, ">"); }; R.set(r, g, b, p).drag(move, start, up);};
Saturday, June 5, 2010
Creating Shapesvar R = Raphael(0, 0, "100%", "100%"), r = R.circle(100, 100, 50) .attr({fill: "hsb(0, 1, 1)"}), g = R.circle(210, 100, 50) .attr({fill: "hsb(.3, 1, 1)"}), b = R.circle(320, 100, 50) .attr({fill: "hsb(.6, 1, 1)"}), p = R.circle(430, 100, 50) .attr({fill: "hsb(.8, 1, 1)"});
Saturday, June 5, 2010
Attaching Eventsfunction start() { this.ox = this.attr("cx"); this.oy = this.attr("cy"); this.animate({r: 70, opacity: .25}, 500, ">");}function move(dx, dy) { this.attr({cx: this.ox + dx, cy: this.oy + dy});}function up() { this.animate({r: 50, opacity: .5}, 500, ">");}R.set(r, g, b, p).drag(move, start, up);
Saturday, June 5, 2010
Let’s combine them!
Saturday, June 5, 2010
• Serving static assets (HTML, CSS, JS)
• Live Interaction (Pub Sub)
• Performance Tweaks (Cache, Gzip)
• Offline Mode (Cache Manifest)
• HTTP Request Logging
Saturday, June 5, 2010
app.js (stack)require.paths.unshift("./lib");var Connect = require('connect');var root = __dirname + "/public";
module.exports = Connect.createServer([ {filter: "log"}, {filter: "body-decoder"}, {provider: "pubsub", route: "/stream", logic: Backend}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root}]);
Saturday, June 5, 2010
app.js (Backend)var Backend = { subscribe: function (subscriber) { if (subscribers.indexOf(subscriber) < 0) { subscribers.push(subscriber); } }, unsubscribe: function (subscriber) { var pos = subscribers.indexOf(subscriber); if (pos >= 0) { subscribers.slice(pos); } }, publish: function (message, callback) { subscribers.forEach(function (subscriber) { subscriber.send(message); }); callback(); }};
Saturday, June 5, 2010
index.html<!DOCTYPE html><html lang="en" manifest="cache.manifest"><head> <meta charset="utf-8" /> <meta name="apple-mobile-web-app-capable" content="yes"> <title>Node + Raphaël</title> <link rel="stylesheet" href="style.css" type="text/css" /> <script src="raphael.js"></script> <script src="client.js"></script></head><body> <div id="holder"></div> </body></html>
Saturday, June 5, 2010
Demo Time!
Saturday, June 5, 2010
http://github.com/extjs/connect
http://twitter.com/creationix
http://raphaeljs.com
http://nodejs.org
Saturday, June 5, 2010
AnyQuestions
?
Saturday, June 5, 2010