Skip to content
This repository
Browse code

initial commit

  • Loading branch information...
commit d98d412cf12cc4f0283680412a3ad3c79cc861f4 0 parents
Christopher Bennage authored
9  .gitignore
... ...
@@ -0,0 +1,9 @@
  1
+*.log
  2
+*.gz
  3
+config.js
  4
+logs
  5
+node_modules/*
  6
+*.sublime-workspace
  7
+*.logs/*
  8
+source/*
  9
+data/*
2  README
... ...
@@ -0,0 +1,2 @@
  1
+Christopher Bennage
  2
+http://dev.bennage.com
82  app.js
... ...
@@ -0,0 +1,82 @@
  1
+var x = require('express'),
  2
+    viewHelper = require('./viewHelper'),
  3
+    routes = require('./routes');
  4
+app = module.exports = x.createServer();
  5
+
  6
+function context(req, res, next) {
  7
+
  8
+    var session, auth;
  9
+
  10
+    // if we already have the context, next
  11
+    if (req.context && req.context.user) {
  12
+        return next();
  13
+    }
  14
+
  15
+    session = req.session;
  16
+    req.context = {};
  17
+
  18
+    // if there's not session or auth, then set an empty context
  19
+    if (!session || !(session.auth && session.auth.loggedIn)) {
  20
+        return next();
  21
+    }
  22
+
  23
+    req.context.user = session.auth.github.user;
  24
+    req.context.accessToken = session.auth.github.accessToken;
  25
+    // github.init(req.context.accessToken);
  26
+    next();
  27
+}
  28
+
  29
+app.configure('development', function() {
  30
+    app.use(x.logger('tiny'));
  31
+    app.use(x.errorHandler({
  32
+        dumpExceptions: true,
  33
+        showStack: true
  34
+    }));
  35
+});
  36
+
  37
+app.configure(function() {
  38
+    app.set('views', __dirname + '/views');
  39
+    app.set('view engine', 'jade');
  40
+    app.use(x.bodyParser());
  41
+    app.use(x.methodOverride());
  42
+    app.use(x.favicon());
  43
+    app.use(x.cookieParser());
  44
+    app.use(x.session({
  45
+        secret: 'bennage'
  46
+    }));
  47
+    app.use(require('stylus').middleware({
  48
+        src: __dirname + '/public'
  49
+    }));
  50
+    app.use(x['static'](__dirname + '/public'));
  51
+    app.use(require('./auth').configure(app));
  52
+    app.use(context);
  53
+    app.use(app.router);
  54
+});
  55
+
  56
+app.configure('production', function() {
  57
+    app.use(x.errorHandler({
  58
+        showStack: true,
  59
+        dumpExceptions: true
  60
+    }));
  61
+});
  62
+
  63
+viewHelper.initialize(app);
  64
+
  65
+var fs = require('fs');
  66
+var markdown = require("markdown").markdown;
  67
+var files = fs.readdirSync('./source');
  68
+
  69
+files.forEach(function(file) {
  70
+    var input = fs.readFileSync('./source/' + file, 'utf8');
  71
+    var output = markdown.toHTML(input);
  72
+    fs.writeFileSync('./source/' + file.replace('.markdown', '.html'), output);
  73
+});
  74
+
  75
+app.get('/', routes.index);
  76
+app.post('/comment/:file', routes.saveComment);
  77
+app.get('/comments/:file', routes.getComments);
  78
+app.get('/documents', routes.getDocuments);
  79
+app.get('/document/:file', routes.getDocument);
  80
+app.get('/:file', routes.index);
  81
+
  82
+app.redirect('login', '/');
25  auth.js
... ...
@@ -0,0 +1,25 @@
  1
+var everyauth = require('everyauth');
  2
+var config = require('./config');
  3
+
  4
+module.exports.configure = function(app) {
  5
+
  6
+    everyauth.debug = true;
  7
+
  8
+    var github = everyauth.github.scope('repo').appId(config.github.appId).appSecret(config.github.appSecret);
  9
+
  10
+    function findOrCreate(user, promise) {
  11
+        promise.fulfill(user);
  12
+        return promise;
  13
+    }
  14
+
  15
+    github.findOrCreateUser(function(session, accessToken, extra, user) {
  16
+        console.dir(extra);
  17
+        console.dir(user);
  18
+        return findOrCreate(user, this.Promise());
  19
+    }).redirectPath('/');
  20
+
  21
+    // mixin view helpers for everyauth
  22
+    everyauth.helpExpress(app);
  23
+
  24
+    return everyauth.middleware();
  25
+};
48  data.js
... ...
@@ -0,0 +1,48 @@
  1
+var fs = require('fs'),
  2
+	path = require('path');
  3
+
  4
+var data_file = './data/data',
  5
+	hash_table = loadSync();
  6
+
  7
+console.log('reading data from file');
  8
+console.dir(hash_table);
  9
+
  10
+function getCommentsFor(documentId, callback) {
  11
+
  12
+	if (!hash_table[documentId]) {
  13
+		hash_table[documentId] = {};
  14
+	}
  15
+
  16
+	callback(null, hash_table[documentId]);
  17
+}
  18
+
  19
+function saveCommentFor(documentId, comment) {
  20
+	if (!hash_table[documentId]) {
  21
+		hash_table[documentId] = {};
  22
+	}
  23
+
  24
+	if (!comment.id) throw new Error('cannot save a comment without an id');
  25
+
  26
+	hash_table[documentId][comment.id] = comment;
  27
+
  28
+	persist();
  29
+}
  30
+
  31
+function persist() {
  32
+	var data = JSON.stringify(hash_table);
  33
+	fs.writeFile(data_file, data, 'utf8');
  34
+}
  35
+
  36
+function loadSync() {
  37
+	if (path.existsSync(data_file)) {
  38
+		var data = fs.readFileSync(data_file, 'utf8');
  39
+		return JSON.parse(data);
  40
+	} else {
  41
+		return {};
  42
+	}
  43
+}
  44
+
  45
+module.exports = {
  46
+	saveCommentFor: saveCommentFor,
  47
+	getCommentsFor: getCommentsFor
  48
+};
15  package.json
... ...
@@ -0,0 +1,15 @@
  1
+{
  2
+    "name": "pundit"
  3
+  , "version": "0.0.1"
  4
+  , "private": true
  5
+  , "dependencies": {
  6
+      "express": ">= 2.5.x"
  7
+    , "stylus": ">= 0.0.1"
  8
+    , "jade": ">= 0.0.1"
  9
+    , "everyauth": ">= 0.2.18"
  10
+    , "markdown": "0.3.1"
  11
+  }
  12
+  , "devDependencies": {
  13
+      "mocha": "*"
  14
+  }
  15
+}
8  project.sublime-project
... ...
@@ -0,0 +1,8 @@
  1
+{
  2
+	"folders":
  3
+	[
  4
+		{
  5
+			"path": "."
  6
+		}
  7
+	]
  8
+}
BIN  public/comment_yellow.gif
239  public/javascripts/client.js
... ...
@@ -0,0 +1,239 @@
  1
+(function() {
  2
+
  3
+	var elems = 'h1,h2,h3,h4,h5,h6,p',
  4
+		root = '#for-review',
  5
+		focus = null,
  6
+		context = '',
  7
+		store = [];
  8
+
  9
+	function to_path(chain) {
  10
+		return chain.map(function(x) {
  11
+			return '[' + $(x).index() + ':' + x.localName + ':' + x.id + ']';
  12
+		}).join();
  13
+	}
  14
+
  15
+	function save_comment(evt) {
  16
+		var el = $(evt.currentTarget);
  17
+		var body = el.text();
  18
+		var target = focus;
  19
+		var chain = target.parentsUntil('section').andSelf().toArray();
  20
+		var path = to_path(chain.slice(1));
  21
+
  22
+		if (!body || body === '') {
  23
+			// todo: check to see if the comment was modified
  24
+			// perhaps by setting a flag when we copy content
  25
+			// into the edit field
  26
+			return;
  27
+		}
  28
+
  29
+		var comment = {
  30
+			regarding: target.text(),
  31
+			path: path,
  32
+			body: body
  33
+		};
  34
+
  35
+		$.post('/comment/' + context, comment, function(data, textStatus, jqXHR) {
  36
+			console.log(textStatus);
  37
+		});
  38
+	}
  39
+
  40
+	function matching(path) {
  41
+		return store.filter(function(x) {
  42
+			return x.path === path;
  43
+		});
  44
+	}
  45
+
  46
+	function how_long_since(time, now) {
  47
+		var delta = now - time;
  48
+		var s = 1000;
  49
+		var m = s * 60;
  50
+		var h = m * 60;
  51
+		var d = h * 24;
  52
+		var w = d * 7;
  53
+
  54
+		if (delta <= m) return 'just now';
  55
+		if (delta <= h) return Math.floor(delta / m) + 'm ago';
  56
+		if (delta <= d) return Math.floor(delta / h) + 'h ago';
  57
+		if (delta <= w) return Math.floor(delta / d) + 'days ago';
  58
+		return Math.floor(delta / w) + 'weeks ago';
  59
+	}
  60
+
  61
+	function reset_comment_edits() {
  62
+		var history = $('#comments .history');
  63
+		var body = $('#comments .comment-body');
  64
+
  65
+		history.empty();
  66
+
  67
+		if (focus) {
  68
+			focus.removeClass('focus');
  69
+		}
  70
+
  71
+		focus = null;
  72
+
  73
+		body.attr('contenteditable', 'false').text('');
  74
+	}
  75
+
  76
+	function set_focus(target, chain) {
  77
+		var path = target.data('path');
  78
+		var history = $('#comments .history');
  79
+		var body = $('#comments .comment-body');
  80
+		var now = Date.now();
  81
+
  82
+		history.empty();
  83
+
  84
+		if (focus) {
  85
+			focus.removeClass('focus');
  86
+		}
  87
+		target.addClass('focus');
  88
+		focus = target;
  89
+
  90
+		body.attr('contenteditable', 'true').text('').focus();
  91
+
  92
+		var template = $('#tmpl-comment').html();
  93
+
  94
+		matching(path).forEach(function(x) {
  95
+			x.timestampFormatted = function() {
  96
+				return how_long_since(x.timestamp, now);
  97
+			};
  98
+
  99
+			if (x.author.login === login) {
  100
+				body.text(x.body);
  101
+			} else {
  102
+				var out = Mustache.render(template, x);
  103
+				history.append(out);
  104
+			}
  105
+		});
  106
+	}
  107
+
  108
+	function render_comments() {
  109
+		var template = $('#tmpl-comment').html();
  110
+		var history = $('#comments .history');
  111
+		var now = Date.now();
  112
+
  113
+		store.forEach(function(x) {
  114
+			x.timestampFormatted = function() {
  115
+				return how_long_since(x.timestamp, now);
  116
+			};
  117
+
  118
+			if (x.author.login === login) {
  119
+				body.text(x.body);
  120
+			} else {
  121
+				var out = Mustache.render(template, x);
  122
+				history.append(out);
  123
+			}
  124
+		});
  125
+	}
  126
+
  127
+	function mark_document_elements() {
  128
+
  129
+		$(root).find(elems).each(function() {
  130
+			var el = $(this);
  131
+
  132
+			var chain = el.parentsUntil('section').andSelf().toArray();
  133
+			var path = to_path(chain.slice(1));
  134
+
  135
+			el.attr('data-path', path);
  136
+		});
  137
+	}
  138
+
  139
+	function associate_existing_comments() {
  140
+
  141
+		$('#comments .comments-stats').text(store.length + ' comments');
  142
+
  143
+		store.forEach(function(x) {
  144
+			var selector = '[data-path="' + x.path + '"]';
  145
+			$(selector).addClass('has-comments');
  146
+		});
  147
+	}
  148
+
  149
+	function make_array(obj) {
  150
+		var a = [],
  151
+			prop;
  152
+
  153
+		for (prop in obj) {
  154
+			a.push(obj[prop]);
  155
+		}
  156
+
  157
+		return a;
  158
+	}
  159
+
  160
+	function setup_document_list(documents) {
  161
+		var el = $('#docs');
  162
+
  163
+		documents.forEach(function(doc) {
  164
+			el.append('<option>' + doc + '</option>');
  165
+		});
  166
+
  167
+		el.change(function() {
  168
+
  169
+			el.find('option:selected').each(function() {
  170
+				var file = $(this).text();
  171
+				History.pushState(null, null, file);
  172
+			});
  173
+
  174
+			el.find('option[data-default]').remove();
  175
+
  176
+		});
  177
+	}
  178
+
  179
+	function document_has_changed() {
  180
+
  181
+		set_context_from_url();
  182
+
  183
+		$.getJSON('/document/' + context, function(data) {
  184
+
  185
+			var article = $(root);
  186
+
  187
+			article.html(data.content);
  188
+
  189
+			article.find(elems).click(function() {
  190
+				var target = $(this);
  191
+				set_focus(target);
  192
+			});
  193
+
  194
+			article.find(elems).hover(function() {
  195
+				$(this).addClass('highlight');
  196
+			}, function() {
  197
+				$(this).removeClass('highlight');
  198
+			});
  199
+		});
  200
+
  201
+		reset_comment_edits();
  202
+
  203
+		$.getJSON('/comments/' + context, function(data) {
  204
+			store = make_array(data);
  205
+			mark_document_elements();
  206
+			associate_existing_comments();
  207
+			render_comments();
  208
+		});
  209
+	}
  210
+
  211
+	function set_context_from_url() {
  212
+
  213
+		context = null;
  214
+
  215
+		var target = window.location.href.match(/\/#?([\w-]*\.html)/);
  216
+		if (target !== null && target.length > 0) context = target[1];
  217
+
  218
+		if (!context) {
  219
+			History.replaceState(null, null, '/');
  220
+			$('#workspace').hide();
  221
+		} else {
  222
+			$('#workspace').show();
  223
+		}
  224
+	}
  225
+
  226
+	$(function() {
  227
+
  228
+		History.Adapter.bind(window, 'statechange', function() {
  229
+			document_has_changed();
  230
+		});
  231
+
  232
+		$.getJSON('/documents', setup_document_list);
  233
+
  234
+		$('#comments .comment-body').blur(save_comment);
  235
+
  236
+		document_has_changed();
  237
+	});
  238
+
  239
+})();
31  public/stylesheets/layout.styl
... ...
@@ -0,0 +1,31 @@
  1
+body, div, header, footer, section
  2
+	box-sizing border-box 
  3
+
  4
+html
  5
+	height 100%
  6
+	width 100%
  7
+
  8
+.root
  9
+	display table
  10
+	height 100%
  11
+	width 100%
  12
+
  13
+	header,footer
  14
+		display table-row
  15
+		width 100%
  16
+
  17
+	#main
  18
+		display table-row
  19
+		height 100%
  20
+
  21
+#workspace
  22
+	display table
  23
+	height 100%
  24
+
  25
+	article
  26
+		display table-cell
  27
+		vertical-align top
  28
+
  29
+	aside
  30
+		display table-cell
  31
+		vertical-align top
13  public/stylesheets/mixins.styl
... ...
@@ -0,0 +1,13 @@
  1
+vendor(prop,args)
  2
+	-webkit-{prop} args
  3
+	-moz-{prop} args
  4
+	-ms-{prop} args
  5
+	-o-{prop} args
  6
+	{prop} args
  7
+
  8
+box()
  9
+	display -webkit-box
  10
+	display -moz-box
  11
+	display -ms-box
  12
+	display -o-box
  13
+	display box
439  public/stylesheets/normalize.styl
... ...
@@ -0,0 +1,439 @@
  1
+/*! normalize.css 2011-08-12T17:28 UTC · http://github.com/necolas/normalize.css */
  2
