react-router
Advanced tools
Comparing version 0.0.0 to 0.0.1
238
index.js
var Backbone = require('backbone') | ||
, _ = require('lodash') | ||
, $ = window.jQuery | ||
var _ = require('lodash') | ||
, d = React.DOM | ||
, Route = require('./route') | ||
DEFAULTS = { | ||
_index: React.createClass({ | ||
render: function () { | ||
return d.span({className: 'react-router_index'}) | ||
} | ||
}), | ||
_error: React.createClass({ | ||
displayName: 'DefaultError', | ||
render: function () { | ||
return d.div({className: 'react-router_error'}, | ||
d.strong({}, 'Error Loading Model:'), | ||
this.props.error) | ||
}, | ||
}), | ||
_loading: React.createClass({ | ||
render: function () { | ||
return d.span({className: 'react-router_loading'}) | ||
} | ||
}) | ||
} | ||
Backbone.$ = $ | ||
module.exports = { | ||
module.exports = { | ||
getDefaultProps: function () { | ||
return { | ||
_baseroute: '', | ||
_on: function (fn) { | ||
window.addEventListener('hashchange', fn) | ||
}, | ||
_off: function (fn) { | ||
window.removeEventListener('hashchange', fn) | ||
}, | ||
_path: function (path) { | ||
if (arguments.length === 1) { | ||
window.location.hash = '#' + path | ||
} else { | ||
return window.location.hash.slice(1) | ||
} | ||
}, | ||
goTo: function () {throw 'override'} | ||
} | ||
}, | ||
getInitialState: function () { | ||
this.createRoutes() | ||
return { | ||
_route: this.defaultRoute(), | ||
_navigations: 0 | ||
_route: '' | ||
} | ||
}, | ||
createRoutes: function () { | ||
var routes = this.routes | ||
, _routes = this._routes = {} | ||
if ('function' === typeof routes) { | ||
routes = routes() | ||
componentWillMount: function () { | ||
if (this.props.goTo) { | ||
// I'm not the top router | ||
var cancel = this.enter && this.enter() | ||
if (cancel) return | ||
} | ||
Object.keys(routes).forEach(function (route) { | ||
_routes[route] = new Route(route, routes[route]) | ||
}) | ||
this.props._on(this.pathchange) | ||
this.setRouteFromPath() | ||
}, | ||
defaultRoute: function () { | ||
var found = null | ||
for (var route in this._routes) { | ||
var obj = route.match('') | ||
if (!obj) continue; | ||
found = { | ||
name: route.name, | ||
params: obj, | ||
raw: '' | ||
} | ||
break; | ||
componentWillUnmount: function () { | ||
this.props._off(this.pathchange) | ||
}, | ||
showTitle: function () { | ||
var title = this.title | ||
if ('function' === typeof title) title = title() | ||
this.setTitle(title) | ||
}, | ||
componentDidMount: function () { | ||
// if there are no child routes | ||
if (!this.refs._outlet && this.title) { | ||
this.showTitle() | ||
} | ||
return found | ||
}, | ||
/** | ||
* Returns the route object, which looks like | ||
* { | ||
* name: the name of the route, | ||
* raw: the raw /matched/url/stuff | ||
* params: {} obj of the parsed params | ||
* } | ||
*/ | ||
getRoute: function () { | ||
return this.state._route | ||
componentDidUpdate: function () { | ||
// if there are no child routes | ||
if (!this.refs._outlet && this.title) { | ||
this.showTitle() | ||
} | ||
}, | ||
setRoute: function (name, params) { | ||
params = params || {} | ||
if (!this._routes[name]) { | ||
console.warn('Route not defined', name) | ||
setTitle: function (text) { | ||
document.title = text | ||
}, | ||
/** Do we need this for something? | ||
componentDidUpdate: function (oprops, ostate) { | ||
if (ostate._route === this.state._route) return | ||
}, | ||
**/ | ||
pathchange: function () { | ||
this.setRouteFromPath() | ||
}, | ||
setRouteFromPath: function () { | ||
// check the current url, if there's information for me there then load | ||
// that into the _route state | ||
var path = this.props._path() | ||
if (path.indexOf(this.props._baseroute) === -1) { | ||
// left this path. we will soon be destructed. | ||
return | ||
} | ||
var fragment = this._routes[name].toFragment(params) | ||
if (fragment === false) { | ||
console.warn('Invalid params given for route', name, params) | ||
throw new Error('Invalid params for route ' + name) | ||
var hash = path.slice(this.props._baseroute.length) | ||
if (hash.length && hash[0] === '/') hash = hash.slice(1) | ||
var part = hash.split('/', 1)[0] | ||
this.setState({_route: part}) | ||
}, | ||
goTo: function (route, global, force) { | ||
var full, part | ||
if (global) { | ||
if (route.indexOf(this.props._baseroute) !== 0) { | ||
return this.props.goTo(route, global, force) | ||
} | ||
full = route | ||
part = route.slice(this.props._baseroute.length).split('/', 1)[0] | ||
} else { | ||
if (route.slice(0, 3) === '../' || route === '..') { | ||
return this.props.goTo(route.slice(3), global, force) | ||
} | ||
full = this.props._baseroute | ||
if (full.length && full[full.length-1] !== '/' && route.length && route[0] !== '/') { | ||
full += '/' | ||
} | ||
full += route | ||
part = route.split('/', 1)[0] | ||
} | ||
Backbone.history.navigate(fragment, {trigger: false}) | ||
this.onRoute(name, params, fragment) | ||
var switching = part !== this.state._route | ||
if (switching && !force) { | ||
/** Not sure how this should work exactly. It should probably be async.... | ||
if (this.refs._outlet.leave && !this.refs._outlet.leave()) { | ||
console.log('Routing cancelled by current outlet', this.state._route, route) | ||
return | ||
} | ||
**/ | ||
} | ||
this.props._path(full) | ||
}, | ||
onRoute: function (name, params, fragment) { | ||
this.setState({ | ||
_route: { | ||
name: name, | ||
raw: fragment, | ||
params: params | ||
}, | ||
_navigations: this.state._navigations + 1 | ||
}) | ||
}, | ||
setupRoutes: function () { | ||
var that = this | ||
for (var name in this._routes) { | ||
this._routes[name].register(that.onRoute.bind(that, name)) | ||
outlet: function () { | ||
var route = this.routes[this.state._route] | ||
, rname = this.state._route | ||
if (!route && this.routes['*'] && rname) route = this.routes['*'] | ||
if (!route && (rname || !this.model)) { | ||
console.log('Invalid router state envountered; no good route here...' + this.state._route) | ||
this.goTo('') | ||
return false | ||
} | ||
if ('string' === typeof route) { | ||
this.goTo(route) | ||
return false | ||
} | ||
var args = {} | ||
// model routes | ||
if (!rname && this.model) { | ||
if (this.state.modelLoading) { | ||
rname = '_loading' | ||
} else if (this.state.modelError) { | ||
rname = '_error' | ||
args.error = this.state.modelError | ||
} else { | ||
rname = '_index' | ||
args.model = this.state.model | ||
} | ||
route = this.routes[rname] || DEFAULTS[rname] | ||
} | ||
if (Array.isArray(route)) { | ||
args = _.extend(args, route[1].call(this)) | ||
cls = route[0] | ||
} else { | ||
cls = route | ||
} | ||
var context = this.props.ctx ? _.extend({}, this.props.ctx) : {} | ||
if (this.getContext) { | ||
_.extend(context, this.getContext()) | ||
} | ||
args.ctx = context | ||
args.goTo = this.goTo | ||
args._baseroute = (this.props._baseroute ? this.props._baseroute + '/' : '') + rname | ||
args.ref = '_outlet' | ||
args.param = rname | ||
return cls(args) | ||
}, | ||
componentDidMount: function () { | ||
this.setupRoutes() | ||
Backbone.history.start() | ||
} | ||
} | ||
{ | ||
"name": "react-router", | ||
"version": "0.0.0", | ||
"version": "0.0.1", | ||
"description": "An integrated router mixin for react components", | ||
@@ -11,5 +11,10 @@ "main": "index.js", | ||
"backbone": "~1.1.0", | ||
"expect.js": "~0.2.0" | ||
"expect.js": "~0.2.0", | ||
"lodash": "~2.4.1" | ||
}, | ||
"devDependencies": {}, | ||
"devDependencies": { | ||
"mocha": "~1.17.1", | ||
"envify": "~0.2.0", | ||
"react": "~0.8.0" | ||
}, | ||
"scripts": { | ||
@@ -20,3 +25,3 @@ "test": "make test" | ||
"type": "git", | ||
"url": "git://github.com/jaredly/reactor.git" | ||
"url": "git://github.com/jaredly/react-router.git" | ||
}, | ||
@@ -30,5 +35,5 @@ "keywords": [ | ||
"bugs": { | ||
"url": "https://github.com/jaredly/reactor/issues" | ||
"url": "https://github.com/jaredly/react-router/issues" | ||
}, | ||
"homepage": "https://github.com/jaredly/reactor" | ||
"homepage": "https://github.com/jaredly/react-router" | ||
} |
200
test/test.js
var expect = require('expect.js') | ||
, Route = require('../route') | ||
, mixin = require('../') | ||
var months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun'] | ||
function merge(a, b) { | ||
if (!b) return a | ||
for (var c in b) { | ||
a[c] = b[c] | ||
} | ||
return a | ||
} | ||
var DateParser = { | ||
compare: function (a, b) { | ||
return a.getTime() === b.getTime() | ||
}, | ||
stringify: function (date) { | ||
return date.getTime() | ||
}, | ||
parse: function (num) { | ||
return new Date(+num) | ||
function Awesome(props, state) { | ||
props._on = props._on || function () { } | ||
props._off = props._off || function () { } | ||
props._path = props._path || function () { } | ||
this.props = merge(this.getDefaultProps(), props) | ||
this.state = merge(this.getInitialState(), state) | ||
this.refs = { | ||
_outlet: { } | ||
} | ||
} | ||
var MonthParser = { | ||
months: months, | ||
match: months.join('|'), | ||
stringify: function (num) { | ||
return months[num] | ||
}, | ||
parse: function (month) { | ||
return months.indexOf(month.toLowerCase()) | ||
Awesome.prototype = mixin | ||
Awesome.prototype.setState = function (state) { | ||
for (var name in state) { | ||
this.state[name] = state[name] | ||
} | ||
@@ -31,103 +32,104 @@ } | ||
describe('Router', function () { | ||
describe('most basic', function () { | ||
var r | ||
describe('simply setup', function () { | ||
var awe, path | ||
beforeEach(function () { | ||
r = new Route('test', '') | ||
awe = new Awesome({ | ||
_path: function (val) { | ||
if (arguments.length) path = val | ||
return path | ||
} | ||
}) | ||
}) | ||
it('should match the empty route', function () { | ||
var m = r.match('') | ||
expect(m).to.be.ok() | ||
expect(m).to.eql({}) | ||
describe('#setRouteFromPath', function () { | ||
it('should load up the index', function () { | ||
path = '' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('') | ||
}) | ||
it('should load up a simple path', function () { | ||
path = 'some' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('some') | ||
}) | ||
it('should load up the first part of a complex path', function () { | ||
path = 'great/thing' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('great') | ||
}) | ||
}) | ||
it('should not match other things', function () { | ||
var m = r.match('something') | ||
expect(m).to.not.be.ok() | ||
}) | ||
it('should not match other complex things', function () { | ||
var m = r.match('something/else') | ||
expect(m).to.not.be.ok() | ||
}) | ||
}) | ||
describe('with two routes', function () { | ||
var r | ||
beforeEach(function () { | ||
r = new Route('test', ['some/thing', 'other/:num']) | ||
describe('#goTo', function () { | ||
it('should set the path', function () { | ||
path = '' | ||
awe.goTo('parties') | ||
expect(path).to.equal('parties') | ||
}) | ||
}) | ||
it('should match the first', function () { | ||
var m = r.match('some/thing') | ||
expect(m).to.eql({}) | ||
}) | ||
it('should match the second', function () { | ||
var m = r.match('other/23m') | ||
expect(m).to.eql({num: '23m'}) | ||
}) | ||
it('should not match another', function () { | ||
expect(r.match('other')).to.not.be.ok() | ||
}) | ||
}) | ||
describe('with complex parsing', function () { | ||
var r | ||
describe('somewhat nested', function () { | ||
var awe, path | ||
beforeEach(function () { | ||
r = new Route('test', { | ||
match: 'one/:two/:three', | ||
args: { | ||
two: Number, | ||
three: MonthParser | ||
awe = new Awesome({ | ||
_baseroute: 'some/thing', | ||
_path: function (val) { | ||
if (arguments.length) path = val | ||
return path | ||
} | ||
}) | ||
}) | ||
it('should parse', function () { | ||
var m = r.match('one/34/jan') | ||
expect(m).to.eql({ | ||
two: 34, | ||
three: 0 | ||
describe('#setRouteFromPath', function () { | ||
it('should load up to the index', function () { | ||
path = 'some/thing' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('') | ||
}) | ||
}) | ||
it('should serialize', function () { | ||
var frag = r.toFragment({ | ||
two: 36, | ||
three: 1 | ||
it('should strip the trailing slash', function () { | ||
path = 'some/thing/' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('') | ||
}) | ||
expect(frag).to.equal('one/36/feb') | ||
}) | ||
it('should not parse something that does not match the type', function () { | ||
expect(r.match('one/34/foo')).to.not.be.ok() | ||
}) | ||
}) | ||
it('should load up a simple path', function () { | ||
path = 'some/thing/else' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('else') | ||
}) | ||
describe('with defaults', function () { | ||
var r | ||
beforeEach(function () { | ||
r = new Route('test', { | ||
match: ['', 'one/:two/:three'], | ||
args: { | ||
two: { | ||
default: 5, | ||
type: Number | ||
}, | ||
three: { | ||
default: new Date(10000), | ||
type: DateParser | ||
} | ||
} | ||
it('should load up a complex path', function () { | ||
path = 'some/thing/here/is/great' | ||
awe.setRouteFromPath() | ||
expect(awe.state._route).to.equal('here') | ||
}) | ||
}) | ||
it('should fill in the default', function () { | ||
expect(r.match('')).to.eql({two: 5, three: new Date(10000)}) | ||
}) | ||
it('should override the default', function () { | ||
expect(r.match('one/12/10')).to.eql({two: 12, three: new Date(10)}) | ||
}) | ||
it('should serialize normally', function () { | ||
expect(r.toFragment({two: 7, three: new Date(20)})).to.equal('one/7/20') | ||
describe('#goTo', function () { | ||
it('should set the index', function () { | ||
path = 'some/thing/else' | ||
awe.goTo('') | ||
expect(path).to.equal('some/thing') | ||
}) | ||
it('should set a path', function () { | ||
path = 'some/thing/else' | ||
awe.goTo('here') | ||
expect(path).to.equal('some/thing/here') | ||
}) | ||
it('should set a complex path', function () { | ||
path = 'some/thing/else' | ||
awe.goTo('here/is/great') | ||
expect(path).to.equal('some/thing/here/is/great') | ||
}) | ||
}) | ||
it('should serialize default', function () { | ||
expect(r.toFragment({two: 5, three: new Date(10000)})).to.equal('') | ||
}) | ||
}) | ||
// TODO: test outlet and things | ||
// TODO: test global goTo, pass up higher paths | ||
// TODO: test pathchange, ignore hashes not sharing a base path | ||
// TODO: refuse bequest, refuse leave... should that be async? | ||
}) | ||
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
0
47
10629
3
3
7
292
1
+ Addedlodash@~2.4.1
+ Addedlodash@2.4.2(transitive)