Commits

Nikhil Marathe committed 1a8e418

Initial and probably final :) import

Comments (0)

Files changed (16)

+ Copyright (c) 2010 Nikhil Marathe <nsm.nikhil@gmail.com>
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+-*- markdown -*-
+
+# Snip #
+
+Snip is a simple pastebin. It was written as the example for an article on [How To Node](http://howtonode.org).
+
+## Prerequisites ##
+
+* [node](http://github.com/ry/node)
+* [Redis](http://code.google.com/p/redis)
+* [Pygments](http://pygments.org)
+
+It also uses the following, but they are included in the source:
+
+* [redis-node-client](http://github.com/fictorial/redis-node-client)
+* [nerve](http://github.com/gjritter/nerve)
+* [underscore.js](http://github.com/documentcloud/underscore)
+
+## Running ##
+
+Start Redis on default port and localhost, do:
+
+    ./run snip.js
+
+Visit http://localhost:8000 and add snippets.
+

deps/nerve/ACKNOWLEDGEMENTS.md

+# Acknowledgements
+
+Thanks to:
+
+Ryan Dahl for building node.js, my favorite new plaything.
+
+Gianni Chiappetta and Noah Sloan, for encouraging me to write good code
+instead of cute code.
+
+Micheil Smith, for the examples of cookies and sessions with node.js, and for
+being patient with my mangling of his name.
+
+Chad Etzel, for writing the example that shows off Nerve with
+[template.node.js](http://github.com/jazzychad/template.node.js).
+
+Everyone else involved in the creation of node.js and the many other projects
+that have sprung up around it.

deps/nerve/LICENSE.md

+# License
+Copyright (c) 2009 Codespin Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

deps/nerve/README.md

+# Nerve
+
+A microframework for [node.js](http://nodejs.org).
+
+## Features
+
+* Simple array-based DSL for request routing
+* Regular expression route matching, including passing of captured groups to route handler
+* Simple cookie and session support
+
+## Examples
+
+### Hello World
+
+This Hello World app listens for http requests on port 8000:
+
+    var nerve = require('./nerve');
+
+    var hello = [
+    	["/", function(req, res) {
+    		res.respond("Hello, World!");
+    	}]
+    ];
+
+    nerve.create(hello).listen(8000);
+
+### Nodewiki
+
+[Nodewiki](http://github.com/gjritter/nodewiki) is a tiny wiki built using Nerve and the redis-node-client.
+
+### Templates
+
+The [template.node.js](http://github.com/jazzychad/template.node.js) project includes a sample application that shows how Nerve can be used to build a web application with templates.
+
+### Sample Application
+
+This sample application makes use of Nerve's regular-expression URI path matching to pass a "name" parameter from the URI into a handler function. This can be extended to any number of named arguments in the handler function.
+
+It also makes use of request method matching. The first matcher will only match get requests; the second will match any request method.
+
+The application stores the user's name in the session, so that it can be used in subsequent responses.
+
+    var posix = require("./posix");
+    var nerve = require("./nerve"),
+      get = nerve.get;
+
+    // define an application using request matcher/handler pairs
+    var app = [
+
+    	// this handler will only respond to GET requests
+    	[get(/^\/hello\/(\w+)$/), function(req, res, name) {
+		
+    		// the session is available on every request; it currently
+    		// lasts for the browser session, but will soon be configurable.
+    		req.session["name"] = name;
+		
+    		// respond takes a string and provides sensible defaults:
+    		// Content-Type: text/html, Content-Length: string length
+    		res.respond("Hello, " + name + "!");
+		
+    	}],
+	
+    	// this handler will respond to any request method
+    	[/^\/goodbye$/, function(req, res) {
+		
+    		var name = req.session["name"];
+    		var message = "Goodbye, " + (name || "I hardly knew thee") + "!";
+
+    		// respond takes an object specifying content and headers,
+    		// and uses sensible defaults if not supplied
+    		res.respond({content: message, headers: {"Content-Type": "text/plain"}});
+		
+    	}]
+	
+    ];
+
+    // create and serve the application with 10 second session duration
+    nerve.create(app, {session_duration: 10000}).listen(8000);

deps/nerve/TODO.md

+# To Do List
+
+* emit events where appropriate (new session, session expiration, ...)
+* create a test suite
+* break up the example in the README into digestible chunks
+* run jslint and clean up code where appropriate

deps/nerve/lib/http_state.js

+/*global require, process, setInterval */
+'use strict';
+
+(function () {
+	var http = require('http'),
+		idgen = require('./idgen'),
+		sessions = {};
+
+	process.mixin(http.IncomingMessage.prototype, {
+		get_cookie: function (name) {
+			var cookies = this.headers.cookie && this.headers.cookie.split(";"),
+				cookie, parts;
+			while ((cookie = (cookies && cookies.shift()))) {
+				parts = cookie.split("=");
+				if (parts[0].trim() === name) {
+					return parts[1];
+				}
+			}
+		},
+		get_or_create_session: function (req, res, options) {
+			var session_id = req.get_cookie("session_id");
+			if (!session_id) {
+				session_id = idgen.generate_id(22);
+				res.set_cookie("session_id", session_id);
+			}
+			sessions[session_id] = (sessions[session_id] || {
+				session: {session_id: session_id},
+				touch: function () {
+					this.expiration = (+ new Date()) + (options.duration || 30 * 60 * 1000);
+					return this;
+				}
+			}).touch();
+			return sessions[session_id].session;
+		}
+	});
+
+	process.mixin(http.ServerResponse.prototype, {
+		set_cookie: function (name, value) {
+			this.cookies = this.cookies || [];
+			this.cookies.push(name + "=" + value + "; path=/;");
+		}
+	});
+
+	function cleanup_sessions() {
+		for (var session_id in sessions) {
+			if ((+ new Date()) > sessions[session_id].expiration) {
+				delete sessions[session_id];
+			}
+		}
+	}
+	
+	setInterval(cleanup_sessions, 1000);
+
+	for(var n in http) {
+		exports[n] = http[n];
+	}
+}());

deps/nerve/lib/idgen.js

+/*global exports */
+'use strict';
+
+(function () {
+	// the "URL and Filename safe" Base 64 Alphabet
+	// see http://tools.ietf.org/html/rfc3548#section-4
+	var id_characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
+
+	exports.generate_id = function (length) {
+		var id = '', i;
+		for (i = 0; i < length; i += 1) {
+			// choose a base 64 character using the low 6 bits of a random value
+			// id += id_characters[0x3f & Math.floor(Math.random() * 0x100000000)];
+			id += id_characters[Math.floor(Math.random() * id_characters.length)];
+		}
+		return id;
+	};
+}());

deps/nerve/lib/mime.js

+/*global require, exports */
+'use strict';
+
+(function () {
+	var path = require('path'),
+		// map of mime types, originally from Express
+		// modified to work with values returned by require('path').extname(pathname)
+		// Express - Mime - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
+		ext_to_type = {
+			'.323'     : 'text/h323',
+			'.3gp'     : 'video/3gpp',
+			'.a'       : 'application/octet-stream',
+			'.acx'     : 'application/internet-property-stream',
+			'.ai'      : 'application/postscript',
+			'.aif'     : 'audio/x-aiff',
+			'.aifc'    : 'audio/x-aiff',
+			'.aiff'    : 'audio/x-aiff',
+			'.asc'     : 'application/pgp-signature',
+			'.asf'     : 'video/x-ms-asf',
+			'.asr'     : 'video/x-ms-asf',
+			'.asm'     : 'text/x-asm',
+			'.asx'     : 'video/x-ms-asf',
+			'.atom'    : 'application/atom+xml',
+			'.au'      : 'audio/basic',
+			'.avi'     : 'video/x-msvideo',
+			'.axs'     : 'application/olescript',
+			'.bas'     : 'text/plain',
+			'.bat'     : 'application/x-msdownload',
+			'.bcpio'   : 'application/x-bcpio',
+			'.bin'     : 'application/octet-stream',
+			'.bmp'     : 'image/bmp',
+			'.bz2'     : 'application/x-bzip2',
+			'.c'       : 'text/x-c',
+			'.cab'     : 'application/vnd.ms-cab-compressed',
+			'.cat'     : 'application/vnd.ms-pkiseccat',
+			'.cc'      : 'text/x-c',
+			'.cdf'     : 'application/x-netcdf',
+			'.cer'     : 'application/x-x509-ca-cert',
+			'.cgm'     : 'image/cgm',
+			'.chm'     : 'application/vnd.ms-htmlhelp',
+			'.class'   : 'application/octet-stream',
+			'.clp'     : 'application/x-msclip',
+			'.cmx'     : 'image/x-cmx',
+			'.cod'     : 'image/cis-cod',
+			'.com'     : 'application/x-msdownload',
+			'.conf'    : 'text/plain',
+			'.cpio'    : 'application/x-cpio',
+			'.cpp'     : 'text/x-c',
+			'.cpt'     : 'application/mac-compactpro',
+			'.crd'     : 'application/x-mscardfile',
+			'.crl'     : 'application/pkix-crl',
+			'.crt'     : 'application/x-x509-ca-cert',
+			'.csh'     : 'application/x-csh',
+			'.css'     : 'text/css',
+			'.csv'     : 'text/csv',
+			'.cxx'     : 'text/x-c',
+			'.dcr'     : 'application/x-director',
+			'.deb'     : 'application/x-debian-package',
+			'.der'     : 'application/x-x509-ca-cert',
+			'.diff'    : 'text/x-diff',
+			'.dir'     : 'application/x-director',
+			'.djv'     : 'image/vnd.djvu',
+			'.djvu'    : 'image/vnd.djvu',
+			'.dll'     : 'application/x-msdownload',
+			'.dmg'     : 'application/octet-stream',
+			'.dms'     : 'application/octet-stream',
+			'.doc'     : 'application/msword',
+			'.dot'     : 'application/msword',
+			'.dtd'     : 'application/xml-dtd',
+			'.dv'      : 'video/x-dv',
+			'.dvi'     : 'application/x-dvi',
+			'.dxr'     : 'application/x-director',
+			'.ear'     : 'application/java-archive',
+			'.eml'     : 'message/rfc822',
+			'.eps'     : 'application/postscript',
+			'.etx'     : 'text/x-setext',
+			'.evy'     : 'application/envoy',
+			'.exe'     : 'application/x-msdownload',
+			'.ez'      : 'application/andrew-inset',
+			'.f'       : 'text/x-fortran',
+			'.f77'     : 'text/x-fortran',
+			'.f90'     : 'text/x-fortran',
+			'.fif'     : 'application/fractals',
+			'.flr'     : 'x-world/x-vrml',
+			'.flv'     : 'video/x-flv',
+			'.for'     : 'text/x-fortran',
+			'.gem'     : 'application/octet-stream',
+			'.gemspec' : 'text/x-script.ruby',
+			'.gif'     : 'image/gif',
+			'.gram'    : 'application/srgs',
+			'.grxml'   : 'application/srgs+xml',
+			'.gtar'    : 'application/x-gtar',
+			'.gz'      : 'application/x-gzip',
+			'.h'       : 'text/x-c',
+			'.hdf'     : 'application/x-hdf',
+			'.hh'      : 'text/x-c',
+			'.hlp'     : 'application/winhlp',
+			'.hqx'     : 'application/mac-binhex40',
+			'.hta'     : 'application/hta',
+			'.htc'     : 'text/x-component',
+			'.htm'     : 'text/html',
+			'.html'    : 'text/html',
+			'.htt'     : 'text/webviewhtml',
+			'.ice'     : 'x-conference/x-cooltalk',
+			'.ico'     : 'image/vnd.microsoft.icon',
+			'.ics'     : 'text/calendar',
+			'.ief'     : 'image/ief',
+			'.ifb'     : 'text/calendar',
+			'.iges'    : 'model/iges',
+			'.igs'     : 'model/iges',
+			'.iii'     : 'application/x-iphone',
+			'.ins'     : 'application/x-internet-signup',
+			'.isp'     : 'application/x-internet-signup',
+			'.iso'     : 'application/octet-stream',
+			'.jar'     : 'application/java-archive',
+			'.java'    : 'text/x-java-source',
+			'.jfif'    : 'image/pipeg',
+			'.jnlp'    : 'application/x-java-jnlp-file',
+			'.jp2'     : 'image/jp2',
+			'.jpe'     : 'image/jpeg',
+			'.jpeg'    : 'image/jpeg',
+			'.jpg'     : 'image/jpeg',
+			'.js'      : 'application/javascript',
+			'.json'    : 'application/json',
+			'.kar'     : 'audio/midi',
+			'.latex'   : 'application/x-latex',
+			'.lha'     : 'application/octet-stream',
+			'.lsf'     : 'video/x-la-asf',
+			'.lsx'     : 'video/x-la-asf',
+			'.lzh'     : 'application/octet-stream',
+			'.log'     : 'text/plain',
+			'.m13'     : 'application/x-msmediaview',
+			'.m14'     : 'application/x-msmediaview',
+			'.m3u'     : 'audio/x-mpegurl',
+			'.m4a'     : 'audio/mp4a-latm',
+			'.m4b'     : 'audio/mp4a-latm',
+			'.m4p'     : 'audio/mp4a-latm',
+			'.m4u'     : 'video/vnd.mpegurl',
+			'.m4v'     : 'video/mp4',
+			'.mac'     : 'image/x-macpaint',
+			'.man'     : 'text/troff',
+			'.mathml'  : 'application/mathml+xml',
+			'.mbox'    : 'application/mbox',
+			'.mdb'     : 'application/x-msaccess',
+			'.mdoc'    : 'text/troff',
+			'.me'      : 'text/troff',
+			'.mesh'    : 'model/mesh',
+			'.mht'     : 'message/rfc822',
+			'.mhtml'   : 'message/rfc822',
+			'.mid'     : 'audio/midi',
+			'.midi'    : 'audio/midi',
+			'.mif'     : 'application/vnd.mif',
+			'.mime'    : 'message/rfc822',
+			'.mml'     : 'application/mathml+xml',
+			'.mng'     : 'video/x-mng',
+			'.mny'     : 'application/x-msmoney',
+			'.mov'     : 'video/quicktime',
+			'.movie'   : 'video/x-sgi-movie',
+			'.mp2'     : 'video/mpeg',
+			'.mp3'     : 'audio/mpeg',
+			'.mp4'     : 'video/mp4',
+			'.mp4v'    : 'video/mp4',
+			'.mpa'     : 'video/mpeg',
+			'.mpe'     : 'video/mpeg',
+			'.mpeg'    : 'video/mpeg',
+			'.mpg'     : 'video/mpeg',
+			'.mpga'    : 'audio/mpeg',
+			'.mpp'     : 'application/vnd.ms-project',
+			'.mpv2'    : 'video/mpeg',
+			'.ms'      : 'text/troff',
+			'.msh'     : 'model/mesh',
+			'.msi'     : 'application/x-msdownload',
+			'.mvb'     : 'application/x-msmediaview',
+			'.mxu'     : 'video/vnd.mpegurl',
+			'.nc'      : 'application/x-netcdf',
+			'.nws'     : 'message/rfc822',
+			'.oda'     : 'application/oda',
+			'.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',
+			'.p10'     : 'application/pkcs10',
+			'.p12'     : 'application/x-pkcs12',
+			'.p7b'     : 'application/x-pkcs7-certificates',
+			'.p7c'     : 'application/x-pkcs7-mime',
+			'.p7m'     : 'application/x-pkcs7-mime',
+			'.p7r'     : 'application/x-pkcs7-certreqresp',
+			'.p7s'     : 'application/x-pkcs7-signature',
+			'.pas'     : 'text/x-pascal',
+			'.pbm'     : 'image/x-portable-bitmap',
+			'.pct'     : 'image/pict',
+			'.pdb'     : 'chemical/x-pdb',
+			'.pdf'     : 'application/pdf',
+			'.pem'     : 'application/x-x509-ca-cert',
+			'.pfx'     : 'application/x-pkcs12',
+			'.pgm'     : 'image/x-portable-graymap',
+			'.pgn'     : 'application/x-chess-pgn',
+			'.pgp'     : 'application/pgp-encrypted',
+			'.pic'     : 'image/pict',
+			'.pict'    : 'image/pict',
+			'.pkg'     : 'application/octet-stream',
+			'.pko'     : 'application/ynd.ms-pkipko',
+			'.pl'      : 'text/x-script.perl',
+			'.pm'      : 'text/x-script.perl-module',
+			'.pma'     : 'application/x-perfmon',
+			'.pmc'     : 'application/x-perfmon',
+			'.pml'     : 'application/x-perfmon',
+			'.pmr'     : 'application/x-perfmon',
+			'.pmw'     : 'application/x-perfmon',
+			'.png'     : 'image/png',
+			'.pnm'     : 'image/x-portable-anymap',
+			'.pnt'     : 'image/x-macpaint',
+			'.pntg'    : 'image/x-macpaint',
+			'.pot'     : 'application/vnd.ms-powerpoint',
+			'.ppm'     : 'image/x-portable-pixmap',
+			'.pps'     : 'application/vnd.ms-powerpoint',
+			'.ppt'     : 'application/vnd.ms-powerpoint',
+			'.prf'     : 'application/pics-rules',
+			'.ps'      : 'application/postscript',
+			'.psd'     : 'image/vnd.adobe.photoshop',
+			'.pub'     : 'application/x-mspublisher',
+			'.py'      : 'text/x-script.python',
+			'.qt'      : 'video/quicktime',
+			'.qti'     : 'image/x-quicktime',
+			'.qtif'    : 'image/x-quicktime',
+			'.ra'      : 'audio/x-pn-realaudio',
+			'.rake'    : 'text/x-script.ruby',
+			'.ram'     : 'audio/x-pn-realaudio',
+			'.rar'     : 'application/x-rar-compressed',
+			'.ras'     : 'image/x-cmu-raster',
+			'.rb'      : 'text/x-script.ruby',
+			'.rdf'     : 'application/rdf+xml',
+			'.rgb'     : 'image/x-rgb',
+			'.rm'      : 'application/vnd.rn-realmedia',
+			'.rmi'     : 'audio/mid',
+			'.roff'    : 'text/troff',
+			'.rpm'     : 'application/x-redhat-package-manager',
+			'.rss'     : 'application/rss+xml',
+			'.rtf'     : 'application/rtf',
+			'.rtx'     : 'text/richtext',
+			'.ru'      : 'text/x-script.ruby',
+			'.s'       : 'text/x-asm',
+			'.scd'     : 'application/x-msschedule',
+			'.sct'     : 'text/scriptlet',
+			'.setpay'  : 'application/set-payment-initiation',
+			'.setreg'  : 'application/set-registration-initiation',
+			'.sgm'     : 'text/sgml',
+			'.sgml'    : 'text/sgml',
+			'.sh'      : 'application/x-sh',
+			'.shar'    : 'application/x-shar',
+			'.sig'     : 'application/pgp-signature',
+			'.silo'    : 'model/mesh',
+			'.sit'     : 'application/x-stuffit',
+			'.skd'     : 'application/x-koan',
+			'.skm'     : 'application/x-koan',
+			'.skp'     : 'application/x-koan',
+			'.skt'     : 'application/x-koan',
+			'.smi'     : 'application/smil',
+			'.smil'    : 'application/smil',
+			'.snd'     : 'audio/basic',
+			'.so'      : 'application/octet-stream',
+			'.spc'     : 'application/x-pkcs7-certificates',
+			'.spl'     : 'application/x-futuresplash',
+			'.src'     : 'application/x-wais-source',
+			'.sst'     : 'application/vnd.ms-pkicertstore',
+			'.stl'     : 'application/vnd.ms-pkistl',
+			'.stm'     : 'text/html',
+			'.sv4cpio' : 'application/x-sv4cpio',
+			'.sv4crc'  : 'application/x-sv4crc',
+			'.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',
+			'.tcl'     : 'application/x-tcl',
+			'.tex'     : 'application/x-tex',
+			'.texi'    : 'application/x-texinfo',
+			'.texinfo' : 'application/x-texinfo',
+			'.text'    : 'text/plain',
+			'.tgz'     : 'application/x-compressed',
+			'.tif'     : 'image/tiff',
+			'.tiff'    : 'image/tiff',
+			'.torrent' : 'application/x-bittorrent',
+			'.tr'      : 'text/troff',
+			'.trm'     : 'application/x-msterminal',
+			'.tsv'     : 'text/tab-seperated-values',
+			'.txt'     : 'text/plain',
+			'.uls'     : 'text/iuls',
+			'.ustar'   : 'application/x-ustar',
+			'.vcd'     : 'application/x-cdlink',
+			'.vcf'     : 'text/x-vcard',
+			'.vcs'     : 'text/x-vcalendar',
+			'.vrml'    : 'model/vrml',
+			'.vxml'    : 'application/voicexml+xml',
+			'.war'     : 'application/java-archive',
+			'.wav'     : 'audio/x-wav',
+			'.wbmp'    : 'image/vnd.wap.wbmp',
+			'.wbxml'   : 'application/vnd.wap.wbxml',
+			'.wcm'     : 'application/vnd.ms-works',
+			'.wdb'     : 'application/vnd.ms-works',
+			'.wks'     : 'application/vnd.ms-works',
+			'.wma'     : 'audio/x-ms-wma',
+			'.wmf'     : 'application/x-msmetafile',
+			'.wml'     : 'text/vnd.wap.wml',
+			'.wmls'    : 'text/vnd.wap.wmlscript',
+			'.wmlsc'   : 'application/vnd.wap.wmlscriptc',
+			'.wmv'     : 'video/x-ms-wmv',
+			'.wmx'     : 'video/x-ms-wmx',
+			'.wps'     : 'application/vnd.ms-works',
+			'.wri'     : 'application/x-mswrite',
+			'.wrl'     : 'model/vrml',
+			'.wrz'     : 'x-world/x-vrml',
+			'.wsdl'    : 'application/wsdl+xml',
+			'.xaf'     : 'x-world/x-vrml',
+			'.xbm'     : 'image/x-xbitmap',
+			'.xht'     : 'application/xhtml+xml',
+			'.xhtml'   : 'application/xhtml+xml',
+			'.xla'     : 'application/vnd.ms-excel',
+			'.xlc'     : 'application/vnd.ms-excel',
+			'.xlm'     : 'application/vnd.ms-excel',
+			'.xls'     : 'application/vnd.ms-excel',
+			'.xlt'     : 'application/vnd.ms-excel',
+			'.xml'     : 'application/xml',
+			'.xof'     : 'x-world/x-vrml',
+			'.xpm'     : 'image/x-xpixmap',
+			'.xsl'     : 'application/xml',
+			'.xslt'    : 'application/xslt+xml',
+			'.xul'     : 'application/vnd.mozilla.xul+xml',
+			'.xwd'     : 'image/x-xwindowdump',
+			'.xyz'     : 'chemical/x-xyz',
+			'.yaml'    : 'text/yaml',
+			'.yml'     : 'text/yaml',
+			'.z'       : 'application/x-compress',
+			'.zip'     : 'application/zip'
+		};
+	
+	function mime_type(pathname, default_type) {
+		return ext_to_type[path.extname(pathname)] || default_type || 'application/octet-stream';
+	}
+	
+	exports.mime_type = mime_type;
+}());

deps/nerve/lib/nerve.js

+/*global require, process, exports */
+'use strict';
+
+(function () {
+	var sys = require('sys'),
+		http = require('./http_state'),
+		url = require('url'),
+		path = require('path'),
+		fs = require('fs'),
+		mime = require('./mime');
+
+	process.mixin(http.ServerResponse.prototype, {
+		respond: function (response_data) {
+			var headers = {
+				'Content-Type': 'text/html',
+				'Content-Length': (response_data.content && response_data.content.length) || response_data.length || 0
+			}, name;
+			if (this.cookies) {
+				headers['Set-Cookie'] = this.cookies.join(', ');
+			}
+			for (name in response_data.headers) {
+				if (response_data.headers.hasOwnProperty(name)) {
+					headers[name] = response_data.headers[name];
+				}
+			}
+			this.sendHeader(response_data.status_code || 200, headers);
+			this.write(response_data.content || response_data, 'binary');
+			this.close();
+		}
+	});
+	
+	function match_request(matcher, req) {
+		if (typeof matcher === 'string') {
+			return (matcher === req.url);
+		} else if (matcher.constructor === RegExp) {
+			return req.url.match(matcher);
+		} else {
+			return req.url.match(matcher.apply(req));
+		}
+	}
+	
+	function to_regexp(pattern) {
+		if (pattern.constructor === RegExp) {
+			return pattern;
+		} else {
+			return new RegExp('^' + pattern + '$');
+		}
+	}
+	
+	function get(pattern) {
+		return function () {
+			if (this.method !== 'GET') {
+				return false;
+			} else {
+				return to_regexp(pattern);
+			}
+		};
+	}
+
+	function post(pattern) {
+		return function () {
+			if (this.method !== 'POST') {
+				return false;
+			} else {
+				return to_regexp(pattern);
+			}
+		};
+	}
+
+	function put(pattern) {
+		return function () {
+			if (this.method !== 'PUT') {
+				return false;
+			} else {
+				return to_regexp(pattern);
+			}
+		};
+	}
+
+	function del(pattern) {
+		return function () {
+			if (this.method !== 'DELETE') {
+				return false;
+			} else {
+				return to_regexp(pattern);
+			}
+		};
+	}
+
+	function serve_static_file(pathname, res) {
+		path.exists(pathname, function (exists) {
+			if (exists) {
+				fs.readFile(pathname, 'binary').addCallback(function (content) {
+					res.respond({content: content, headers: {'Content-Type': mime.mime_type(pathname)}});
+				}).addErrback(function (e) {
+					res.respond({content: '<html><head><title>Exception</title></head><body><h1>Exception</h1><pre>' + sys.inspect(e) + '</pre></body></html>', status_code: 501});
+				});
+			} else {
+				res.respond({content: '<html><head><title>Not Found</title></head><body><h1>Not Found</h1></body></html>', status_code: 404});
+			}
+		});
+	}
+
+	function create(app, options) {
+		options = options || {};
+		function request_handler(req, res) {
+			var matcher, handler, handler_args, match, pathname;
+			req.session = req.get_or_create_session(req, res, {duration: options.session_duration || 30 * 60 * 1000});
+			for (var i = 0; i < app.length; i += 1) {
+				matcher = app[i][0];
+				handler = app[i][1];
+				handler_args = [req, res];
+				match = match_request(matcher, req);
+				if (match) {
+					try {
+						if (typeof match.slice === 'function') {
+							handler_args = handler_args.concat(match.slice(1));
+						}
+						handler.apply(null, handler_args);
+					} catch (e) {
+						res.respond({content: '<html><head><title>Exception</title></head><body><h1>Exception</h1><pre>' + sys.inspect(e) + '</pre></body></html>', status_code: 501});
+					}
+					return;
+				}
+			}
+			// no matching handler found; check for file if document_root option provided
+			if(options.document_root) {
+				pathname = options.document_root + unescape(url.parse(req.url).pathname).replace(/\.{2,}\//g, './');
+				serve_static_file(pathname, res);
+			} else {
+				res.respond({content: '<html><head><title>Not Found</title></head><body><h1>Not Found</h1></body></html>', status_code: 404});
+			}
+		}
+
+		return http.createServer(request_handler);
+	}
+	
+	exports.get = get;
+	exports.post = post;
+	exports.put = put;
+	exports.del = del;
+	exports.create = create;
+}());

deps/redis-node-client/LICENSE

+Copyright (c) 2009 Fictorial LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

deps/redis-node-client/README.md

+# redis-node-client
+
+A Redis client implementation for Node.js which runs atop Google V8.
+
+This project lets you access a Redis instance using server-side JavaScript.
+
+## Asynchronicity
+
+Node.js does not block on I/O operations.
+
+This means that while a typical Redis client might have code that accesses a
+Redis server in a blocking call, Node.js-based code cannot.
+
+Typical Redis client (e.g. Python):
+
+    foo = client.get('counter')
+
+This Node.js-based Redis client:
+
+    var sys = require("sys");
+    var redis = require("./redis");
+
+    var client = new redis.Client();
+    client.connect(learn_to_count);
+
+    function learn_to_count () {
+        client.incr('counter').addCallback(function (value) {
+            sys.puts("counter is now " + value);
+            client.close();
+        });
+    }
+
+Running this example, we'd see:
+
+    $ node counter-example.js
+    counter is now 1
+    $ node counter-example.js
+    counter is now 2
+    $ node counter-example.js
+    counter is now 3
+
+That is, you must supply a callback function that is called when Redis returns,
+even if Redis queries are extremely fast.
+
+A potential upside to this slightly awkward requirement is that you can enjoy
+the benefits of pipelining many Redis queries in a non-blocking way.  Redis
+returns replies for requests in the order received.
+
+See the [test.js](http://github.com/fictorial/redis-node-client/raw/master/test.js) 
+file as a good example of this.
+
+## Status
+
+* The full Redis 1.1 command specification is supported.
+* All tests pass using Redis HEAD at commit (09f6f7020952cd93e178da11e66e36f8a98398d1; Dec 1 2009) 
+* All tests pass using Node.js v0.1.20-3-g5b1a535 (Dec 1 2009)
+* See the TODO file for known issues.
+
+## Testing
+
+To test:
+
+1. fire up redis-server on 127.0.0.1:6379 (the default)
+1. install node.js 
+1. run `node test.js`
+
+## Author
+
+Brian Hammond, Fictorial (brian at fictorial dot com)
+
+## Copyright
+
+Copyright (C) 2009 Fictorial LLC
+
+## License
+
+See LICENSE (it's MIT; go nuts).

deps/redis-node-client/redisclient.js

+// Redis client for Node.js
+// Author: Brian Hammond <brian at fictorial dot com>
+// Copyright (C) 2009 Fictorial LLC
+// License: MIT
+
+var sys = require("sys"), 
+    tcp = require("tcp");
+
+var crlf = "\r\n", 
+    crlf_len = 2;
+
+
+var inline_commands = { 
+  auth:1, bgsave:1, dbsize:1, decr:1, decrby:1, del:1,
+  exists:1, expire:1, flushall:1, flushdb:1, get:1, incr:1, incrby:1, info:1,
+  keys:1, lastsave:1, lindex:1, llen:1, lpop:1, lrange:1, ltrim:1, mget:1,
+  move:1, randomkey:1, rename:1, renamenx:1, rpop:1, save:1, scard:1, sdiff:1,
+  sdiffstore:1, select:1, shutdown:1, sinter:1, sinterstore:1, smembers:1,
+  spop:1, srandmember:1, sunion:1, sunionstore:1, ttl:1, type:1, 
+  zrange:1, zrevrange:1, zcard:1, zrangebyscore:1
+};
+
+var bulk_commands = { 
+  getset:1, lpush:1, lrem:1, lset:1, rpush:1, sadd:1, set:1,
+  setnx:1, sismember:1, smove:1, srem:1, zadd:1, zrem:1, zscore:1,
+  rpoplpush:1
+};
+
+var multi_bulk_commands = {
+  mset:1, msetnx:1
+};
+
+var Client = exports.Client = function (port, host) {
+  process.EventEmitter.call(this);
+
+  this.host = host || '127.0.0.1';
+  this.port = port || 6379;
+  this.callbacks = [];
+  this.conn = null;
+};
+
+// The client emits "connect" when a connection is established, and emits
+// "close" when a connection is closed (passing boolean true if failed in
+// error).
+//
+// Note that calling a Redis client method when the connection is closed will
+// automatically attempt to reconnect to Redis first.
+
+sys.inherits(Client, process.EventEmitter);
+
+// Callback a function after we've ensured we're connected to Redis.
+
+Client.prototype.connect = function (callback) {
+  if (!this.conn) {
+    this.conn = new process.tcp.Connection();
+  }
+  if (this.conn.readyState === "open" && typeof(callback) === 'function') {
+    callback();
+  } else {
+    var self = this;
+
+    this.conn.addListener("connect", function () {
+      this.setEncoding("binary");
+      this.setTimeout(0);          // try to stay connected.
+      this.setNoDelay();
+      self.emit("connect");
+      if (typeof(callback) === 'function')
+        callback();
+    }); 
+
+    this.conn.addListener("data", function (data) {
+      if (!self.buffer)
+        self.buffer = "";
+      self.buffer += data;
+      self.handle_replies();
+    });
+
+    this.conn.addListener("end", function () {
+      if (self.conn && self.conn.readyState === "open") {
+        self.conn.close();
+        self.conn = null;
+      }
+    });
+
+    this.conn.addListener("close", function (encountered_error) {
+      self.conn = null;
+      self.emit("close", encountered_error);
+    });
+
+    this.conn.connect(this.port, this.host);
+  }
+};
+
+Client.prototype.close = function () {
+  if (this.conn && this.conn.readyState === "open") {
+    this.conn.close();
+    this.conn = null;
+  }
+};
+
+// Reply handlers read replies from the current reply buffer.  At the time of
+// the call the buffer will start with at least the prefix associated with the
+// relevant reply type which is at this time always of length 1.  
+//
+// Note the buffer may not contain a full reply in which case these reply
+// handlers return null.  In this case the buffer is left intact for future
+// "receive" events to append onto, and the read-replies process repeats.
+// Repeat ad infinitum.  
+//
+// Each handler returns [ value, next_command_index ] on success, null on
+// underflow.
+
+var prefix_len = 1;
+
+// Bulk replies resemble:
+// $6\r\nFOOBAR\r\n
+
+Client.prototype.handle_bulk_reply = function (start_at, buf) {
+  var buffer = buf || this.buffer;
+  start_at = (start_at || 0) + prefix_len;
+  var crlf_at = buffer.indexOf(crlf, start_at);
+  if (crlf_at === -1) 
+    return null;
+  var value_len_str = buffer.substring(start_at, crlf_at);
+  var value_len = parseInt(value_len_str, 10);
+  if (value_len === NaN) 
+    throw new Error("invalid bulk value len: " + value_len_str);
+  if (value_len === -1)                 // value doesn't exist
+    return [ null, crlf_at + crlf_len ];  
+  var value_at = crlf_at + crlf_len;
+  var next_reply_at = value_at + value_len + crlf_len;
+  if (next_reply_at > buffer.length)
+    return null;
+  var value = buffer.substr(value_at, value_len);
+  return [ value, next_reply_at ];
+}
+
+// Mult-bulk replies resemble:
+// *4\r\n$3\r\nFOO\r\n$3\r\nBAR\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n
+// *4 is the number of bulk replies to follow.
+
+Client.prototype.handle_multi_bulk_reply = function (buf) {
+  var buffer = buf || this.buffer;
+  var crlf_at = buffer.indexOf(crlf, prefix_len);
+  if (crlf_at === -1) 
+    return null;
+  var count_str = buffer.substring(prefix_len, crlf_at);
+  var count = parseInt(count_str, 10);
+  if (count === NaN) 
+    throw new Error("invalid multi-bulk count: " + count_str);
+  var next_reply_at = crlf_at + crlf_len;
+  if (count === -1)                   // value doesn't exist
+    return [ null, next_reply_at ];  
+  if (count === 0)
+    return [ [], next_reply_at ];
+  if (next_reply_at >= buffer.length) 
+    return null;
+  var results = [];
+  for (var i = 0; i < count; ++i) {
+    var bulk_reply = this.handle_bulk_reply(next_reply_at, buffer);
+    if (bulk_reply === null)             // no full multi-bulk cmd
+      return null;
+    var bulk_reply_value = bulk_reply[0];
+    results.push(bulk_reply_value);
+    next_reply_at = bulk_reply[1];
+  }
+  return [ results, next_reply_at ];
+};
+
+// Single line replies resemble:
+// +OK\r\n
+
+Client.prototype.handle_single_line_reply = function (buf) {
+  var buffer = buf || this.buffer;
+  var crlf_at = buffer.indexOf(crlf, prefix_len);
+  if (crlf_at === -1) 
+    return null;
+  var value = buffer.substring(prefix_len, crlf_at);
+  if (value === 'OK') 
+    value = true;
+  var next_reply_at = crlf_at + crlf_len;
+  return [ value, next_reply_at ];
+};
+
+// Integer replies resemble:
+// :1000\r\n
+
+Client.prototype.handle_integer_reply = function (buf) {
+  var buffer = buf || this.buffer;
+  var crlf_at = buffer.indexOf(crlf, prefix_len);
+  if (crlf_at === -1) 
+    return null;
+  var value_str = buffer.substring(prefix_len, crlf_at);
+  var value = parseInt(value_str, 10);
+  if (value === NaN) 
+    throw new Error("invalid integer reply: " + value_str);
+  var next_reply_at = crlf_at + crlf_len;
+  return [ value, next_reply_at ];
+};
+
+// Error replies resemble:
+// -ERR you suck at tennis\r\n
+
+Client.prototype.handle_error_reply = function (buf) {
+  var buffer = buf || this.buffer;
+  var crlf_at = buffer.indexOf(crlf, prefix_len);
+  if (crlf_at === -1) 
+    return null;
+  var value = buffer.substring(prefix_len, crlf_at);
+  var next_reply_at = crlf_at + crlf_len;
+  if (value.indexOf("ERR ") === 0)
+    value = value.substr("ERR ".length);
+  return [ value, next_reply_at ];
+}
+
+// Try to read as many replies from the current buffer as we can.  Leave
+// partial replies in the buffer, else eat 'em.  Dispatch any promises waiting
+// for these replies.  Error replies emit error on the promise, else success is
+// emitted.
+
+Client.prototype.handle_replies = function () {
+  while (this.buffer.length > 0) {
+    if (GLOBAL.DEBUG) {
+      write_debug('---');
+      write_debug('buffer: ' + this.buffer);
+    }
+    var prefix = this.buffer.charAt(0);
+    var result, is_error = false;
+    switch (prefix) {
+      case '$': result = this.handle_bulk_reply();                   break;
+      case '*': result = this.handle_multi_bulk_reply();             break;
+      case '+': result = this.handle_single_line_reply();            break;
+      case ':': result = this.handle_integer_reply();                break;
+      case '-': result = this.handle_error_reply(); is_error = true; break;
+    }
+    // The handlers return null when there's not enough data
+    // in the buffer to read a full reply.  Leave the buffer alone until
+    // we receive more data.
+    if (result === null) 
+      break;
+    if (GLOBAL.DEBUG) {
+      write_debug('prefix: ' + prefix);
+      write_debug('result: ' + JSON.stringify(result));
+    }
+    var next_reply_at = result[1];
+    this.buffer = this.buffer.substring(next_reply_at);
+    var callback = this.callbacks.shift();
+    if (callback.promise) {
+      var result_value = result[0];
+      if (is_error) 
+        callback.promise.emitError(result_value);
+      else {
+        result_value = post_process_results(callback.command, result_value);
+        callback.promise.emitSuccess(result_value);
+      }
+    }
+  }
+};
+
+function write_debug(data) {
+  if (!GLOBAL.DEBUG || !data) return;
+  sys.puts(data.replace(/\r\n/g, '<CRLF>'));
+}
+
+function try_convert_to_number(str) {
+  var value = parseInt(str, 10);
+  if (value === NaN) 
+    value = parseFloat(str);
+  if (value === NaN) 
+    return str;
+  return value;
+}
+
+function format_inline(name, args) {
+  var command = name;
+  for (var arg in args) 
+    command += ' ' + args[arg].toString();
+  return command + crlf;
+}
+
+function format_bulk_command(name, args) {
+  var output = name;
+  for (var i = 0; i < args.length - 1; ++i) 
+    output += ' ' + args[i].toString();
+  var last_arg = args[args.length - 1].toString();
+  return output + ' ' + last_arg.length + crlf + last_arg + crlf;
+}
+
+function format_multi_bulk_command(name, args) {
+  var output = '*' + (args.length + 1) + crlf + '$' + name.length + crlf + name + crlf;
+  for (var i = 0; i < args.length; ++i) {
+    var arg_as_str = args[i].toString();
+    output += '$' + arg_as_str.length + crlf + arg_as_str + crlf;
+  }
+  return output;
+}
+
+function make_command_sender(name) {
+  Client.prototype[name] = function () {
+    if (GLOBAL.DEBUG) {
+      var description = "client." + name + "( ";
+      for (var a in arguments) 
+        description += "'" + arguments[a] + "',";
+      description = description.substr(0, description.length - 1) + " )";
+    }
+    var args = arguments;    
+    var self = this;
+    var promise = new process.Promise();
+    this.connect(function () {
+      var command;
+      if (inline_commands[name]) 
+        command = format_inline(name, args);
+      else if (bulk_commands[name]) 
+        command = format_bulk_command(name, args);
+      else if (multi_bulk_commands[name]) 
+        command = format_multi_bulk_command(name, args);
+      else 
+        throw new Error('unknown command type for "' + name + '"');
+      if (GLOBAL.DEBUG) {
+        write_debug("---");
+        write_debug("call:   " + description);
+        write_debug("command:" + command);
+      }
+      self.callbacks.push({ promise:promise, command:name.toLowerCase() });
+      self.conn.write(command);
+    });
+    return promise;
+  };
+}
+
+for (var name in inline_commands) 
+  make_command_sender(name);
+
+for (var name in bulk_commands)   
+  make_command_sender(name);
+
+for (var name in multi_bulk_commands)   
+  make_command_sender(name);
+
+function post_process_results(command, result) {
+  var new_result = result;
+  switch (command) {
+    case 'info':
+      var info = {};
+      result.split(/\r\n/g).forEach(function (line) {
+        var parts = line.split(':');
+        if (parts.length === 2)
+          info[parts[0]] = try_convert_to_number(parts[1]);
+      });
+      new_result = info;
+      break;
+    case 'keys': 
+      new_result = result.split(' '); 
+      break;
+    case 'lastsave': 
+    case 'scard':
+    case 'zcard':
+    case 'zscore':
+      new_result = try_convert_to_number(result); 
+      break;
+    default: 
+      break;
+  }
+  return new_result;
+}
+
+// Read this: http://code.google.com/p/redis/wiki/SortCommand
+// 'key' is what to sort, 'options' is how to sort.
+// 'options' is an object with optional properties:
+//   'by_pattern': 'pattern'
+//   'limit': [start, end]
+//   'get_patterns': [ 'pattern', 'pattern', ... ]
+//   'ascending': true|false
+//   'lexicographically': true|false
+//   'store_key': 'a_key_name'
+
+Client.prototype.sort = function (key, options) {
+  var promise = new process.Promise();
+  var self = this;
+  this.connect(function () {
+    var opts = [];
+    if (typeof(options) == 'object') {
+      if (options.by_pattern) 
+        opts.push('by ' + options.by_pattern);
+      if (options.get_patterns) {
+        options.get_patterns.forEach(function (pat) {
+          opts.push('get ' + pat);
+        });
+      }
+      if (!options.ascending)
+        opts.push('desc');
+      if (options.lexicographically)
+        opts.push('alpha');
+      if (options.store_key) 
+        opts.push('store ' + options.store_key);
+      if (options.limit)
+        opts.push('limit ' + options.limit[0] + ' ' + options.limit[1]);
+    } 
+    var command = 'sort ' + key + ' ' + opts.join(' ') + crlf;
+    write_debug("call:    client.sort(...)\ncommand: " + command);
+    self.callbacks.push({ promise:promise, command:'sort' });
+    self.conn.write(command);
+  });
+  return promise;
+}
+
+Client.prototype.quit = function () {
+  if (this.conn.readyState != "open") {
+    this.conn.close();
+  } else {
+    this.conn.write('quit' + crlf);
+    this.conn.close();
+  }
+};
+
+Client.prototype.make_master = function () {
+  var self = this;
+  this.connect(function () {
+    self.callbacks.push({ promise:null, command:'slaveof' });
+    self.conn.write('slaveof no one');
+  });
+};
+
+Client.prototype.make_slave_of = function (host, port) {
+  var self = this;
+  this.connect(function () {
+    port = port || 6379;
+    var command = 'slaveof ' + host + ' ' + port;
+    self.callbacks.push({ promise:null, command:'slaveof' });
+    self.conn.write(command);
+  });
+};

deps/underscore.js

+(function(){var c=this;var e=c._;var g=function(h){this._wrapped=h};var f=typeof StopIteration!=="undefined"?StopIteration:"__break__";var d=c._=function(h){return new g(h)};if(typeof exports!=="undefined"){exports._=d}d.VERSION="0.4.3";d.each=function(q,o,n){var j=0;try{if(q.forEach){q.forEach(o,n)}else{if(q.length){for(var m=0,h=q.length;m<h;m++){o.call(n,q[m],m,q)}}else{for(var k in q){if(Object.prototype.hasOwnProperty.call(q,k)){o.call(n,q[k],k,q)}}}}}catch(p){if(p!=f){throw p}}return q};d.map=function(m,k,j){if(m&&m.map){return m.map(k,j)}var h=[];d.each(m,function(p,n,o){h.push(k.call(j,p,n,o))});return h};d.reduce=function(m,h,k,j){if(m&&m.reduce){return m.reduce(d.bind(k,j),h)}d.each(m,function(p,n,o){h=k.call(j,h,p,n,o)});return h};d.reduceRight=function(m,h,k,j){if(m&&m.reduceRight){return m.reduceRight(d.bind(k,j),h)}var n=d.clone(d.toArray(m)).reverse();d.each(n,function(p,o){h=k.call(j,h,p,o,m)});return h};d.detect=function(m,k,j){var h;d.each(m,function(p,n,o){if(k.call(j,p,n,o)){h=p;d.breakLoop()}});return h};d.select=function(m,k,j){if(m.filter){return m.filter(k,j)}var h=[];d.each(m,function(p,n,o){k.call(j,p,n,o)&&h.push(p)});return h};d.reject=function(m,k,j){var h=[];d.each(m,function(p,n,o){!k.call(j,p,n,o)&&h.push(p)});return h};d.all=function(m,k,j){k=k||d.identity;if(m.every){return m.every(k,j)}var h=true;d.each(m,function(p,n,o){if(!(h=h&&k.call(j,p,n,o))){d.breakLoop()}});return h};d.any=function(m,k,j){k=k||d.identity;if(m.some){return m.some(k,j)}var h=false;d.each(m,function(p,n,o){if(h=k.call(j,p,n,o)){d.breakLoop()}});return h};d.include=function(k,j){if(d.isArray(k)){return d.indexOf(k,j)!=-1}var h=false;d.each(k,function(m){if(h=m===j){d.breakLoop()}});return h};d.invoke=function(j,k){var h=d.toArray(arguments).slice(2);return d.map(j,function(m){return(k?m[k]:m).apply(m,h)})};d.pluck=function(j,h){return d.map(j,function(k){return k[h]})};d.max=function(m,k,j){if(!k&&d.isArray(m)){return Math.max.apply(Math,m)}var h={computed:-Infinity};d.each(m,function(q,n,p){var o=k?k.call(j,q,n,p):q;o>=h.computed&&(h={value:q,computed:o})});return h.value};d.min=function(m,k,j){if(!k&&d.isArray(m)){return Math.min.apply(Math,m)}var h={computed:Infinity};d.each(m,function(q,n,p){var o=k?k.call(j,q,n,p):q;o<h.computed&&(h={value:q,computed:o})});return h.value};d.sortBy=function(k,j,h){return d.pluck(d.map(k,function(o,m,n){return{value:o,criteria:j.call(h,o,m,n)}}).sort(function(p,o){var n=p.criteria,m=o.criteria;return n<m?-1:n>m?1:0}),"value")};d.sortedIndex=function(o,n,k){k=k||d.identity;var h=0,m=o.length;while(h<m){var j=(h+m)>>1;k(o[j])<k(n)?h=j+1:m=j}return h};d.toArray=function(h){if(!h){return[]}if(d.isArray(h)){return h}return d.map(h,function(j){return j})};d.size=function(h){return d.toArray(h).length};d.first=function(h){return h[0]};d.last=function(h){return h[h.length-1]};d.compact=function(h){return d.select(h,function(j){return !!j})};d.flatten=function(h){return d.reduce(h,[],function(j,k){if(d.isArray(k)){return j.concat(d.flatten(k))}j.push(k);return j})};d.without=function(j){var h=j.slice.call(arguments,0);return d.select(j,function(k){return !d.include(h,k)})};d.uniq=function(j,h){return d.reduce(j,[],function(k,n,m){if(0==m||(h?d.last(k)!=n:!d.include(k,n))){k.push(n)}return k})};d.intersect=function(j){var h=d.toArray(arguments).slice(1);return d.select(d.uniq(j),function(k){return d.all(h,function(m){return d.indexOf(m,k)>=0})})};d.zip=function(){var h=d.toArray(arguments);var m=d.max(d.pluck(h,"length"));var k=new Array(m);for(var j=0;j<m;j++){k[j]=d.pluck(h,String(j))}return k};d.indexOf=function(j,h){if(j.indexOf){return j.indexOf(h)}for(i=0,l=j.length;i<l;i++){if(j[i]===h){return i}}return -1};d.lastIndexOf=function(k,j){if(k.lastIndexOf){return k.lastIndexOf(j)}var h=k.length;while(h--){if(k[h]===j){return h}}return -1};d.bind=function(k,j){j=j||c;var h=d.toArray(arguments).slice(2);return function(){var m=h.concat(d.toArray(arguments));return k.apply(j,m)}};d.bindAll=function(){var h=d.toArray(arguments);var j=h.pop();d.each(h,function(k){j[k]=d.bind(j[k],j)})};d.delay=function(j,k){var h=d.toArray(arguments).slice(2);return setTimeout(function(){return j.apply(j,h)},k)};d.defer=function(h){return d.delay.apply(d,[h,1].concat(d.toArray(arguments).slice(1)))};d.wrap=function(h,j){return function(){var k=[h].concat(d.toArray(arguments));return j.apply(j,k)}};d.compose=function(){var h=d.toArray(arguments);return function(){for(var j=h.length-1;j>=0;j--){arguments=[h[j].apply(this,arguments)]}return arguments[0]}};d.keys=function(h){return d.map(h,function(k,j){return j})};d.values=function(h){return d.map(h,d.identity)};d.extend=function(h,k){for(var j in k){h[j]=k[j]}return h};d.clone=function(h){if(d.isArray(h)){return h.slice(0)}return d.extend({},h)};d.isEqual=function(j,h){if(j===h){return true}var n=typeof(j),p=typeof(h);if(n!=p){return false}if(j==h){return true}if(j.isEqual){return j.isEqual(h)}if(n!=="object"){return false}var k=d.keys(j),o=d.keys(h);if(k.length!=o.length){return false}for(var m in j){if(!d.isEqual(j[m],h[m])){return false}}return true};d.isEmpty=function(h){return(d.isArray(h)?h:d.values(h)).length==0};d.isElement=function(h){return !!(h&&h.nodeType==1)};d.isArray=function(h){return Object.prototype.toString.call(h)=="[object Array]"};d.isFunction=function(h){return Object.prototype.toString.call(h)=="[object Function]"};d.isUndefined=function(h){return typeof h=="undefined"};d.noConflict=function(){c._=e;return this};d.identity=function(h){return h};d.breakLoop=function(){throw f};var b=0;d.uniqueId=function(h){var j=b++;return h?h+j:j};d.functions=function(){var j=[];for(var h in d){if(Object.prototype.hasOwnProperty.call(d,h)){j.push(h)}}return d.without(j,"VERSION","prototype","noConflict").sort()};d.template=function(k,j){var h=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+k.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return j?h(j):h};d.forEach=d.each;d.foldl=d.inject=d.reduce;d.foldr=d.reduceRight;d.filter=d.select;d.every=d.all;d.some=d.any;d.methods=d.functions;var a=function(j,h){return h?d(j).chain():j};d.each(d.functions(),function(h){g.prototype[h]=function(){Array.prototype.unshift.call(arguments,this._wrapped);return a(d[h].apply(d,arguments),this._chain)}});d.each(["pop","push","reverse","shift","sort","splice","unshift"],function(h){g.prototype[h]=function(){Array.prototype[h].apply(this._wrapped,arguments);return a(this._wrapped,this._chain)}});d.each(["concat","join","slice"],function(h){g.prototype[h]=function(){return a(Array.prototype[h].apply(this._wrapped,arguments),this._chain)}});g.prototype.chain=function(){this._chain=true;return this};g.prototype.value=function(){return this._wrapped}})();
+#!/usr/bin/env sh
+export SNIP_PATH=$(dirname `readlink -f $0`)
+
+export NODE_PATH=\
+$NODE_PATH\
+:$SNIP_PATH\
+:$SNIP_PATH/deps/redis-node-client\
+:$SNIP_PATH/deps/nerve/lib\
+:$SNIP_PATH/deps/ #underscore.js
+
+node "$@"
+var redis = require('redisclient')
+  , nerve = require('nerve')
+  , sys = require('sys')
+  , qs = require('querystring')
+  , _ = require('underscore')._;
+
+/* Passes cb to a new instance redis.Client.connect
+ * but handles error in connecting
+ * accepts optional errback as second argument
+ * the callback gets a this.redis representing the redis object
+ *
+ * Returns nothing
+ */
+var withRedis = function( cb ) {
+    var errback = arguments[1];
+
+    var r = new redis.Client();
+
+    r.connect( _.bind( cb, { redis : r } ) );
+
+    r.addListener( "close", function(error) {
+        if( error ) {
+            process.stdio.writeError( "Error connecting to Redis database\n" );
+            if( typeof(errback) === "function" )
+                errback();
+        }
+    });
+}
+
+var languages = 
+ [["apacheconf", "ApacheConf"],
+  ["applescript", "AppleScript"],
+  ["as", "ActionScript"],
+  ["as3", "ActionScript 3"],
+  ["basemake", "Makefile"],
+  ["bash", "Bash"],
+  ["bat", "Batchfile"],
+  ["bbcode", "BBCode"],
+  ["befunge", "Befunge"],
+  ["boo", "Boo"],
+  ["brainfuck", "Brainfuck"],
+  ["c", "C"],
+  ["c-objdump", "c-objdump"],
+  ["cheetah", "Cheetah"],
+  ["clojure", "Clojure"],
+  ["common-lisp", "Common Lisp"],
+  ["control", "Debian Control file"],
+  ["cpp", "C++"],
+  ["cpp-objdump", "cpp-objdump"],
+  ["csharp", "C#"],
+  ["css", "CSS"],
+  ["css+django", "CSS+Django/Jinja"],
+  ["css+erb", "CSS+Ruby"],
+  ["css+genshitext", "CSS+Genshi Text"],
+  ["css+mako", "CSS+Mako"],
+  ["css+mako", "CSS+Mako"],
+  ["css+myghty", "CSS+Myghty"],
+  ["css+php", "CSS+PHP"],
+  ["css+smarty", "CSS+Smarty"],
+  ["d", "D"],
+  ["d-objdump", "d-objdump"],
+  ["delphi", "Delphi"],
+  ["diff", "Diff"],
+  ["django", "Django/Jinja"],
+  ["dpatch", "Darcs Patch"],
+  ["dylan", "Dylan"],
+  ["erb", "ERB"],
+  ["erlang", "Erlang"],
+  ["fortran", "Fortran"],
+  ["gas", "GAS"],
+  ["genshi", "Genshi"],
+  ["genshitext", "Genshi Text"],
+  ["gnuplot", "Gnuplot"],
+  ["groff", "Groff"],
+  ["haskell", "Haskell"],
+  ["html", "HTML"],
+  ["html+cheetah", "HTML+Cheetah"],
+  ["html+django", "HTML+Django/Jinja"],
+  ["html+genshi", "HTML+Genshi"],
+  ["html+mako", "HTML+Mako"],
+  ["html+mako", "HTML+Mako"],
+  ["html+myghty", "HTML+Myghty"],
+  ["html+php", "HTML+PHP"],
+  ["html+smarty", "HTML+Smarty"],
+  ["ini", "INI"],
+  ["io", "Io"],
+  ["irc", "IRC logs"],
+  ["java", "Java"],
+  ["js", "JavaScript"],
+  ["js+cheetah", "JavaScript+Cheetah"],
+  ["js+django", "JavaScript+Django/Jinja"],
+  ["js+erb", "JavaScript+Ruby"],
+  ["js+genshitext", "JavaScript+Genshi Text"],
+  ["js+mako", "JavaScript+Mako"],
+  ["js+mako", "JavaScript+Mako"],
+  ["js+myghty", "JavaScript+Myghty"],
+  ["js+php", "JavaScript+PHP"],
+  ["js+smarty", "JavaScript+Smarty"],
+  ["jsp", "Java Server Page"],
+  ["lhs", "Literate Haskell"],
+  ["lighty", "Lighttpd configuration file"],
+  ["llvm", "LLVM"],
+  ["logtalk", "Logtalk"],
+  ["lua", "Lua"],
+  ["make", "Makefile"],
+  ["mako", "Mako"],
+  ["mako", "Mako"],
+  ["matlab", "Matlab"],
+  ["matlabsession", "Matlab session"],
+  ["minid", "MiniD"],
+  ["moocode", "MOOCode"],
+  ["mupad", "MuPAD"],
+  ["myghty", "Myghty"],
+  ["mysql", "MySQL"],
+  ["nasm", "NASM"],
+  ["nginx", "Nginx configuration file"],
+  ["numpy", "NumPy"],
+  ["objdump", "objdump"],
+  ["objective-c", "Objective-C"],
+  ["ocaml", "OCaml"],
+  ["perl", "Perl"],
+  ["php", "PHP"],
+  ["pot", "Gettext Catalog"],
+  ["pov", "POVRay"],
+  ["py3tb", "Python 3.0 Traceback"],
+  ["pycon", "Python console session"],
+  ["pytb", "Python Traceback"],
+  ["python", "Python"],
+  ["python3", "Python 3"],
+  ["raw", "Raw token data"],
+  ["rb", "Ruby"],
+  ["rbcon", "Ruby irb session"],
+  ["redcode", "Redcode"],
+  ["rhtml", "RHTML"],
+  ["rst", "reStructuredText"],
+  ["scala", "Scala"],
+  ["scheme", "Scheme"],
+  ["smalltalk", "Smalltalk"],
+  ["smarty", "Smarty"],
+  ["sourceslist", "Debian Sourcelist"],
+  ["splus", "S"],
+  ["sql", "SQL"],
+  ["sqlite3", "sqlite3con"],
+  ["squidconf", "SquidConf"],
+  ["tcl", "Tcl"],
+  ["tcsh", "Tcsh"],
+  ["tex", "TeX"],
+  ["text", "Text only"],
+  ["trac-wiki", "MoinMoin/Trac Wiki markup"],
+  ["vb.net", "VB.net"],
+  ["vim", "VimL"],
+  ["xml", "XML"],
+  ["xml+cheetah", "XML+Cheetah"],
+  ["xml+django", "XML+Django/Jinja"],
+  ["xml+erb", "XML+Ruby"],
+  ["xml+mako", "XML+Mako"],
+  ["xml+mako", "XML+Mako"],
+  ["xml+myghty", "XML+Myghty"],
+  ["xml+php", "XML+PHP"],
+  ["xml+smarty", "XML+Smarty"],
+  ["xslt", "XSLT"],
+  ["yaml", "YAML"]];
+
+var genLanguageList = function() {
+    return languages.map( function(lang) {
+        return '<option value="' + lang[0] + '">' + lang[1] + '</option>';
+    } );
+}
+
+var formHtml = '<form action="/add" method="post">'
+            +  '<label for="code">Paste code</label><br>'
+            +  '<textarea name="code" rows="25" cols="80"></textarea><br>'
+            +  '<label for="language">Language</label>'
+            +  '<select name="language">'
+            +  genLanguageList()
+            +  '</select>'
+            +  '<input type="submit" value="Paste!" /></form>';
+
+var getPostParams = function(req, callback){ 
+  var body = ''; 
+  req.addListener('data', function(chunk){
+       body += chunk;
+     }) 
+     .addListener('end', function() { 
+       var obj = qs.parse(  body.replace( /\+/g, ' ' ) ) ;
+       callback( obj );
+     });
+} 
+
+var addSnippet = function( req, res ) {
+    getPostParams( req, function( obj ) {
+        withRedis( function() {
+            var redis = this.redis; // required in inner callback
+
+            this.redis.incr( 'nextid' )
+            .addCallback( function( id ) {
+                redis.set( 'snippet:'+id, JSON.stringify( obj ) )
+                .addCallback( function() {
+                    var msg = 'The snippet has been saved at <a href="/'+id+'">'+req.headers.host+'/'+id+'</a>';
+                    res.respond( msg );
+                } );
+            } );
+        }, function() {
+            res.respond( "Error saving data. Please try again later" );
+        });
+    });
+};
+
+var showSnippet = function( req, res, id ) {
+    withRedis( function() {
+        this.redis.get( 'snippet:'+id )
+        .addCallback( function( data ) {
+            if( !data ) {
+                res.sendHeader( 404 );
+                res.write( "No such snippet" );
+                res.close();
+                return;
+            }
+
+            res.sendHeader( 200, { "Content-Type" : "text/html" } );
+
+            var obj = JSON.parse( data );
+            var shortcode = languages.filter( function(el) { 
+                return el[0] == obj.language;
+            } ) [0][0];
+
+            var pyg = process.createChildProcess( "pygmentize",
+                                                [ "-l", shortcode,
+                                                  "-f", "html",
+                                                  "-O", "full,style=pastie",
+                                                  "-P", "title=Snippet #" + id ] );
+            pyg.addListener( "output", function( coloured ) {
+                if( coloured )
+                    res.write( coloured );
+                else
+                    res.close();
+            } );
+
+            pyg.write( obj.code );
+            pyg.close();
+        });
+    });
+}
+
+nerve.create( [
+    [ /^\/([0-9]+)/, showSnippet ],
+    [ nerve.post("/add"), addSnippet ],
+    [ "/", function( req, res ) { res.respond( formHtml ); } ]
+]).listen( 8000 );
+