+
  3
+/* =============================================================================
  4
+   HTML5 display definitions
  5
+   ========================================================================== */
  6
+
  7
+/*
  8
+ * Corrects block display not defined in IE6/7/8/9 & FF3
  9
+ */
  10
+
  11
+article,
  12
+aside,
  13
+details,
  14
+figcaption,
  15
+figure,
  16
+footer,
  17
+header,
  18
+hgroup,
  19
+nav,
  20
+section {
  21
+    display: block;
  22
+}
  23
+
  24
+/*
  25
+ * Corrects inline-block display not defined in IE6/7/8/9 & FF3
  26
+ */
  27
+
  28
+audio,
  29
+canvas,
  30
+video {
  31
+    display: inline-block;
  32
+    *display: inline;
  33
+    *zoom: 1;
  34
+}
  35
+
  36
+/*
  37
+ * Prevents modern browsers from displaying 'audio' without controls
  38
+ */
  39
+
  40
+audio:not([controls]) {
  41
+    display: none;
  42
+}
  43
+
  44
+/*
  45
+ * Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4
  46
+ * Known issue: no IE6 support
  47
+ */
  48
+
  49
+[hidden] {
  50
+    display: none;
  51
+}
  52
+
  53
+
  54
+/* =============================================================================
  55
+   Base
  56
+   ========================================================================== */
  57
