Commit 7a6824f5 authored by Addy Osmani's avatar Addy Osmani

Updating to latest official Polymer TodoMVC implementation

parent f7db3237
......@@ -20,14 +20,14 @@ body {
font-smoothing: antialiased;
}
body > section {
position: relative;
margin: 130px 0 40px 0;
body > header {
padding-top: 22px;
margin-bottom: -5px;
}
h1 {
position: absolute;
top: -120px;
/* position: absolute;
top: -120px;*/
width: 100%;
font-size: 70px;
font-weight: bold;
......@@ -185,6 +185,12 @@ hr {
transition-duration: 500ms;
}
/* IE doesn't support the hidden attribute */
[hidden] {
display: none;
}
@media (min-width: 899px) {
/**body*/.learn-bar {
width: auto;
......@@ -193,7 +199,7 @@ hr {
/**body*/.learn-bar > .learn {
left: 8px;
}
/**body*/.learn-bar > section {
/**body*/.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
......
{
"name": "director",
"homepage": "https://github.com/flatiron/director",
"version": "1.2.0",
"_release": "1.2.0",
"_resolution": {
"type": "version",
"tag": "v1.2.0",
"commit": "538dee97b0d57163d682a397de674f36af4d16a1"
},
"_source": "git://github.com/flatiron/director.git",
"_target": "*"
}
\ No newline at end of file
/.idea/
node_modules
npm-debug.log
.DS_Store
/test/browser/browserified-bundle.js
language: node_js
node_js:
- 0.6
- 0.8
- "0.10"
notifications:
email:
- travis@nodejitsu.com
irc: "irc.freenode.org#nodejitsu"
Copyright (c) 2011 Nodejitsu 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.
\ No newline at end of file
#!/usr/bin/env node
var Codesurgeon = require('codesurgeon').Codesurgeon;
var surgeon = new Codesurgeon;
var path = require('path');
var root = path.join(__dirname, '..');
var lib = path.join(root, 'lib', 'director');
//
// Distill and package the browser version.
//
surgeon
//
.configure({
package: root + '/package.json',
owner: 'Nodejitsu, Inc (Using Codesurgeon).',
noVersion: true
})
.read(
path.join(lib, 'browser.js')
)
//
// we want everything so far. specify extract with no
// parameters to get everything into the output buffer.
//
.extract()
//
// clear the input so far, but don't clear the output.
//
.clear('inputs')
//
// read the `router.js` file
//
.read(
path.join(lib, 'router.js')
)
//
// the current input buffer contains stuff that we dont
// want in the browser build, so let's cherry pick from
// the buffer.
//
.extract(
'_every',
'_flatten',
'_asyncEverySeries',
'paramifyString',
'regifyString',
'terminator',
'Router.prototype.configure',
'Router.prototype.param',
'Router.prototype.on',
'Router.prototype.dispatch',
'Router.prototype.invoke',
'Router.prototype.traverse',
'Router.prototype.insert',
'Router.prototype.insertEx',
'Router.prototype.extend',
'Router.prototype.runlist',
'Router.prototype.mount'
)
//
// wrap everything that is in the current buffer with a
// closure so that we dont get any collisions with other
// libraries
//
.wrap()
//
// write the debuggable version of the file. This file will
// get renamed to include the version from the package.json
//
.write(root + '/build/director.js')
//
// now lets make a minified version for production use.
//
.uglify()
.write(root + '/build/director.min.js')
;
var http = require('http'),
director = require('../lib/director');
var router = new director.http.Router();
var server = http.createServer(function (req, res) {
req.chunks = [];
req.on('data', function (chunk) {
req.chunks.push(chunk.toString());
});
router.dispatch(req, res, function (err) {
if (err) {
res.writeHead(404);
res.end();
}
console.log('Served ' + req.url);
});
});
router.get(/foo/, function () {
this.res.writeHead(200, { 'Content-Type': 'text/plain' });
this.res.end('hello world\n');
});
router.post(/foo/, function () {
this.res.writeHead(200, { 'Content-Type': 'application/json' });
this.res.end(JSON.stringify(this.req.body));
});
server.listen(8080);
console.log('vanilla http server with director running on 8080');
exports.Router = require('./director/router').Router;
exports.http = require('./director/http');
exports.cli = require('./director/cli');
/*
* browser.js: Browser specific functionality for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location;
function dlocHashEmpty() {
// Non-IE browsers return '' when the address bar shows '#'; Director's logic
// assumes both mean empty.
return dloc.hash === '' || dloc.hash === '#';
}
var listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check: function () {
var h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire: function () {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
init: function (fn, history) {
var self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (var i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
}
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = onchange;
}, 500);
}
else {
window.onhashchange = onchange;
}
this.mode = 'modern';
}
else {
//
// IE support, based on a concept by Erik Arvidson ...
//
var frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () { self.check(); }, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy: function (fn) {
if (!Router || !Router.listeners) {
return;
}
var listeners = Router.listeners;
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash: function (s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s);
// Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = (s[0] === '/') ? s : '/' + s;
}
return this;
},
writeFrame: function (s) {
// IE support...
var f = document.getElementById('state-frame');
var d = f.contentDocument || f.contentWindow.document;
d.open();
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
d.close();
},
syncHash: function () {
// IE support...
var s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged: function () {}
};
var Router = exports.Router = function (routes) {
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, ''));
}
}
else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
// Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
var v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
Router.prototype.setRoute = function (i, v, val) {
var url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
}
else if (typeof val === 'string') {
url.splice(i, v, s);
}
else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
if (method === "once") {
method = "on";
route = function(route) {
var once = false;
return function() {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
var ret = v;
if (typeof v === "number") {
ret = this.explode()[v];
}
else if (typeof v === "string"){
var h = this.explode();
ret = h.indexOf(v);
}
else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
var path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = '/' + path;
}
return path;
};
var util = require('util'),
director = require('../director');
var Router = exports.Router = function (routes) {
director.Router.call(this, routes);
this.recurse = false;
};
//
// Inherit from `director.Router`.
//
util.inherits(Router, director.Router);
//
// ### function configure (options)
// #### @options {Object} **Optional** Options to configure this instance with
// Configures this instance with the specified `options`.
//
Router.prototype.configure = function (options) {
options = options || {};
director.Router.prototype.configure.call(this, options);
//
// Delimiter must always be `\s` in CLI routing.
// e.g. `jitsu users create`
//
this.delimiter = '\\s';
return this;
};
//
// ### function dispatch (method, path)
// #### @method {string} Method to dispatch
// #### @path {string} Path to dispatch
// Finds a set of functions on the traversal towards
// `method` and `path` in the core routing table then
// invokes them based on settings in this instance.
//
Router.prototype.dispatch = function (method, path, tty, callback) {
//
// Prepend a single space onto the path so that the traversal
// algorithm will recognize it. This is because we always assume
// that the `path` begins with `this.delimiter`.
//
path = ' ' + path;
var fns = this.traverse(method, path, this.routes, '');
if (!fns || fns.length === 0) {
if (typeof this.notfound === 'function') {
this.notfound.call({ tty: tty, cmd: path }, callback);
}
else if (callback) {
callback(new Error('Could not find path: ' + path));
}
return false;
}
if (this.recurse === 'forward') {
fns = fns.reverse();
}
this.invoke(this.runlist(fns), { tty: tty, cmd: path }, callback);
return true;
};
var events = require('events'),
qs = require('querystring'),
util = require('util'),
director = require('../../director'),
responses = require('./responses');
//
// ### Expose all HTTP methods and responses
//
exports.methods = require('./methods');
Object.keys(responses).forEach(function (name) {
exports[name] = responses[name];
});
//
// ### function Router (routes)
// #### @routes {Object} **Optional** Routing table for this instance.
// Constuctor function for the HTTP Router object responsible for building
// and dispatching from a given routing table.
//
var Router = exports.Router = function (routes) {
//
// ### Extend the `Router` prototype with all of the RFC methods.
//
this.params = {};
this.routes = {};
this.methods = ['on', 'after', 'before'];
this.scope = [];
this._methods = {};
this.recurse = 'forward';
this._attach = [];
this.extend(exports.methods.concat(['before', 'after']));
this.configure();
this.mount(routes || {});
};
//
// Inherit from `director.Router`.
//
util.inherits(Router, director.Router);
//
// ### function configure (options)
// #### @options {Object} **Optional** Options to configure this instance with
// Configures this instance with the specified `options`.
//
Router.prototype.configure = function (options) {
options = options || {};
// useful when using connect's bodyParser
this.stream = options.stream || false;
return director.Router.prototype.configure.call(this, options);
};
//
// ### function on (method, path, route)
// #### @method {string} **Optional** Method to use
// #### @path {string} Path to set this route on.
// #### @route {Array|function} Handler for the specified method and path.
// Adds a new `route` to this instance for the specified `method`
// and `path`.
//
Router.prototype.on = function (method, path) {
var args = Array.prototype.slice.call(arguments, 2),
route = args.pop(),
options = args.pop(),
accept;
if (options) {
if (options.stream) {
route.stream = true;
}
if (options.accept) {
this._hasAccepts = true;
accept = options.accept;
route.accept = (Array.isArray(accept) ? accept : [accept]).map(function (a) {
return typeof a === 'string' ? RegExp(a) : a;
});
}
}
if (typeof path !== 'string' && !path.source) {
path = '';
}
director.Router.prototype.on.call(this, method, path, route);
};
//
// ### function attach (func)
// ### @func {function} Function to execute on `this` before applying to router function
// Ask the router to attach objects or manipulate `this` object on which the
// function passed to the http router will get applied
Router.prototype.attach = function (func) {
this._attach.push(func);
};
//
// ### function dispatch (method, path)
// #### @req {http.ServerRequest} Incoming request to dispatch.
// #### @res {http.ServerResponse} Outgoing response to dispatch.
// #### @callback {function} **Optional** Continuation to respond to for async scenarios.
// Finds a set of functions on the traversal towards
// `method` and `path` in the core routing table then
// invokes them based on settings in this instance.
//
Router.prototype.dispatch = function (req, res, callback) {
//
// Dispatch `HEAD` requests to `GET`
//
var method = req.method === 'HEAD' ? 'get' : req.method.toLowerCase(),
thisArg = { req: req, res: res },
self = this,
contentType,
runlist,
stream,
error,
fns,
url;
//
// Trap bad URLs from `decodeUri`
//
try { url = decodeURI(req.url.split('?', 1)[0]) }
catch (ex) { url = null }
if (url && this._hasAccepts) {
contentType = req.headers['content-type'];
fns = this.traverse(method, url, this.routes, '', function (route) {
return !route.accept || route.accept.some(function (a) {
return a.test(contentType);
});
});
}
else if (url) {
fns = this.traverse(method, url, this.routes, '');
}
if (this._attach) {
for (var i in this._attach) {
this._attach[i].call(thisArg);
}
}
if (!fns || fns.length === 0) {
error = new exports.NotFound('Could not find path: ' + req.url);
if (typeof this.notfound === 'function') {
this.notfound.call(thisArg, callback);
}
else if (callback) {
callback.call(thisArg, error, req, res);
}
return false;
}
if (this.recurse === 'forward') {
fns = fns.reverse();
}
runlist = this.runlist(fns);
stream = this.stream || runlist.some(function (fn) { return fn.stream === true; });
function parseAndInvoke() {
error = self.parse(req);
if (error) {
if (callback) {
callback.call(thisArg, error, req, res);
}
return false;
}
self.invoke(runlist, thisArg, callback);
}
if (!stream) {
//
// If there is no streaming required on any of the functions on the
// way to `path`, then attempt to parse the fully buffered request stream
// once it has emitted the `end` event.
//
if (req.readable) {
//
// If the `http.ServerRequest` is still readable, then await
// the end event and then continue
//
req.once('end', parseAndInvoke);
// Streams2 requires us to start the stream if we're not explicitly
// reading from it.
req.resume();
}
else {
//
// Otherwise, just parse the body now.
//
parseAndInvoke();
}
}
else {
this.invoke(runlist, thisArg, callback);
}
return true;
};
//
// ### @parsers {Object}
// Lookup table of parsers to use when attempting to
// parse incoming responses.
//
Router.prototype.parsers = {
'application/x-www-form-urlencoded': qs.parse,
'application/json': JSON.parse
};
//
// ### function parse (req)
// #### @req {http.ServerResponse|BufferedStream} Incoming HTTP request to parse
// Attempts to parse `req.body` using the value found at `req.headers['content-type']`.
//
Router.prototype.parse = function (req) {
function mime(req) {
var str = req.headers['content-type'] || '';
return str.split(';')[0];
}
var parser = this.parsers[mime(req)],
body;
if (parser) {
req.body = req.body || '';
if (req.chunks) {
req.chunks.forEach(function (chunk) {
req.body += chunk;
});
}
try {
req.body = req.body && req.body.length
? parser(req.body)
: {};
}
catch (err) {
return new exports.BadRequest('Malformed data');
}
}
};
/*!
* Express - router - methods
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*
* Adapted for SS
* (C) 2011 Nodejitsu Inc. <info@nodejitsu.com>
*
*/
/**
* Hypertext Transfer Protocol -- HTTP/1.1
* http://www.ietf.org/rfc/rfc2616.txt
*/
var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'];
/**
* HTTP Extensions for Distributed Authoring -- WEBDAV
* http://www.ietf.org/rfc/rfc2518.txt
*/
var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'];
/**
* Versioning Extensions to WebDAV
* http://www.ietf.org/rfc/rfc3253.txt
*/
var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY'];
/**
* Ordered Collections Protocol (WebDAV)
* http://www.ietf.org/rfc/rfc3648.txt
*/
var RFC3648 = ['ORDERPATCH'];
/**
* Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
* http://www.ietf.org/rfc/rfc3744.txt
*/
var RFC3744 = ['ACL'];
/**
* Web Distributed Authoring and Versioning (WebDAV) SEARCH
* http://www.ietf.org/rfc/rfc5323.txt
*/
var RFC5323 = ['SEARCH'];
/**
* PATCH Method for HTTP
* http://www.ietf.org/rfc/rfc5789.txt
*/
var RFC5789 = ['PATCH'];
/**
* Expose the methods.
*/
module.exports = [].concat(
RFC2616,
RFC2518,
RFC3253,
RFC3648,
RFC3744,
RFC5323,
RFC5789
).map(function (method) {
return method.toLowerCase();
});
\ No newline at end of file
//
// HTTP Error objectst
//
var util = require('util');
exports.NotModified = function () {
this.status = 304;
this.options = {
removeContentHeaders: true
};
};
util.inherits(exports.NotModified, Error);
exports.BadRequest = function (msg) {
msg = msg || 'Bad request';
this.status = 400;
this.headers = {};
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.BadRequest, Error);
exports.NotAuthorized = function (msg) {
msg = msg || 'Not Authorized';
this.status = 401;
this.headers = {};
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.NotAuthorized, Error);
exports.Forbidden = function (msg) {
msg = msg || 'Not Authorized';
this.status = 403;
this.headers = {};
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.Forbidden, Error);
exports.NotFound = function (msg) {
msg = msg || 'Not Found';
this.status = 404;
this.headers = {};
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.NotFound, Error);
exports.MethodNotAllowed = function (allowed) {
var msg = 'method not allowed.';
this.status = 405;
this.headers = { allow: allowed };
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.MethodNotAllowed, Error);
exports.NotAcceptable = function (accept) {
var msg = 'cannot generate "' + accept + '" response';
this.status = 406;
this.headers = {};
this.message = msg;
this.body = {
error: msg,
only: 'application/json'
};
};
util.inherits(exports.NotAcceptable, Error);
exports.NotImplemented = function (msg) {
msg = msg || 'Not Implemented';
this.status = 501;
this.headers = {};
this.message = msg;
this.body = { error: msg };
};
util.inherits(exports.NotImplemented, Error);
{
"name": "director",
"description": "A client Side/Server Side Router",
"author": "Nodejitsu Inc. <info@nodejitsu.com>",
"version": "1.2.0",
"maintainers": [
"hij1nx <paolo@nodejitsu.com>",
"indexzero <charlie@nodejitsu.com>"
],
"repository": {
"type": "git",
"url": "http://github.com/flatiron/director.git"
},
"keywords": [
"URL",
"router",
"http",
"cli",
"flatiron",
"client side",
"ender"
],
"devDependencies": {
"codesurgeon": "https://github.com/hij1nx/codesurgeon/tarball/master",
"colors": "0.5.x",
"api-easy": "0.3.x",
"uglify-js": "1.0.6",
"request": "2.9.x",
"qunitjs": "1.9.x",
"vows": "0.6.x"
},
"ender": "./build/ender.js",
"browserify": "./build/director",
"main": "./lib/director",
"engines": {
"node": ">= 0.4.0"
},
"scripts": {
"test": "vows test/server/*/*-test.js --spec"
}
}
var http = require('http'),
fs = require('fs'),
path = require('path'),
director = require('../../../lib/director'),
index;
fs.readFile(path.join(__dirname, '..', 'html5-routes-harness.html'), function (err, data) {
if (err) {
throw err;
}
index = data;
});
var CONTENT_TYPES = {
'.js' : 'text/javascript',
'.css' : 'text/css'
};
// Dummy file server
function fileServer(folder, file) {
var root = path.resolve(__dirname, '..');
if (folder === 'build' || folder === 'node_modules') {
root = path.resolve(root, '..', '..');
}
var filepath = path.resolve(root, folder, file);
var res = this.res;
(fs.exists || path.exists)(filepath, function (exists) {
if (exists) {
fs.readFile(filepath, function (err, data) {
if (err) {
res.writeHead(404);
res.end();
}
res.writeHead(200, {'Content-Type': CONTENT_TYPES[path.extname(filepath)]});
res.end(data);
});
} else {
res.writeHead(404);
res.end();
}
});
}
var router = new director.http.Router({
'/files': {
'/:folder': {
'/(.+)': {
get: fileServer
},
get: fileServer
}
}
});
var server = http.createServer(function (req, res) {
router.dispatch(req, res, function (err) {
if (err && req.url != '/favicon.ico') {
// By default just reply with the index page
this.res.writeHead(200, {'Content-Type': 'text/html'});
this.res.end(index);
}
});
});
server.listen(8080);
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Director Browserify Tests</title>
<link rel="stylesheet" href="../../node_modules/qunitjs/qunit/qunit.css">
</head>
<body>
<p>To run these tests first generate a Browserify bundle for director by running the command <kbd>browserify -r ../director -o test/browser/browserified-bundle.js</kbd> in the repo's root directory.</p>
<div id="qunit">
<div id="qunit-fixture"></div>
<script src="browserified-bundle.js"></script>
<script>
var HTML5TEST = false;
var RouterAlias = require('/director').Router;
</script>
<script src="../../node_modules/qunitjs/qunit/qunit.js"></script>
<script src="helpers/api.js"></script>
<script src="routes-test.js"></script>
</body>
</html>
module("Director.js", {
setup: function() {
window.location.hash = "";
shared = {};
// Init needed keys earlier because of in HTML5 mode the route handler
// is executed upon Router.init() and due to that setting shared.fired
// in the param test of createTest is too late
if (HTML5TEST) {
shared.fired = [];
shared.fired_count = 0;
}
},
teardown: function() {
window.location.hash = "";
shared = {};
}
});
var shared;
function createTest(name, config, use, test, initialRoute) {
// We rename to `RouterAlias` for the browserify tests, since we want to be
// sure that no code is depending on `window.Router` being available.
var Router = window.Router || window.RouterAlias;
if (typeof use === 'function') {
test = use;
use = undefined;
}
if (HTML5TEST) {
if (use === undefined) {
use = {};
}
if (use.run_handler_in_init === undefined) {
use.run_handler_in_init = false;
}
use.html5history = true;
}
// Because of the use of setTimeout when defining onpopstate
var innerTimeout = HTML5TEST === true ? 500 : 0;
asyncTest(name, function() {
setTimeout(function() {
var router = new Router(config),
context;
if (use !== undefined) {
router.configure(use);
}
router.init(initialRoute);
setTimeout(function() {
test.call(context = {
router: router,
navigate: function(url, callback) {
if (HTML5TEST) {
router.setRoute(url);
} else {
window.location.hash = url;
}
setTimeout(function() {
callback.call(context);
}, 14);
},
finish: function() {
router.destroy();
start();
}
})
}, innerTimeout);
}, 14);
});
};
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Director HTML5 Tests</title>
<link rel="stylesheet" href="/files/node_modules/qunitjs/qunit/qunit.css">
</head>
<body>
<p>Note: in order to execute HTML5 mode test this file needs to be served with provided nodejs backend. Start the server from <kbd>director/test/browser/backend</kbd> and go to <kbd>http://localhost:8080/</kbd> to launch the tests.</p>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script>
var HTML5TEST = true;
</script>
<script src="/files/node_modules/qunitjs/qunit/qunit.js"></script>
<script src="/files/build/director.js"></script>
<script src="/files/helpers/api.js"></script>
<script src="/files/html5-routes-test.js"></script>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Director Tests</title>
<link rel="stylesheet" href="../../node_modules/qunitjs/qunit/qunit.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script>
var HTML5TEST = false;
</script>
<script src="../../node_modules/qunitjs/qunit/qunit.js"></script>
<script src="../../build/director.js"></script>
<script src="helpers/api.js"></script>
<script src="routes-test.js"></script>
</body>
</html>
/*
* dispatch-test.js: Tests for the core dispatch method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/cli/dispatch').addBatch({
"An instance of director.cli.Router": {
topic: function () {
var router = new director.cli.Router(),
that = this;
that.matched = {};
that.matched['users'] = [];
that.matched['apps'] = []
router.on('users create', function () {
that.matched['users'].push('on users create');
});
router.on(/apps (\w+\s\w+)/, function () {
assert.equal(arguments.length, 1);
that.matched['apps'].push('on apps (\\w+\\s\\w+)');
});
return router;
},
"should have the correct routing table": function (router) {
assert.isObject(router.routes.users);
assert.isObject(router.routes.users.create);
},
"the dispatch() method": {
"users create": function (router) {
assert.isTrue(router.dispatch('on', 'users create'));
assert.equal(this.matched.users[0], 'on users create');
},
"apps foo bar": function (router) {
assert.isTrue(router.dispatch('on', 'apps foo bar'));
assert.equal(this.matched['apps'][0], 'on apps (\\w+\\s\\w+)');
},
"not here": function (router) {
assert.isFalse(router.dispatch('on', 'not here'));
},
"still not here": function (router) {
assert.isFalse(router.dispatch('on', 'still not here'));
}
}
}
}).export(module);
/*
* mount-test.js: Tests for the core mount method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/cli/path').addBatch({
"An instance of director.cli.Router with routes": {
topic: new director.cli.Router({
'apps': function () {
console.log('apps');
},
' users': function () {
console.log('users');
}
}),
"should create the correct nested routing table": function (router) {
assert.isObject(router.routes.apps);
assert.isFunction(router.routes.apps.on);
assert.isObject(router.routes.users);
assert.isFunction(router.routes.users.on);
}
}
}).export(module);
/*
* dispatch-test.js: Tests for the core dispatch method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/cli/path').addBatch({
"An instance of director.cli.Router": {
topic: new director.cli.Router(),
"the path() method": {
"should create the correct nested routing table": function (router) {
function noop () {}
router.path(/apps/, function () {
router.path(/foo/, function () {
router.on(/bar/, noop);
});
router.on(/list/, noop);
});
router.on(/users/, noop);
assert.isObject(router.routes.apps);
assert.isFunction(router.routes.apps.list.on);
assert.isObject(router.routes.apps.foo);
assert.isFunction(router.routes.apps.foo.bar.on);
assert.isFunction(router.routes.users.on);
}
}
}
}).export(module);
/*
* dispatch-test.js: Tests for the core dispatch method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/core/dispatch').addBatch({
"An instance of director.Router": {
topic: function () {
var that = this;
that.matched = {};
that.matched['/'] = [];
that.matched['foo'] = [];
that.matched['f*'] = []
var router = new director.Router({
'/': {
before: function () { that.matched['/'].push('before /') },
on: function () { that.matched['/'].push('on /') },
after: function () { that.matched['/'].push('after /') }
},
'/foo': {
before: function () { that.matched.foo.push('before foo') },
on: function () { that.matched.foo.push('on foo') },
after: function () { that.matched.foo.push('after foo') },
'/bar': {
before: function () { that.matched.foo.push('before foo bar') },
on: function () { that.matched.foo.push('foo bar') },
after: function () { that.matched.foo.push('after foo bar') },
'/buzz': function () { that.matched.foo.push('foo bar buzz') }
}
},
'/f*': {
'/barbie': function () { that.matched['f*'].push('f* barbie') }
}
});
router.configure({
recurse: 'backward'
});
return router;
},
"should have the correct routing table": function (router) {
assert.isObject(router.routes.foo);
assert.isObject(router.routes.foo.bar);
assert.isObject(router.routes.foo.bar.buzz);
assert.isFunction(router.routes.foo.bar.buzz.on);
},
"the dispatch() method": {
"/": function (router) {
assert.isTrue(router.dispatch('on', '/'));
assert.isTrue(router.dispatch('on', '/'));
assert.equal(this.matched['/'][0], 'before /');
assert.equal(this.matched['/'][1], 'on /');
assert.equal(this.matched['/'][2], 'after /');
},
"/foo/bar/buzz": function (router) {
assert.isTrue(router.dispatch('on', '/foo/bar/buzz'));
assert.equal(this.matched.foo[0], 'foo bar buzz');
assert.equal(this.matched.foo[1], 'before foo bar');
assert.equal(this.matched.foo[2], 'foo bar');
assert.equal(this.matched.foo[3], 'before foo');
assert.equal(this.matched.foo[4], 'on foo');
},
"/foo/barbie": function (router) {
assert.isTrue(router.dispatch('on', '/foo/barbie'));
assert.equal(this.matched['f*'][0], 'f* barbie');
},
"/foo/barbie/": function (router) {
assert.isFalse(router.dispatch('on', '/foo/barbie/'));
},
"/foo/BAD": function (router) {
assert.isFalse(router.dispatch('on', '/foo/BAD'));
},
"/bar/bar": function (router) {
assert.isFalse(router.dispatch('on', '/bar/bar'));
},
"with the strict option disabled": {
topic: function (router) {
return router.configure({
recurse: 'backward',
strict: false
});
},
"should have the proper configuration set": function (router) {
assert.isFalse(router.strict);
},
"/foo/barbie/": function (router) {
assert.isTrue(router.dispatch('on', '/foo/barbie/'));
assert.equal(this.matched['f*'][0], 'f* barbie');
},
"/foo/bar/buzz": function (router) {
assert.isTrue(router.dispatch('on', '/foo/bar/buzz'));
assert.equal(this.matched.foo[0], 'foo bar buzz');
assert.equal(this.matched.foo[1], 'before foo bar');
assert.equal(this.matched.foo[2], 'foo bar');
assert.equal(this.matched.foo[3], 'before foo');
assert.equal(this.matched.foo[4], 'on foo');
},
}
}
}
}).export(module);
/*
* insert-test.js: Tests for inserting routes into a normalized routing table.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/core/insert').addBatch({
"An instance of director.Router": {
topic: new director.Router(),
"the insert() method": {
"'on', ['foo', 'bar']": function (router) {
function route () { }
router.insert('on', ['foo', 'bar'], route);
assert.strictEqual(router.routes.foo.bar.on, route);
},
"'on', ['foo', 'bar'] again": function (router) {
function route () { }
router.insert('on', ['foo', 'bar'], route);
assert.isArray(router.routes.foo.bar.on);
assert.strictEqual(router.routes.foo.bar.on.length, 2);
},
"'on', ['foo', 'bar'] a third time": function (router) {
function route () { }
router.insert('on', ['foo', 'bar'], route);
assert.isArray(router.routes.foo.bar.on);
assert.strictEqual(router.routes.foo.bar.on.length, 3);
},
"'get', ['fizz', 'buzz']": function (router) {
function route () { }
router.insert('get', ['fizz', 'buzz'], route);
assert.strictEqual(router.routes.fizz.buzz.get, route);
}
}
}
}).export(module);
\ No newline at end of file
/*
* mount-test.js: Tests for mounting and normalizing routes into a Router instance.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
function assertRoute (fn, path, route) {
if (path.length === 1) {
return assert.strictEqual(route[path.shift()], fn);
}
route = route[path.shift()];
assert.isObject(route);
assertRoute(fn, path, route);
}
vows.describe('director/core/mount').addBatch({
"An instance of director.Router": {
"with no preconfigured params": {
topic: new director.Router(),
"the mount() method": {
"should sanitize the routes correctly": function (router) {
function foobar () { }
function foostar () { }
function foobazzbuzz () { }
function foodog () { }
function root () {}
var fnArray = [foobar, foostar];
router.mount({
'/': {
before: root,
on: root,
after: root,
'/nesting': {
on: foobar,
'/deep': foostar
}
},
'/foo': {
'/bar': foobar,
'/*': foostar,
'/jitsu/then': {
before: foobar
}
},
'/foo/bazz': {
'/buzz': foobazzbuzz
},
'/foo/jitsu': {
'/then': fnArray
},
'/foo/jitsu/then/now': foostar,
'/foo/:dog': foodog
});
assertRoute(root, ['on'], router.routes);
assertRoute(root, ['after'], router.routes);
assertRoute(root, ['before'], router.routes);
assertRoute(foobar, ['nesting', 'on'], router.routes);
assertRoute(foostar, ['nesting', 'deep', 'on'], router.routes);
assertRoute(foobar, [ 'foo', 'bar', 'on'], router.routes);
assertRoute(foostar, ['foo', '([_.()!\\ %@&a-zA-Z0-9-]+)', 'on'], router.routes);
assertRoute(fnArray, ['foo', 'jitsu', 'then', 'on'], router.routes);
assertRoute(foobar, ['foo', 'jitsu', 'then', 'before'], router.routes);
assertRoute(foobazzbuzz, ['foo', 'bazz', 'buzz', 'on'], router.routes);
assertRoute(foostar, ['foo', 'jitsu', 'then', 'now', 'on'], router.routes);
assertRoute(foodog, ['foo', '([._a-zA-Z0-9-]+)', 'on'], router.routes);
},
"should accept string path": function(router) {
function dogs () { }
router.mount({
'/dogs': {
on: dogs
}
},
'/api');
assertRoute(dogs, ['api', 'dogs', 'on'], router.routes);
}
}
},
"with preconfigured params": {
topic: function () {
var router = new director.Router();
router.param('city', '([\\w\\-]+)');
router.param(':country', /([A-Z][A-Za-z]+)/);
router.param(':zip', /([\d]{5})/);
return router;
},
"should sanitize the routes correctly": function (router) {
function usaCityZip () { }
function countryCityZip () { }
router.mount({
'/usa/:city/:zip': usaCityZip,
'/world': {
'/:country': {
'/:city/:zip': countryCityZip
}
}
});
assertRoute(usaCityZip, ['usa', '([\\w\\-]+)', '([\\d]{5})', 'on'], router.routes);
assertRoute(countryCityZip, ['world', '([A-Z][A-Za-z]+)', '([\\w\\-]+)', '([\\d]{5})', 'on'], router.routes);
}
}
}
}).export(module);
/*
* on-test.js: Tests for the on/route method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/core/insert').addBatch({
"An instance of director.Router": {
topic: new director.Router(),
"the on() method": {
"['foo', 'bar']": function (router) {
function noop () { }
router.on(['foo', 'bar'], noop);
assert.strictEqual(router.routes.foo.on, noop);
assert.strictEqual(router.routes.bar.on, noop);
},
"'baz'": function (router) {
function noop () { }
router.on('baz', noop);
assert.strictEqual(router.routes.baz.on, noop);
},
"'after', 'baz'": function (router) {
function noop () { }
router.on('after', 'boo', noop);
assert.strictEqual(router.routes.boo.after, noop);
}
}
}
}).export(module);
\ No newline at end of file
/*
* path-test.js: Tests for the core `.path()` method.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/core/path').addBatch({
"An instance of director.Router": {
topic: function () {
var that = this;
that.matched = {};
that.matched['foo'] = [];
that.matched['newyork'] = [];
var router = new director.Router({
'/foo': function () { that.matched['foo'].push('foo'); }
});
return router;
},
"the path() method": {
"should create the correct nested routing table": function (router) {
var that = this;
router.path('/regions', function () {
this.on('/:state', function(country) {
that.matched['newyork'].push('new york');
});
});
assert.isFunction(router.routes.foo.on);
assert.isObject(router.routes.regions);
assert.isFunction(router.routes.regions['([._a-zA-Z0-9-]+)'].on);
},
"should dispatch the function correctly": function (router) {
router.dispatch('on', '/regions/newyork')
router.dispatch('on', '/foo');
assert.equal(this.matched['foo'].length, 1);
assert.equal(this.matched['newyork'].length, 1);
assert.equal(this.matched['foo'][0], 'foo');
assert.equal(this.matched['newyork'][0], 'new york');
}
}
}
}).export(module);
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
var callback = function() {
return true;
};
var testRoute = function(route, callback) {
var router = new director.Router();
router.on(route, callback);
return function(value) {
return router.dispatch('on', value);
};
};
vows.describe('director/core/regifyString').addBatch({
'When using "/home(.*)"': {
topic: function() {
return testRoute('/home(.*)', callback);
},
'Should match "/homepage"': function(result) {
assert.isTrue(result('/homepage'));
},
'Should match "/home/page"': function(result) {
assert.isTrue(result('/home/page'));
},
'Should not match "/foo-bar"': function(result) {
assert.isFalse(result('/foo-bar'));
}
},
'When using "/home.*"': {
topic: function() {
return testRoute('/home.*', callback);
},
'Should match "/homepage"': function(result) {
assert.isTrue(result('/homepage'));
},
'Should match "/home/page"': function(result) {
assert.isTrue(result('/home/page'));
},
'Should not match "/foo-bar"': function(result) {
assert.isFalse(result('/foo-bar'));
}
},
'When using "/home(page[0-9])*"': {
topic: function() {
return testRoute('/home(page[0-9])*', callback);
},
'Should match "/home"': function(result) {
assert.isTrue(result('/home'));
},
'Should match "/homepage0", "/homepage1", etc.': function(result) {
for (i = 0; i < 10; i++) {
assert.isTrue(result('/homepage' + i));
}
},
'Should not match "/home_page"': function(result) {
assert.isFalse(result('/home_page'));
},
'Should not match "/home/page"': function(result) {
assert.isFalse(result('/home/page'));
}
},
'When using "/home*"': {
topic: function() {
return testRoute('/home*', callback);
},
'Should match "/homepage"': function(result) {
assert.isTrue(result('/homepage'));
},
'Should match "/home_page"': function(result) {
assert.isTrue(result('/home_page'));
},
'Should match "/home-page"': function(result) {
assert.isTrue(result('/home-page'));
},
'Should not match "/home/page"': function(result) {
assert.isFalse(result('/home/page'));
}
}
}).export(module);
/*
* index.js: Test helpers for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var http = require('http');
exports.createServer = function (router) {
return http.createServer(function (req, res) {
router.dispatch(req, res, function (err) {
if (err) {
res.writeHead(404);
res.end();
}
});
});
};
exports.handlers = {
respondWithId: function (id) {
this.res.writeHead(200, { 'Content-Type': 'text/plain' })
this.res.end('hello from (' + id + ')');
},
respondWithData: function () {
this.res.writeHead(200, { 'Content-Type': 'application/json' })
this.res.end(JSON.stringify(this.data));
},
respondWithOk: function () {
return function () {
this.res.writeHead(200);
this.res.end('ok');
};
},
streamBody: function () {
var body = '',
res = this.res;
this.req.on('data', function (chunk) {
body += chunk;
});
this.req.on('end', function () {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(body);
});
}
};
exports.macros = require('./macros');
/*
* macros.js: Test macros for director tests.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
request = require('request');
exports.assertGet = function(port, uri, expected) {
var context = {
topic: function () {
request({ uri: 'http://localhost:' + port + '/' + uri }, this.callback);
}
};
context['should respond with `' + expected + '`'] = function (err, res, body) {
assert.isNull(err);
assert.equal(res.statusCode, 200);
assert.equal(body, expected);
};
return context;
};
exports.assert404 = function (port, uri) {
return {
topic: function () {
request({ uri: 'http://localhost:' + port + '/' + uri }, this.callback);
},
"should respond with 404": function (err, res, body) {
assert.isNull(err);
assert.equal(res.statusCode, 404);
}
};
};
exports.assertPost = function(port, uri, expected) {
return {
topic: function () {
request({
method: 'POST',
uri: 'http://localhost:' + port + '/' + uri,
body: JSON.stringify(expected)
}, this.callback);
},
"should respond with the POST body": function (err, res, body) {
assert.isNull(err);
assert.equal(res.statusCode, 200);
assert.deepEqual(JSON.parse(body), expected);
}
};
};
/*
* attach-test.js: Tests 'router.attach' functionality.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
http = require('http'),
vows = require('vows'),
request = require('request'),
director = require('../../../lib/director'),
helpers = require('../helpers'),
handlers = helpers.handlers,
macros = helpers.macros;
function assertData(uri) {
return macros.assertGet(
9091,
uri,
JSON.stringify([1,2,3])
);
}
vows.describe('director/http/attach').addBatch({
"An instance of director.http.Router": {
"instantiated with a Routing table": {
topic: new director.http.Router({
'/hello': {
get: handlers.respondWithData
}
}),
"should have the correct routes defined": function (router) {
assert.isObject(router.routes.hello);
assert.isFunction(router.routes.hello.get);
},
"when passed to an http.Server instance": {
topic: function (router) {
router.attach(function () {
this.data = [1,2,3];
});
helpers.createServer(router)
.listen(9091, this.callback);
},
"a request to hello": assertData('hello'),
}
}
}
}).export(module);
/*
* responses-test.js: Tests for HTTP responses.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
var assert = require('assert'),
vows = require('vows'),
director = require('../../../lib/director');
vows.describe('director/http/responses').addBatch({
"When using director.http": {
"it should have the relevant responses defined": function () {
Object.keys(require('../../../lib/director/http/responses')).forEach(function (name) {
assert.isFunction(director.http[name]);
});
}
}
}).export(module);
\ No newline at end of file
This diff is collapsed.
Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers.
For Docs, License, Tests, and pre-packed downloads, see:
http://www.polymer-project.org/
Many thanks to our contributors:
https://github.com/Polymer/platform/contributors
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment