From 6b025fa08c6f12356e9480e8ce38d7971c7cb491 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Mon, 22 Sep 2014 09:59:44 -0400 Subject: [PATCH] Smarter route loading, more sensible folder names --- app.js | 18 ++- node_modules/require-dir/.npmignore | 2 + node_modules/require-dir/README.md | 108 ++++++++++++++++ node_modules/require-dir/index.js | 120 ++++++++++++++++++ node_modules/require-dir/package.json | 48 +++++++ node_modules/require-dir/test/duplicates.js | 43 +++++++ node_modules/require-dir/test/duplicates/a.js | 1 + .../require-dir/test/duplicates/b.json | 1 + .../require-dir/test/duplicates/b/1.js | 1 + .../require-dir/test/duplicates/b/1.txt | 1 + .../require-dir/test/duplicates/b/2.js | 1 + .../require-dir/test/duplicates/b/2.json | 1 + .../require-dir/test/duplicates/c.txt | 1 + .../require-dir/test/duplicates/c/3.json | 1 + node_modules/require-dir/test/duplicates/d.js | 1 + .../require-dir/test/duplicates/d.json | 1 + node_modules/require-dir/test/index.js | 2 + node_modules/require-dir/test/recurse.js | 24 ++++ node_modules/require-dir/test/recurse/a.js | 1 + .../require-dir/test/recurse/b/1/bar.json | 1 + .../require-dir/test/recurse/b/1/foo.js | 1 + .../require-dir/test/recurse/b/2/baz.txt | 1 + .../require-dir/test/recurse/c/3.json | 1 + node_modules/require-dir/test/simple.js | 20 +++ node_modules/require-dir/test/simple/a.js | 1 + node_modules/require-dir/test/simple/b.json | 1 + node_modules/require-dir/test/simple/c.coffee | 1 + node_modules/require-dir/test/simple/d.txt | 1 + public/{stylesheets => css}/style.css | 0 public/js/native.history.js | 1 + routes/tasks.js | 9 ++ views/index.dust | 2 +- views/layout.dust | 4 +- 33 files changed, 413 insertions(+), 7 deletions(-) create mode 100644 node_modules/require-dir/.npmignore create mode 100644 node_modules/require-dir/README.md create mode 100644 node_modules/require-dir/index.js create mode 100644 node_modules/require-dir/package.json create mode 100644 node_modules/require-dir/test/duplicates.js create mode 100644 node_modules/require-dir/test/duplicates/a.js create mode 100644 node_modules/require-dir/test/duplicates/b.json create mode 100644 node_modules/require-dir/test/duplicates/b/1.js create mode 100644 node_modules/require-dir/test/duplicates/b/1.txt create mode 100644 node_modules/require-dir/test/duplicates/b/2.js create mode 100644 node_modules/require-dir/test/duplicates/b/2.json create mode 100644 node_modules/require-dir/test/duplicates/c.txt create mode 100644 node_modules/require-dir/test/duplicates/c/3.json create mode 100644 node_modules/require-dir/test/duplicates/d.js create mode 100644 node_modules/require-dir/test/duplicates/d.json create mode 100644 node_modules/require-dir/test/index.js create mode 100644 node_modules/require-dir/test/recurse.js create mode 100644 node_modules/require-dir/test/recurse/a.js create mode 100644 node_modules/require-dir/test/recurse/b/1/bar.json create mode 100644 node_modules/require-dir/test/recurse/b/1/foo.js create mode 100644 node_modules/require-dir/test/recurse/b/2/baz.txt create mode 100644 node_modules/require-dir/test/recurse/c/3.json create mode 100644 node_modules/require-dir/test/simple.js create mode 100644 node_modules/require-dir/test/simple/a.js create mode 100644 node_modules/require-dir/test/simple/b.json create mode 100644 node_modules/require-dir/test/simple/c.coffee create mode 100644 node_modules/require-dir/test/simple/d.txt rename public/{stylesheets => css}/style.css (100%) create mode 100644 public/js/native.history.js create mode 100644 routes/tasks.js diff --git a/app.js b/app.js index 310d605..8ffd572 100644 --- a/app.js +++ b/app.js @@ -4,9 +4,14 @@ var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); +var requireDir = require('require-dir'); var app = express(); +// General config +app.set('trust proxy', true); // Trust X-Forwarded-* headers + + // view engine setup var consolidate = require('consolidate'); var dust = require('dustjs-linkedin'); @@ -23,10 +28,13 @@ app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'), {redirect:false})); // Route mapping -var routes = require('./routes/index'); -var users = require('./routes/users'); -app.use('/', routes); -app.use('/users', users); +var routes = requireDir('routes'); +Object.keys(routes).forEach(function(route) { + var path = (route != 'index') + ? '/' + route + : '/'; + app.use(path, routes[route]); +}); // catch 404 and forward to error handler app.use(function(req, res, next) { @@ -60,4 +68,4 @@ app.use(function(err, req, res, next) { }); -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/node_modules/require-dir/.npmignore b/node_modules/require-dir/.npmignore new file mode 100644 index 0000000..688ef23 --- /dev/null +++ b/node_modules/require-dir/.npmignore @@ -0,0 +1,2 @@ +npm-debug.log +node_modules \ No newline at end of file diff --git a/node_modules/require-dir/README.md b/node_modules/require-dir/README.md new file mode 100644 index 0000000..ff2678d --- /dev/null +++ b/node_modules/require-dir/README.md @@ -0,0 +1,108 @@ +# requireDir() + +Node helper to `require()` directories. The directory's files are examined, +and each one that can be `require()`'d is `require()`'d and returned as part +of a hash from that file's basename to its exported contents. + +## Example + +Given this directory structure: + +``` +dir ++ a.js ++ b.json ++ c.coffee ++ d.txt +``` + +`requireDir('./dir')` will return the equivalent of: + +```js +{ a: require('./dir/a.js') +, b: require('./dir/b.json') +} +``` + +And if CoffeeScript was registered, `c.coffee` will also be returned. + +## Installation + +``` +npm install require-dir +``` + +Note that this package is *not* `requireDir` — turns out that's already +[taken](https://github.com/JamesEggers1/node-requiredir)! ;) + +## Usage + +Basic usage that examines only directories' immediate files: + +```js +var requireDir = require('require-dir'); +var dir = requireDir('./path/to/dir'); +``` + +You can optionally customize the behavior by passing an extra options object: + +```js +var dir = requireDir('./path/to/dir', {recurse: true}); +``` + +## Options + +`recurse`: Whether to recursively `require()` subdirectories too. +Default is false. + +`duplicates`: By default, if multiple files share the same basename, only the +highest priority one is `require()`'d and returned. (Priority is determined by +the order of `require.extensions` keys, with directories taking precedence +over files if `recurse` is true.) Specifying this option `require()`'s all +files and returns full filename keys in addition to basename keys. +Default is false. + +E.g. in the example above, if there were also an `a.json`, the behavior would +be the same by default, but specifying `duplicates: true` would yield: + +```js +{ a: require('./dir/a.js') +, 'a.js': require('./dir/a.js') +, 'a.json': require('./dir/a.json') +, b: require('./dir/b.json') +, 'b.json': require('./dir/b.json') +} +``` + +There might be more options in the future. ;) + +## Tips + +If you want to `require()` the same directory in multiple places, you can do +this in the directory itself! Just make an `index.js` file with the following: + +```js +module.exports = require('require-dir')(); // defaults to '.' +``` + +And don't worry, the calling file is always ignored to prevent infinite loops. + +## TODO + +It'd be awesome if this could work with the regular `require()`, e.g. like a +regular `require()` hook. Not sure that's possible though; directories are +already special-cased to look for an `index` file or `package.json`. + +An `ignore` option would be nice: a string or regex, or an array of either or +both, of paths, relative to the directory, to ignore. String paths can be +extensionless to ignore all extensions for that path. Supporting shell-style +globs in string paths would be nice. + +Currently, basenames are derived for directories too — e.g. a directory named +`a.txt` will be returned as `a` when recursing — but should that be the case? +Maybe directories should always be returned by their full name, and/or maybe +this behavior should be customizable. This is hopefully an edge case. + +## License + +MIT. © 2012 Aseem Kishore. diff --git a/node_modules/require-dir/index.js b/node_modules/require-dir/index.js new file mode 100644 index 0000000..8631261 --- /dev/null +++ b/node_modules/require-dir/index.js @@ -0,0 +1,120 @@ +// requireDir.js +// See README.md for details. + +var FS = require('fs'); +var Path = require('path'); + +// make a note of the calling file's path, so that we can resolve relative +// paths. this only works if a fresh version of this module is run on every +// require(), so important: we clear the require() cache each time! +var parent = module.parent; +var parentFile = parent.filename; +var parentDir = Path.dirname(parentFile); +delete require.cache[__filename]; + +module.exports = function requireDir(dir, opts) { + // default arguments: + dir = dir || '.'; + opts = opts || {}; + + // resolve the path to an absolute one: + dir = Path.resolve(parentDir, dir); + + // read the directory's files: + // note that this'll throw an error if the path isn't a directory. + var files = FS.readdirSync(dir); + + // to prioritize between multiple files with the same basename, we'll + // first derive all the basenames and create a map from them to files: + var filesForBase = {}; + + for (var i = 0; i < files.length; i++) { + var file = files[i]; + var ext = Path.extname(file); + var base = Path.basename(file, ext); + + (filesForBase[base] = filesForBase[base] || []).push(file); + } + + // then we'll go through each basename, and first check if any of the + // basenames' files are directories, since directories take precedence if + // we're recursing and can be ignored if we're not. if a basename has no + // directory, then we'll follow Node's own require() algorithm of going + // through and trying the require.extension keys in order. in the process, + // we create and return a map from basename to require()'d contents! and + // if duplicates are asked for, we'll never short-circuit; we'll just add + // to the map using the full filename as a key also. + var map = {}; + + for (var base in filesForBase) { + // protect against enumerable object prototype extensions: + if (!filesForBase.hasOwnProperty(base)) { + continue; + } + + // go through the files for this base and check for directories. we'll + // also create a hash "set" of the non-dir files so that we can + // efficiently check for existence in the next step: + var files = filesForBase[base]; + var filesMinusDirs = {}; + + for (var i = 0; i < files.length; i++) { + var file = files[i]; + var path = Path.resolve(dir, file); + + // ignore the calling file: + if (path === parentFile) { + continue; + } + + if (FS.statSync(path).isDirectory()) { + if (opts.recurse) { + map[base] = requireDir(path, opts); + + // if duplicates are wanted, key off the full name too: + if (opts.duplicates) { + map[file] = map[base]; + } + } + } else { + filesMinusDirs[file] = path; + } + } + + // if we're recursing and we already encountered a directory for this + // basename, we're done for this base if we're ignoring duplicates: + if (map[base] && !opts.duplicates) { + continue; + } + + // otherwise, go through and try each require.extension key! + for (ext in require.extensions) { + // again protect against enumerable object prototype extensions: + if (!require.extensions.hasOwnProperty(ext)) { + continue; + } + + // if a file exists with this extension, we'll require() it: + var file = base + ext; + var path = filesMinusDirs[file]; + + if (path) { + // if duplicates are wanted, key off the full name always, and + // also the base if it hasn't been taken yet (since this ext + // has higher priority than any that follow it). if duplicates + // aren't wanted, we're done with this basename. + if (opts.duplicates) { + map[file] = require(path); + if (!map[base]) { + map[base] = map[file]; + } + } else { + map[base] = require(path); + break; + } + } + } + } + + return map; +}; diff --git a/node_modules/require-dir/package.json b/node_modules/require-dir/package.json new file mode 100644 index 0000000..8c2f15b --- /dev/null +++ b/node_modules/require-dir/package.json @@ -0,0 +1,48 @@ +{ + "name": "require-dir", + "description": "Helper to require() directories.", + "version": "0.1.0", + "author": { + "name": "Aseem Kishore", + "email": "aseem.kishore@gmail.com" + }, + "dependencies": {}, + "devDependencies": { + "coffee-script": "~1.3.3" + }, + "engines": { + "node": "*" + }, + "scripts": { + "test": "node test" + }, + "homepage": "https://github.com/aseemk/requireDir", + "repository": { + "type": "git", + "url": "git://github.com/aseemk/requireDir.git" + }, + "_npmUser": { + "name": "aseemk", + "email": "aseem.kishore@gmail.com" + }, + "_id": "require-dir@0.1.0", + "optionalDependencies": {}, + "_engineSupported": true, + "_npmVersion": "1.1.21", + "_nodeVersion": "v0.6.16", + "_defaultsLoaded": true, + "dist": { + "shasum": "81e01e299faf5b74c34b6594f8e5add5985ddec5", + "tarball": "http://registry.npmjs.org/require-dir/-/require-dir-0.1.0.tgz" + }, + "maintainers": [ + { + "name": "aseemk", + "email": "aseem.kishore@gmail.com" + } + ], + "directories": {}, + "_shasum": "81e01e299faf5b74c34b6594f8e5add5985ddec5", + "_from": "require-dir@", + "_resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.1.0.tgz" +} diff --git a/node_modules/require-dir/test/duplicates.js b/node_modules/require-dir/test/duplicates.js new file mode 100644 index 0000000..ec0db97 --- /dev/null +++ b/node_modules/require-dir/test/duplicates.js @@ -0,0 +1,43 @@ +var assert = require('assert'); +var requireDir = require('..'); + +// first test without recursing *or* duplicates: +assert.deepEqual(requireDir('./duplicates'), { + a: 'a.js', + b: 'b.json', + d: 'd.js', +}); + +// then test with duplicates but without recursing: +assert.deepEqual(requireDir('./duplicates', {duplicates: true}), { + a: 'a.js', + 'a.js': 'a.js', + b: 'b.json', + 'b.json': 'b.json', + d: 'd.js', + 'd.js': 'd.js', + 'd.json': 'd.json', +}); + +// finally, test with duplicates while recursing: +assert.deepEqual(requireDir('./duplicates', {duplicates: true, recurse: true}), { + a: 'a.js', + 'a.js': 'a.js', + b: { + '1': '1.js', + '1.js': '1.js', + '2': '2.js', + '2.js': '2.js', + '2.json': '2.json', + }, + 'b.json': 'b.json', + c: { + '3': '3.json', + '3.json': '3.json', + }, + d: 'd.js', + 'd.js': 'd.js', + 'd.json': 'd.json', +}); + +console.log('Duplicate tests passed.'); diff --git a/node_modules/require-dir/test/duplicates/a.js b/node_modules/require-dir/test/duplicates/a.js new file mode 100644 index 0000000..735d820 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/a.js @@ -0,0 +1 @@ +module.exports = 'a.js'; diff --git a/node_modules/require-dir/test/duplicates/b.json b/node_modules/require-dir/test/duplicates/b.json new file mode 100644 index 0000000..fb6f7a0 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/b.json @@ -0,0 +1 @@ +"b.json" diff --git a/node_modules/require-dir/test/duplicates/b/1.js b/node_modules/require-dir/test/duplicates/b/1.js new file mode 100644 index 0000000..ca7daaa --- /dev/null +++ b/node_modules/require-dir/test/duplicates/b/1.js @@ -0,0 +1 @@ +module.exports = '1.js'; diff --git a/node_modules/require-dir/test/duplicates/b/1.txt b/node_modules/require-dir/test/duplicates/b/1.txt new file mode 100644 index 0000000..d1a2268 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/b/1.txt @@ -0,0 +1 @@ +'1.txt' diff --git a/node_modules/require-dir/test/duplicates/b/2.js b/node_modules/require-dir/test/duplicates/b/2.js new file mode 100644 index 0000000..9ba2ca7 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/b/2.js @@ -0,0 +1 @@ +module.exports = '2.js'; diff --git a/node_modules/require-dir/test/duplicates/b/2.json b/node_modules/require-dir/test/duplicates/b/2.json new file mode 100644 index 0000000..f4bf2d0 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/b/2.json @@ -0,0 +1 @@ +"2.json" diff --git a/node_modules/require-dir/test/duplicates/c.txt b/node_modules/require-dir/test/duplicates/c.txt new file mode 100644 index 0000000..123457d --- /dev/null +++ b/node_modules/require-dir/test/duplicates/c.txt @@ -0,0 +1 @@ +'c.txt' diff --git a/node_modules/require-dir/test/duplicates/c/3.json b/node_modules/require-dir/test/duplicates/c/3.json new file mode 100644 index 0000000..7d718fa --- /dev/null +++ b/node_modules/require-dir/test/duplicates/c/3.json @@ -0,0 +1 @@ +"3.json" diff --git a/node_modules/require-dir/test/duplicates/d.js b/node_modules/require-dir/test/duplicates/d.js new file mode 100644 index 0000000..2b74cb6 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/d.js @@ -0,0 +1 @@ +module.exports = 'd.js'; diff --git a/node_modules/require-dir/test/duplicates/d.json b/node_modules/require-dir/test/duplicates/d.json new file mode 100644 index 0000000..bfa3928 --- /dev/null +++ b/node_modules/require-dir/test/duplicates/d.json @@ -0,0 +1 @@ +"d.json" diff --git a/node_modules/require-dir/test/index.js b/node_modules/require-dir/test/index.js new file mode 100644 index 0000000..14e6fb7 --- /dev/null +++ b/node_modules/require-dir/test/index.js @@ -0,0 +1,2 @@ +require('..')('.'); +console.log('\nAll tests passed!'); diff --git a/node_modules/require-dir/test/recurse.js b/node_modules/require-dir/test/recurse.js new file mode 100644 index 0000000..2bd953f --- /dev/null +++ b/node_modules/require-dir/test/recurse.js @@ -0,0 +1,24 @@ +var assert = require('assert'); +var requireDir = require('..'); + +// first test without recursing: +assert.deepEqual(requireDir('./recurse'), { + a: 'a', +}); + +// then test with recursing: +assert.deepEqual(requireDir('./recurse', {recurse: true}), { + a: 'a', + b: { + '1': { + foo: 'foo', + bar: 'bar', + }, + '2': {} // note how the directory is always returned + }, + c: { + '3': 3 + }, +}); + +console.log('Recurse tests passed.'); diff --git a/node_modules/require-dir/test/recurse/a.js b/node_modules/require-dir/test/recurse/a.js new file mode 100644 index 0000000..2e7700b --- /dev/null +++ b/node_modules/require-dir/test/recurse/a.js @@ -0,0 +1 @@ +module.exports = 'a'; diff --git a/node_modules/require-dir/test/recurse/b/1/bar.json b/node_modules/require-dir/test/recurse/b/1/bar.json new file mode 100644 index 0000000..196e587 --- /dev/null +++ b/node_modules/require-dir/test/recurse/b/1/bar.json @@ -0,0 +1 @@ +"bar" diff --git a/node_modules/require-dir/test/recurse/b/1/foo.js b/node_modules/require-dir/test/recurse/b/1/foo.js new file mode 100644 index 0000000..2651774 --- /dev/null +++ b/node_modules/require-dir/test/recurse/b/1/foo.js @@ -0,0 +1 @@ +module.exports = 'foo'; diff --git a/node_modules/require-dir/test/recurse/b/2/baz.txt b/node_modules/require-dir/test/recurse/b/2/baz.txt new file mode 100644 index 0000000..7601807 --- /dev/null +++ b/node_modules/require-dir/test/recurse/b/2/baz.txt @@ -0,0 +1 @@ +baz diff --git a/node_modules/require-dir/test/recurse/c/3.json b/node_modules/require-dir/test/recurse/c/3.json new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/node_modules/require-dir/test/recurse/c/3.json @@ -0,0 +1 @@ +3 diff --git a/node_modules/require-dir/test/simple.js b/node_modules/require-dir/test/simple.js new file mode 100644 index 0000000..31e9458 --- /dev/null +++ b/node_modules/require-dir/test/simple.js @@ -0,0 +1,20 @@ +var assert = require('assert'); +var requireDir = require('..'); + +// first test regularly: +assert.deepEqual(requireDir('./simple'), { + a: 'a', + b: 'b', +}); + +// now register CoffeeScript and do it again: +// note that CoffeeScript shouldn't be used by any other tests! we can't rely +// on ordering of tests, and require.extensions and require.cache are global. +require('coffee-script'); +assert.deepEqual(requireDir('./simple'), { + a: 'a', + b: 'b', + c: 'c', +}); + +console.log('Simple tests passed.'); diff --git a/node_modules/require-dir/test/simple/a.js b/node_modules/require-dir/test/simple/a.js new file mode 100644 index 0000000..2e7700b --- /dev/null +++ b/node_modules/require-dir/test/simple/a.js @@ -0,0 +1 @@ +module.exports = 'a'; diff --git a/node_modules/require-dir/test/simple/b.json b/node_modules/require-dir/test/simple/b.json new file mode 100644 index 0000000..19010cc --- /dev/null +++ b/node_modules/require-dir/test/simple/b.json @@ -0,0 +1 @@ +"b" diff --git a/node_modules/require-dir/test/simple/c.coffee b/node_modules/require-dir/test/simple/c.coffee new file mode 100644 index 0000000..7212f4d --- /dev/null +++ b/node_modules/require-dir/test/simple/c.coffee @@ -0,0 +1 @@ +module.exports = 'c' diff --git a/node_modules/require-dir/test/simple/d.txt b/node_modules/require-dir/test/simple/d.txt new file mode 100644 index 0000000..4bcfe98 --- /dev/null +++ b/node_modules/require-dir/test/simple/d.txt @@ -0,0 +1 @@ +d diff --git a/public/stylesheets/style.css b/public/css/style.css similarity index 100% rename from public/stylesheets/style.css rename to public/css/style.css diff --git a/public/js/native.history.js b/public/js/native.history.js new file mode 100644 index 0000000..bb23acd --- /dev/null +++ b/public/js/native.history.js @@ -0,0 +1 @@ +(function(e,t){"use strict";var n=e.History=e.History||{};if(typeof n.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");n.Adapter={handlers:{},_uid:1,uid:function(e){return e._uid||(e._uid=n.Adapter._uid++)},bind:function(e,t,r){var i=n.Adapter.uid(e);n.Adapter.handlers[i]=n.Adapter.handlers[i]||{},n.Adapter.handlers[i][t]=n.Adapter.handlers[i][t]||[],n.Adapter.handlers[i][t].push(r),e["on"+t]=function(e,t){return function(r){n.Adapter.trigger(e,t,r)}}(e,t)},trigger:function(e,t,r){r=r||{};var i=n.Adapter.uid(e),s,o;n.Adapter.handlers[i]=n.Adapter.handlers[i]||{},n.Adapter.handlers[i][t]=n.Adapter.handlers[i][t]||[];for(s=0,o=n.Adapter.handlers[i][t].length;s")&&n[0]);return e>4?e:!1}();return e},h.isInternetExplorer=function(){var e=h.isInternetExplorer.cached=typeof h.isInternetExplorer.cached!="undefined"?h.isInternetExplorer.cached:Boolean(h.getInternetExplorerMajorVersion());return e},h.options.html4Mode?h.emulated={pushState:!0,hashChange:!0}:h.emulated={pushState:!Boolean(e.history&&e.history.pushState&&e.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(i.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(i.userAgent)),hashChange:Boolean(!("onhashchange"in e||"onhashchange"in r)||h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8)},h.enabled=!h.emulated.pushState,h.bugs={setHash:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),safariPoll:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),ieDoubleCheck:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<7)},h.isEmptyObject=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},h.cloneObject=function(e){var t,n;return e?(t=l.stringify(e),n=l.parse(t)):n={},n},h.getRootUrl=function(){var e=r.location.protocol+"//"+(r.location.hostname||r.location.host);if(r.location.port||!1)e+=":"+r.location.port;return e+="/",e},h.getBaseHref=function(){var e=r.getElementsByTagName("base"),t=null,n="";return e.length===1&&(t=e[0],n=t.href.replace(/[^\/]+$/,"")),n=n.replace(/\/+$/,""),n&&(n+="/"),n},h.getBaseUrl=function(){var e=h.getBaseHref()||h.getBasePageUrl()||h.getRootUrl();return e},h.getPageUrl=function(){var e=h.getState(!1,!1),t=(e||{}).url||h.getLocationHref(),n;return n=t.replace(/\/+$/,"").replace(/[^\/]+$/,function(e,t,n){return/\./.test(e)?e:e+"/"}),n},h.getBasePageUrl=function(){var e=h.getLocationHref().replace(/[#\?].*/,"").replace(/[^\/]+$/,function(e,t,n){return/[^\/]$/.test(e)?"":e}).replace(/\/+$/,"")+"/";return e},h.getFullUrl=function(e,t){var n=e,r=e.substring(0,1);return t=typeof t=="undefined"?!0:t,/[a-z]+\:\/\//.test(e)||(r==="/"?n=h.getRootUrl()+e.replace(/^\/+/,""):r==="#"?n=h.getPageUrl().replace(/#.*/,"")+e:r==="?"?n=h.getPageUrl().replace(/[\?#].*/,"")+e:t?n=h.getBaseUrl()+e.replace(/^(\.\/)+/,""):n=h.getBasePageUrl()+e.replace(/^(\.\/)+/,"")),n.replace(/\#$/,"")},h.getShortUrl=function(e){var t=e,n=h.getBaseUrl(),r=h.getRootUrl();return h.emulated.pushState&&(t=t.replace(n,"")),t=t.replace(r,"/"),h.isTraditionalAnchor(t)&&(t="./"+t),t=t.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),t},h.getLocationHref=function(e){return e=e||r,e.URL===e.location.href?e.location.href:e.location.href===decodeURIComponent(e.URL)?e.URL:e.location.hash&&decodeURIComponent(e.location.href.replace(/^[^#]+/,""))===e.location.hash?e.location.href:e.URL.indexOf("#")==-1&&e.location.href.indexOf("#")!=-1?e.location.href:e.URL||e.location.href},h.store={},h.idToState=h.idToState||{},h.stateToId=h.stateToId||{},h.urlToId=h.urlToId||{},h.storedStates=h.storedStates||[],h.savedStates=h.savedStates||[],h.normalizeStore=function(){h.store.idToState=h.store.idToState||{},h.store.urlToId=h.store.urlToId||{},h.store.stateToId=h.store.stateToId||{}},h.getState=function(e,t){typeof e=="undefined"&&(e=!0),typeof t=="undefined"&&(t=!0);var n=h.getLastSavedState();return!n&&t&&(n=h.createStateObject()),e&&(n=h.cloneObject(n),n.url=n.cleanUrl||n.url),n},h.getIdByState=function(e){var t=h.extractId(e.url),n;if(!t){n=h.getStateString(e);if(typeof h.stateToId[n]!="undefined")t=h.stateToId[n];else if(typeof h.store.stateToId[n]!="undefined")t=h.store.stateToId[n];else{for(;;){t=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof h.idToState[t]=="undefined"&&typeof h.store.idToState[t]=="undefined")break}h.stateToId[n]=t,h.idToState[t]=e}}return t},h.normalizeState=function(e){var t,n;if(!e||typeof e!="object")e={};if(typeof e.normalized!="undefined")return e;if(!e.data||typeof e.data!="object")e.data={};return t={},t.normalized=!0,t.title=e.title||"",t.url=h.getFullUrl(e.url?e.url:h.getLocationHref()),t.hash=h.getShortUrl(t.url),t.data=h.cloneObject(e.data),t.id=h.getIdByState(t),t.cleanUrl=t.url.replace(/\??\&_suid.*/,""),t.url=t.cleanUrl,n=!h.isEmptyObject(t.data),(t.title||n)&&h.options.disableSuid!==!0&&(t.hash=h.getShortUrl(t.url).replace(/\??\&_suid.*/,""),/\?/.test(t.hash)||(t.hash+="?"),t.hash+="&_suid="+t.id),t.hashedUrl=h.getFullUrl(t.hash),(h.emulated.pushState||h.bugs.safariPoll)&&h.hasUrlDuplicate(t)&&(t.url=t.hashedUrl),t},h.createStateObject=function(e,t,n){var r={data:e,title:t,url:n};return r=h.normalizeState(r),r},h.getStateById=function(e){e=String(e);var n=h.idToState[e]||h.store.idToState[e]||t;return n},h.getStateString=function(e){var t,n,r;return t=h.normalizeState(e),n={data:t.data,title:e.title,url:e.url},r=l.stringify(n),r},h.getStateId=function(e){var t,n;return t=h.normalizeState(e),n=t.id,n},h.getHashByState=function(e){var t,n;return t=h.normalizeState(e),n=t.hash,n},h.extractId=function(e){var t,n,r,i;return e.indexOf("#")!=-1?i=e.split("#")[0]:i=e,n=/(.*)\&_suid=([0-9]+)$/.exec(i),r=n?n[1]||e:e,t=n?String(n[2]||""):"",t||!1},h.isTraditionalAnchor=function(e){var t=!/[\/\?\.]/.test(e);return t},h.extractState=function(e,t){var n=null,r,i;return t=t||!1,r=h.extractId(e),r&&(n=h.getStateById(r)),n||(i=h.getFullUrl(e),r=h.getIdByUrl(i)||!1,r&&(n=h.getStateById(r)),!n&&t&&!h.isTraditionalAnchor(e)&&(n=h.createStateObject(null,null,i))),n},h.getIdByUrl=function(e){var n=h.urlToId[e]||h.store.urlToId[e]||t;return n},h.getLastSavedState=function(){return h.savedStates[h.savedStates.length-1]||t},h.getLastStoredState=function(){return h.storedStates[h.storedStates.length-1]||t},h.hasUrlDuplicate=function(e){var t=!1,n;return n=h.extractState(e.url),t=n&&n.id!==e.id,t},h.storeState=function(e){return h.urlToId[e.url]=e.id,h.storedStates.push(h.cloneObject(e)),e},h.isLastSavedState=function(e){var t=!1,n,r,i;return h.savedStates.length&&(n=e.id,r=h.getLastSavedState(),i=r.id,t=n===i),t},h.saveState=function(e){return h.isLastSavedState(e)?!1:(h.savedStates.push(h.cloneObject(e)),!0)},h.getStateByIndex=function(e){var t=null;return typeof e=="undefined"?t=h.savedStates[h.savedStates.length-1]:e<0?t=h.savedStates[h.savedStates.length+e]:t=h.savedStates[e],t},h.getCurrentIndex=function(){var e=null;return h.savedStates.length<1?e=0:e=h.savedStates.length-1,e},h.getHash=function(e){var t=h.getLocationHref(e),n;return n=h.getHashByUrl(t),n},h.unescapeHash=function(e){var t=h.normalizeHash(e);return t=decodeURIComponent(t),t},h.normalizeHash=function(e){var t=e.replace(/[^#]*#/,"").replace(/#.*/,"");return t},h.setHash=function(e,t){var n,i;return t!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.setHash,args:arguments,queue:t}),!1):(h.busy(!0),n=h.extractState(e,!0),n&&!h.emulated.pushState?h.pushState(n.data,n.title,n.url,!1):h.getHash()!==e&&(h.bugs.setHash?(i=h.getPageUrl(),h.pushState(null,null,i+"#"+e,!1)):r.location.hash=e),h)},h.escapeHash=function(t){var n=h.normalizeHash(t);return n=e.encodeURIComponent(n),h.bugs.hashEscape||(n=n.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),n},h.getHashByUrl=function(e){var t=String(e).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return t=h.unescapeHash(t),t},h.setTitle=function(e){var t=e.title,n;t||(n=h.getStateByIndex(0),n&&n.url===e.url&&(t=n.title||h.options.initialTitle));try{r.getElementsByTagName("title")[0].innerHTML=t.replace("<","<").replace(">",">").replace(" & "," & ")}catch(i){}return r.title=t,h},h.queues=[],h.busy=function(e){typeof e!="undefined"?h.busy.flag=e:typeof h.busy.flag=="undefined"&&(h.busy.flag=!1);if(!h.busy.flag){u(h.busy.timeout);var t=function(){var e,n,r;if(h.busy.flag)return;for(e=h.queues.length-1;e>=0;--e){n=h.queues[e];if(n.length===0)continue;r=n.shift(),h.fireQueueItem(r),h.busy.timeout=o(t,h.options.busyDelay)}};h.busy.timeout=o(t,h.options.busyDelay)}return h.busy.flag},h.busy.flag=!1,h.fireQueueItem=function(e){return e.callback.apply(e.scope||h,e.args||[])},h.pushQueue=function(e){return h.queues[e.queue||0]=h.queues[e.queue||0]||[],h.queues[e.queue||0].push(e),h},h.queue=function(e,t){return typeof e=="function"&&(e={callback:e}),typeof t!="undefined"&&(e.queue=t),h.busy()?h.pushQueue(e):h.fireQueueItem(e),h},h.clearQueue=function(){return h.busy.flag=!1,h.queues=[],h},h.stateChanged=!1,h.doubleChecker=!1,h.doubleCheckComplete=function(){return h.stateChanged=!0,h.doubleCheckClear(),h},h.doubleCheckClear=function(){return h.doubleChecker&&(u(h.doubleChecker),h.doubleChecker=!1),h},h.doubleCheck=function(e){return h.stateChanged=!1,h.doubleCheckClear(),h.bugs.ieDoubleCheck&&(h.doubleChecker=o(function(){return h.doubleCheckClear(),h.stateChanged||e(),!0},h.options.doubleCheckInterval)),h},h.safariStatePoll=function(){var t=h.extractState(h.getLocationHref()),n;if(!h.isLastSavedState(t))return n=t,n||(n=h.createStateObject()),h.Adapter.trigger(e,"popstate"),h;return},h.back=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.back,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.back(!1)}),p.go(-1),!0)},h.forward=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.forward,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.forward(!1)}),p.go(1),!0)},h.go=function(e,t){var n;if(e>0)for(n=1;n<=e;++n)h.forward(t);else{if(!(e<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(n=-1;n>=e;--n)h.back(t)}return h};if(h.emulated.pushState){var v=function(){};h.pushState=h.pushState||v,h.replaceState=h.replaceState||v}else h.onPopState=function(t,n){var r=!1,i=!1,s,o;return h.doubleCheckComplete(),s=h.getHash(),s?(o=h.extractState(s||h.getLocationHref(),!0),o?h.replaceState(o.data,o.title,o.url,!1):(h.Adapter.trigger(e,"anchorchange"),h.busy(!1)),h.expectedStateId=!1,!1):(r=h.Adapter.extractEventData("state",t,n)||!1,r?i=h.getStateById(r):h.expectedStateId?i=h.getStateById(h.expectedStateId):i=h.extractState(h.getLocationHref()),i||(i=h.createStateObject(null,null,h.getLocationHref())),h.expectedStateId=!1,h.isLastSavedState(i)?(h.busy(!1),!1):(h.storeState(i),h.saveState(i),h.setTitle(i),h.Adapter.trigger(e,"statechange"),h.busy(!1),!0))},h.Adapter.bind(e,"popstate",h.onPopState),h.pushState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.pushState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.pushState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0},h.replaceState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.replaceState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.replaceState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0};if(s){try{h.store=l.parse(s.getItem("History.store"))||{}}catch(m){h.store={}}h.normalizeStore()}else h.store={},h.normalizeStore();h.Adapter.bind(e,"unload",h.clearAllIntervals),h.saveState(h.storeState(h.extractState(h.getLocationHref(),!0))),s&&(h.onUnload=function(){var e,t,n;try{e=l.parse(s.getItem("History.store"))||{}}catch(r){e={}}e.idToState=e.idToState||{},e.urlToId=e.urlToId||{},e.stateToId=e.stateToId||{};for(t in h.idToState){if(!h.idToState.hasOwnProperty(t))continue;e.idToState[t]=h.idToState[t]}for(t in h.urlToId){if(!h.urlToId.hasOwnProperty(t))continue;e.urlToId[t]=h.urlToId[t]}for(t in h.stateToId){if(!h.stateToId.hasOwnProperty(t))continue;e.stateToId[t]=h.stateToId[t]}h.store=e,h.normalizeStore(),n=l.stringify(e);try{s.setItem("History.store",n)}catch(i){if(i.code!==DOMException.QUOTA_EXCEEDED_ERR)throw i;s.length&&(s.removeItem("History.store"),s.setItem("History.store",n))}},h.intervalList.push(a(h.onUnload,h.options.storeInterval)),h.Adapter.bind(e,"beforeunload",h.onUnload),h.Adapter.bind(e,"unload",h.onUnload));if(!h.emulated.pushState){h.bugs.safariPoll&&h.intervalList.push(a(h.safariStatePoll,h.options.safariPollInterval));if(i.vendor==="Apple Computer, Inc."||(i.appCodeName||"")==="Mozilla")h.Adapter.bind(e,"hashchange",function(){h.Adapter.trigger(e,"popstate")}),h.getHash()&&h.Adapter.onDomLoad(function(){h.Adapter.trigger(e,"hashchange")})}},(!h.options||!h.options.delayInit)&&h.init()}(window) \ No newline at end of file diff --git a/routes/tasks.js b/routes/tasks.js new file mode 100644 index 0000000..8b645fb --- /dev/null +++ b/routes/tasks.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET list of tasks */ +router.get('/', function(req, res) { + res.json([{}]); +}); + +module.exports = router; diff --git a/views/index.dust b/views/index.dust index 1470928..ad973c4 100644 --- a/views/index.dust +++ b/views/index.dust @@ -1,6 +1,6 @@ {>layout/} {{title} +

{title}

Welcome to {title}

{/content} diff --git a/views/layout.dust b/views/layout.dust index 57c4780..61f2b8e 100644 --- a/views/layout.dust +++ b/views/layout.dust @@ -2,7 +2,7 @@ {title} - + {+content} @@ -12,4 +12,6 @@ + +