+
  58
+/*
  59
+ * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units
  60
+ *    http://clagnut.com/blog/348/#c790
  61
+ * 2. Keeps page centred in all browsers regardless of content height
  62
+ * 3. Prevents iOS text size adjust after orientation change, without disabling user zoom
  63
+ *    www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/
  64
+ */
  65
+
  66
+html {
  67
+    font-size: 100%; /* 1 */
  68
+    overflow-y: scroll; /* 2 */
  69
+    -webkit-text-size-adjust: 100%; /* 3 */
  70
+    -ms-text-size-adjust: 100%; /* 3 */
  71
+}
  72
+
  73
+/*
  74
+ * Addresses margins handled incorrectly in IE6/7
  75
+ */
  76
+
  77
+body {
  78
+    margin: 0;
  79
+}
  80
+
  81
+/* 
  82
+ * Addresses font-family inconsistency between 'textarea' and other form elements.
  83
+ */
  84
+
  85
+body,
  86
+button,
  87
+input,
  88
+select,
  89
+textarea {
  90
+    font-family: sans-serif;
  91
+}
  92
+
  93
+
  94
+/* =============================================================================
  95
+   Links
  96
+   ========================================================================== */
  97
+
  98
+a {
  99
+    color: #00e;
  100
+}
  101
+
  102
+a:visited {
  103
+    color: #551a8b;
  104
+}
  105
+
  106
+/*
  107
+ * Addresses outline displayed oddly in Chrome
  108
+ */
  109
+
  110
+a:focus {
  111
+    outline: thin dotted;
  112
+}
  113
+
  114
+/*
  115
+ * Improves readability when focused and also mouse hovered in all browsers
  116
+ * people.opera.com/patrickl/experiments/keyboard/test
  117
+ */
  118
+
  119
+a:hover,
  120
+a:active {
  121
+    outline: 0;
  122
+}
  123
+
  124
+
  125
+/* =============================================================================
  126
+   Typography
  127
+   ========================================================================== */
  128
+
  129
+/*
  130
+ * Addresses styling not present in IE7/8/9, S5, Chrome
  131
+ */
  132
+
  133
+abbr[title] {
  134
+    border-bottom: 1px dotted;
  135
+}
  136
+
  137
+/*
  138
+ * Addresses style set to 'bolder' in FF3/4, S4/5, Chrome
  139
+*/
  140
+
  141
+b, 
  142
+strong { 
  143
+    font-weight: bold; 
  144
+}
  145
+
  146
+blockquote {
  147
+    margin: 1em 40px;
  148
+}
  149
+
  150
+/*
  151
+ * Addresses styling not present in S5, Chrome
  152
+ */
  153
+
  154
+dfn {
  155
+    font-style: italic;
  156
+}
  157
+
  158
+/*
  159
+ * Addresses styling not present in IE6/7/8/9
  160
+ */
  161
+
  162
+mark {
  163
+    background: #ff0;
  164
+    color: #000;
  165
+}
  166
+
  167
+/*
  168
+ * Corrects font family set oddly in IE6, S4/5, Chrome
  169
+ * en.wikipedia.org/wiki/User:Davidgothberg/Test59
  170
+ */
  171
+
  172
+pre,
  173
+code,
  174
+kbd,
  175
+samp {
  176
+    font-family: monospace, serif;
  177
+    _font-family: 'courier new', monospace;
  178
+    font-size: 1em;
  179
+}
  180
+
  181
+/*
  182
+ * Improves readability of pre-formatted text in all browsers
  183
+ */
  184
+
  185
+pre {
  186
+    white-space: pre;
  187
+    white-space: pre-wrap;
  188
+    word-wrap: break-word;
  189
+}
  190
+
  191
+/*
  192
+ * 1. Addresses CSS quotes not supported in IE6/7
  193
+ * 2. Addresses quote property not supported in S4
  194
+ */
  195
+
  196
+/* 1 */
  197
+
  198
+q {
  199
+    quotes: none;
  200
+}
  201
+
  202
+/* 2 */
  203
+
  204
+q:before,
  205
+q:after {
  206
+    content: '';
  207
+    content: none;
  208
+}
  209
+
  210
+small {
  211
+    font-size: 75%;
  212
+}
  213
+
  214
+/*
  215
+ * Prevents sub and sup affecting line-height in all browsers
  216
+ * gist.github.com/413930
  217
+ */
  218
+
  219
+sub,
  220
+sup {
  221
+    font-size: 75%;
  222
+    line-height: 0;
  223
+    position: relative;
  224
+    vertical-align: baseline;
  225
+}
  226
+
  227
+sup {
  228
+    top: -0.5em;
  229
+}
  230
+
  231
+sub {
  232
+    bottom: -0.25em;
  233
+}
  234
+
  235
+
  236
+/* =============================================================================
  237
+   Lists
  238
+   ========================================================================== */
  239
+
  240
+ul,
  241
+ol {
  242
+    margin: 1em 0;
  243
+    padding: 0 0 0 40px;
  244
+}
  245
+
  246
+dd {
  247
+    margin: 0 0 0 40px;
  248
+}
  249
+
  250
+nav ul,
  251
+nav ol {
  252
+    list-style: none;
  253
+    list-style-image: none;
  254
+}
  255
+
  256
+
  257
+/* =============================================================================
  258
+   Embedded content
  259
+   ========================================================================== */
  260
+
  261
+/*
  262
+ * 1. Removes border when inside 'a' element in IE6/7/8/9
  263
+ * 2. Improves image quality when scaled in IE7
  264
+ *    code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/
  265
+ */
  266
+
  267
+img {
  268
+    border: 0; /* 1 */
  269
+    -ms-interpolation-mode: bicubic; /* 2 */
  270
+}
  271
+
  272
+/*
  273
+ * Corrects overflow displayed oddly in IE9 
  274
+ */
  275
+
  276
+svg:not(:root) {
  277
+    overflow: hidden;
  278
+}
  279
+
  280
+
  281
+/* =============================================================================
  282
+   Figures
  283
+   ========================================================================== */
  284
+
  285
+/*
  286
+ * Addresses margin not present in IE6/7/8/9, S5, O11
  287
+ */
  288
+
  289
+figure {
  290
+    margin: 0;
  291
+}
  292
+
  293
+
  294
+/* =============================================================================
  295
+   Forms
  296
+   ========================================================================== */
  297
+
  298
+/*
  299
+ * Corrects margin displayed oddly in IE6/7
  300
+ */
  301
+
  302
+form {
  303
+    margin: 0;
  304
+}
  305
+
  306
+/*
  307
+ * Define consistent margin and padding
  308
+ */
  309
+
  310
+fieldset {
  311
+    margin: 0 2px;
  312
+    padding: 0.35em 0.625em 0.75em;
  313
+}
  314
+
  315
+/*
  316
+ * 1. Corrects color not being inherited in IE6/7/8/9
  317
+ * 2. Corrects alignment displayed oddly in IE6/7
  318
+ */
  319
+
  320
+legend {
  321
+    border: 0; /* 1 */
  322
+    *margin-left: -7px; /* 2 */
  323
+}
  324
+
  325
+/*
  326
+ * 1. Corrects font size not being inherited in all browsers
  327
+ * 2. Addresses margins set differently in IE6/7, F3/4, S5, Chrome
  328
+ * 3. Improves appearance and consistency in all browsers
  329
+ */
  330
+
  331
+button,
  332
+input,
  333
+select,
  334
+textarea {
  335
+    font-size: 100%; /* 1 */
  336
+    margin: 0; /* 2 */
  337
+    vertical-align: baseline; /* 3 */
  338
+    *vertical-align: middle; /* 3 */
  339
+}
  340
+
  341
+/*
  342
+ * 1. Addresses FF3/4 setting line-height using !important in the UA stylesheet
  343
+ * 2. Corrects inner spacing displayed oddly in IE6/7
  344
+ */
  345
+
  346
+button,
  347
+input {
  348
+    line-height: normal; /* 1 */
  349
+    *overflow: visible;  /* 2 */
  350
+}
  351
+
  352
+/*
  353
+ * Corrects overlap and whitespace issue for buttons and inputs in IE6/7
  354
+ * Known issue: reintroduces inner spacing
  355
+ */
  356
+
  357
+table button,
  358
+table input {
  359
+    *overflow: auto;
  360
+}
  361
+
  362
+/*
  363
+ * 1. Improves usability and consistency of cursor style between image-type 'input' and others
  364
+ * 2. Corrects inability to style clickable 'input' types in iOS
  365
+ */
  366
+
  367
+button,
  368
+html input[type="button"], 
  369
+input[type="reset"], 
  370
+input[type="submit"] {
  371
+    cursor: pointer; /* 1 */
  372
+    -webkit-appearance: button; /* 2 */
  373
+}
  374
+
  375
+/*
  376
+ * 1. Addresses box sizing set to content-box in IE8/9
  377
+ * 2. Addresses excess padding in IE8/9
  378
+ */
  379
+
  380
+input[type="checkbox"],
  381
+input[type="radio"] {
  382
+    box-sizing: border-box; /* 1 */
  383
+    padding: 0; /* 2 */
  384
+}
  385
+
  386
+/*
  387
+ * 1. Addresses appearance set to searchfield in S5, Chrome
  388
+ * 2. Addresses box sizing set to border-box in S5, Chrome (include -moz to future-proof)
  389
+ */
  390
+
  391
+input[type="search"] {
  392
+    -webkit-appearance: textfield; /* 1 */
  393
+    -moz-box-sizing: content-box;
  394
+    -webkit-box-sizing: content-box; /* 2 */
  395
+    box-sizing: content-box;
  396
+}
  397
+
  398
+/*
  399
+ * Corrects inner padding displayed oddly in S5, Chrome on OSX
  400
+ */
  401
+
  402
+input[type="search"]::-webkit-search-decoration {
  403
+    -webkit-appearance: none;
  404
+}
  405
+
  406
+/*
  407
+ * Corrects inner padding and border displayed oddly in FF3/4
  408
+ * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/
  409
+ */
  410
+
  411
+button::-moz-focus-inner,
  412
+input::-moz-focus-inner {
  413
+    border: 0;
  414
+    padding: 0;
  415
+}
  416
+
  417
+/*
  418
+ * 1. Removes default vertical scrollbar in IE6/7/8/9
  419
+ * 2. Improves readability and alignment in all browsers
  420
+ */
  421
+
  422
+textarea {
  423
+    overflow: auto; /* 1 */
  424
+    vertical-align: top; /* 2 */
  425
+}
  426
+
  427
+
  428
+/* =============================================================================
  429
+   Tables
  430
+   ========================================================================== */
  431
+
  432
+/* 
  433
+ * Remove most spacing between table cells
  434
+ */
  435
+
  436
+table {
  437
+    border-collapse: collapse;
  438
+    border-spacing: 0;
  439
+}
18  public/stylesheets/notification.styl
... ...
@@ -0,0 +1,18 @@
  1
+// looking for a css-only animation here
  2
+#messages
  3
+    position absolute
  4
+    z-index 101
  5
+    top 0
  6
+    left 0
  7
+    right 0
  8
+    background contrast
  9
+    text-align center
  10
+    line-height 2.5em
  11
+    font-size 24px
  12
+    overflow hidden
  13
+    vendor(box-shadow,0 0 5px black)
  14
+    color light_text
  15
+    text-shadow white 0px 0px 2px, black 1px 1px 0px
  16
+    
  17
+    .error
  18
+        color black
392  public/stylesheets/style.css
... ...
@@ -0,0 +1,392 @@
  1
+article,
  2
+aside,
  3
+details,
  4
+figcaption,
  5
+figure,
  6
+footer,
  7
+header,
  8
+hgroup,
  9
+nav,
  10
+section {
  11
+  display: block;
  12
+}
  13
+audio,
  14
+canvas,
  15
+video {
  16
+  display: inline-block;
  17
+  *display: inline;
  18
+  *zoom: 1;
  19
+}
  20
+audio:not([controls]) {
  21
+  display: none;
  22
+}
  23
+[hidden] {
  24
+  display: none;
  25
+}
  26
+html {
  27
+  font-size: 100%;
  28
+/* 1 */
  29
+  overflow-y: scroll;
  30
+/* 2 */
  31
+  -webkit-text-size-adjust: 100%;
  32
+/* 3 */
  33
+  -ms-text-size-adjust: 100%;
  34
+/* 3 */
  35
+}
  36
+body {
  37
+  margin: 0;
  38
+}
  39
+body,
  40
+button,
  41
+input,
  42
+select,
  43
+textarea {
  44
+  font-family: sans-serif;
  45
+}
  46
+a {
  47
+  color: #00e;
  48
+}
  49
+a:visited {
  50
+  color: #551a8b;
  51
+}
  52
+a:focus {
  53
+  outline: thin dotted;
  54
+}
  55
+a:hover,
  56
+a:active {
  57
+  outline: 0;
  58
+}
  59
+abbr[title] {
  60
+  border-bottom: 1px dotted;
  61
+}
  62
+b,
  63
+strong {
  64
+  font-weight: bold;
  65
+}
  66
+blockquote {
  67
+  margin: 1em 40px;
  68
+}
  69
+dfn {
  70
+  font-style: italic;
  71
+}
  72
+mark {
  73
+  background: #ff0;
  74
+  color: #000;
  75
+}
  76
+pre,
  77
+code,
  78
+kbd,
  79
+samp {
  80
+  font-family: monospace, serif;
  81
+  _font-family: 'courier new', monospace;
  82
+  font-size: 1em;
  83
+}
  84
+pre {
  85
+  white-space: pre;
  86
+  white-space: pre-wrap;
  87
+  word-wrap: break-word;
  88
+}
  89
+q {
  90
+  quotes: none;
  91
+}
  92
+q:before,
  93
+q:after {
  94
+  content: '';
  95
+  content: none;
  96
+}
  97
+small {
  98
+  font-size: 75%;
  99
+}
  100
+sub,
  101
+sup {
  102
+  font-size: 75%;
  103
+  line-height: 0;
  104
+  position: relative;
  105
+  vertical-align: baseline;
  106
+}
  107
+sup {
  108
+  top: -0.5em;
  109
+}
  110
+sub {
  111
+  bottom: -0.25em;
  112
+}
  113
+ul,
  114
+ol {
  115
+  margin: 1em 0;
  116
+  padding: 0 0 0 40px;
  117
+}
  118
+dd {
  119
+  margin: 0 0 0 40px;
  120
+}
  121
+nav ul,
  122
+nav ol {
  123
+  list-style: none;
  124
+  list-style-image: none;
  125
+}
  126
+img {
  127
+  border: 0;
  128
+/* 1 */
  129
+  -ms-interpolation-mode: bicubic;
  130
+/* 2 */
  131
+}
  132
+svg:not(:root) {
  133
+  overflow: hidden;
  134
+}
  135
+figure {
  136
+  margin: 0;
  137
+}
  138
+form {
  139
+  margin: 0;
  140
+}
  141
+fieldset {
  142
+  margin: 0 2px;
  143
+  padding: 0.35em 0.625em 0.75em;
  144
+}
  145
+legend {
  146
+  border: 0;
  147
+/* 1 */
  148
+  *margin-left: -7px;
  149
+/* 2 */
  150
+}
  151
+button,
  152
+input,
  153
+select,
  154
+textarea {
  155
+  font-size: 100%;
  156
+/* 1 */
  157
+  margin: 0;
  158
+/* 2 */
  159
+  vertical-align: baseline;
  160
+/* 3 */
  161
+  *vertical-align: middle;
  162
+/* 3 */
  163
+}
  164
+button,
  165
+input {
  166
+  line-height: normal;
  167
+/* 1 */
  168
+  *overflow: visible;
  169
+/* 2 */
  170
+}
  171
+table button,
  172
+table input {
  173
+  *overflow: auto;
  174
+}
  175
+button,
  176
+html input[type="button"],
  177
+input[type="reset"],
  178
+input[type="submit"] {
  179
+  cursor: pointer;
  180
+/* 1 */
  181
+  -webkit-appearance: button;
  182
+/* 2 */
  183
+}
  184
+input[type="checkbox"],
  185
+input[type="radio"] {
  186
+  box-sizing: border-box;
  187
+/* 1 */
  188
+  padding: 0;
  189
+/* 2 */
  190
+}
  191
+input[type="search"] {
  192
+  -webkit-appearance: textfield;
  193
+/* 1 */
  194
+  -moz-box-sizing: content-box;
  195
+  -webkit-box-sizing: content-box;
  196
+/* 2 */
  197
+  box-sizing: content-box;
  198
+}
  199
+input[type="search"]::-webkit-search-decoration {
  200
+  -webkit-appearance: none;
  201
+}
  202
+button::-moz-focus-inner,
  203
+input::-moz-focus-inner {
  204
+  border: 0;
  205
+  padding: 0;
  206
+}
  207
+textarea {
  208
+  overflow: auto;
  209
+/* 1 */
  210
+  vertical-align: top;
  211
+/* 2 */
  212
+}
  213
+table {
  214
+  border-collapse: collapse;
  215
+  border-spacing: 0;
  216
+}
  217
+body,
  218
+div,
  219
+header,
  220
+footer,
  221
+section {
  222
+  box-sizing: border-box;
  223
+}
  224
+html {
  225
+  height: 100%;
  226
+  width: 100%;
  227
+}
  228
+.root {
  229
+  display: table;