abstract-state-router
Advanced tools
Comparing version 5.3.0 to 5.4.0
49
index.js
@@ -1,14 +0,14 @@ | ||
var StateState = require('./state-state') | ||
var StateState = require('./lib/state-state') | ||
var extend = require('extend') | ||
var Promise = require('promise') | ||
var StateComparison = require('./state-comparison') | ||
var CurrentState = require('./current-state') | ||
var stateChangeLogic = require('./state-change-logic') | ||
var StateComparison = require('./lib/state-comparison') | ||
var CurrentState = require('./lib/current-state') | ||
var stateChangeLogic = require('./lib/state-change-logic') | ||
var newHashBrownRouter = require('hash-brown-router') | ||
var EventEmitter = require('events').EventEmitter | ||
var series = require('promise-map-series') | ||
var parse = require('./state-string-parser') | ||
var parse = require('./lib/state-string-parser') | ||
var combine = require('combine-arrays') | ||
var buildPath = require('page-path-builder') | ||
var StateTransitionManager = require('./state-transition-manager') | ||
var StateTransitionManager = require('./lib/state-transition-manager') | ||
var debug = require('debug')('abstract-state-router') | ||
@@ -100,10 +100,23 @@ | ||
function onRouteChange(state, parameters) { | ||
var fullStateName = prototypalStateHolder.applyDefaultChildStates(state.name) | ||
try { | ||
var finalDestinationStateName = prototypalStateHolder.applyDefaultChildStates(state.name) | ||
if (fullStateName !== state.name) { | ||
stateProviderEmitter.go(fullStateName, parameters, { replace: true }) | ||
} else { | ||
stateProviderEmitter.emit('stateChangeAttempt', function stateGo(transition) { | ||
attemptStateChange(fullStateName, parameters, transition) | ||
}) | ||
if (finalDestinationStateName === state.name) { | ||
emitEventAndAttemptStateChange(finalDestinationStateName, parameters) | ||
} else { | ||
// There are default child states that need to be applied | ||
var theRouteWeNeedToEndUpAt = makePath(finalDestinationStateName, parameters) | ||
var currentRoute = stateRouterOptions.router.location.get() | ||
if (theRouteWeNeedToEndUpAt === currentRoute) { | ||
// the child state has the same route as the current one, just start navigating there | ||
emitEventAndAttemptStateChange(finalDestinationStateName, parameters) | ||
} else { | ||
// change the url to match the full default child state route | ||
stateProviderEmitter.go(finalDestinationStateName, parameters, { replace: true }) | ||
} | ||
} | ||
} catch (err) { | ||
handleError('stateError', err) | ||
} | ||
@@ -131,2 +144,8 @@ } | ||
function emitEventAndAttemptStateChange(newStateName, parameters) { | ||
stateProviderEmitter.emit('stateChangeAttempt', function stateGo(transition) { | ||
attemptStateChange(newStateName, parameters, transition) | ||
}) | ||
} | ||
function attemptStateChange(newStateName, parameters, transition) { | ||
@@ -246,4 +265,4 @@ function ifNotCancelled(fn) { | ||
} | ||
stateProviderEmitter.evaluateCurrentRoute = function evaluateCurrentRoute(defaultRoute, defaultParams) { | ||
return promiseMe(makePath, defaultRoute, defaultParams).then(function(defaultPath) { | ||
stateProviderEmitter.evaluateCurrentRoute = function evaluateCurrentRoute(defaultState, defaultParams) { | ||
return promiseMe(makePath, defaultState, defaultParams).then(function(defaultPath) { | ||
stateRouterOptions.router.evaluateCurrent(defaultPath) | ||
@@ -250,0 +269,0 @@ }).catch(function(err) { |
{ | ||
"name": "abstract-state-router", | ||
"version": "5.3.0", | ||
"version": "5.4.0", | ||
"description": "The basics of a client-side state router ala the AngularJS ui-router, but without any DOM interactions", | ||
@@ -8,3 +8,4 @@ "main": "index.js", | ||
"zuul": "zuul -- test/*.js", | ||
"test": "tape test/*.js | faucet" | ||
"test": "tape test/*.js | faucet", | ||
"coverage": "covert test/*.js" | ||
}, | ||
@@ -27,16 +28,18 @@ "repository": { | ||
"dependencies": { | ||
"combine-arrays": "^1.0.2", | ||
"debug": "^2.2.0", | ||
"extend": "^2.0.1", | ||
"hash-brown-router": "^1.4.0", | ||
"page-path-builder": "^1.0.2", | ||
"path-to-regexp-with-reversible-keys": "^1.0.3", | ||
"promise": "^7.0.1", | ||
"promise-map-series": "^0.2.1" | ||
"combine-arrays": "~1.0.2", | ||
"debug": "~2.2.0", | ||
"extend": "~2.0.1", | ||
"hash-brown-router": "~1.5.0", | ||
"page-path-builder": "~1.0.2", | ||
"path-to-regexp-with-reversible-keys": "~1.0.3", | ||
"promise": "~7.0.1", | ||
"promise-map-series": "~0.2.1" | ||
}, | ||
"devDependencies": { | ||
"covert": "^1.1.0", | ||
"faucet": "0.0.1", | ||
"tape": "^4.0.0", | ||
"tape-catch": "^1.0.4", | ||
"zuul": "^3.0.0" | ||
} | ||
} |
@@ -7,10 +7,8 @@ [ui-router](https://github.com/angular-ui/ui-router/wiki) is fantastic, and I would use it in all of my projects if it wasn't tied to AngularJS. | ||
For example: if your "user profile" state contains "contact settings" and "profile image" sub-states, browsing from /profile/contact to /profile/image will change the content to display the "profile image" state without touching any of the DOM elements in the user profile page template. | ||
If you've never used AngularJS's ui-router before, check out this post: [Why your webapp needs a state-based router](http://joshduff.com/#!/post/2015-06-why-you-need-a-state-router.md). | ||
To see an example app implemented with a couple of different browser rendering libraries, [click here to visit the state-router-example on Github Pages](http://tehshrike.github.io/state-router-example). | ||
If you have any questions at all, [ask me on Gitter](https://gitter.im/TehShrike/abstract-state-router). | ||
If you have any questions, [ask me on Gitter](https://gitter.im/TehShrike/abstract-state-router)! [![Join the chat at https://gitter.im/TehShrike/abstract-state-router](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/TehShrike/abstract-state-router) | ||
[![Join the chat at https://gitter.im/TehShrike/abstract-state-router](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/TehShrike/abstract-state-router) | ||
# Current renderer implementations | ||
@@ -22,3 +20,3 @@ | ||
If you want to use the state router with some other templating/dom manipulation library, it's pretty easy to set up! Check out the code for the above libraries and/or ping me if you'd like help getting it working. | ||
If you want to use the state router with some other templating/dom manipulation library, [read these docs](https://github.com/TehShrike/abstract-state-router/blob/master/renderer.md)! It's not too bad to get started. | ||
@@ -33,3 +31,3 @@ # API | ||
The `makeRenderer` should be a function that returns an object with four properties: render, destroy, getChildElement, and reset. Still needs to be documented, see test/support/renderer-mock.js for an implementation. | ||
The `makeRenderer` should be a function that returns an object with four properties: render, destroy, getChildElement, and reset. Documentation is [here](https://github.com/TehShrike/abstract-state-router/blob/master/renderer.md) - see [test/support/renderer-mock.js](https://github.com/TehShrike/abstract-state-router/blob/master/test/helpers/renderer-mock.js) for an example implementation. | ||
@@ -80,5 +78,5 @@ The `rootElement` is the element where the first-generation states will be created. | ||
#### context events | ||
The `context` object is also an event emitter that emits a `'destroy'` event when the state is being transitioned away from. You should listen to this event to clean up any workers that may be ongoing. | ||
- 'destroy': emitted when the state is destroyed | ||
### addState examples | ||
@@ -114,2 +112,10 @@ ```js | ||
document.getElementById('tab').innerText = context.content | ||
var intervalId = setInterval(function() { | ||
document.getElementById('tab').innerText = 'MORE CONTENT!' | ||
}, 1000) | ||
context.on('destroy', function() { | ||
clearInterval(intervalId) | ||
}) | ||
} | ||
@@ -152,2 +158,18 @@ }) | ||
## stateRouter.stateIsActive(stateName, [stateParameters]) | ||
Returns true if the state name matches the current active state, and all the properties of the state parameters object match exactly the current state parameter values. | ||
```js | ||
stateRouter.stateIsActive('app.tab1', { fancy: 'yes' }) | ||
``` | ||
## stateRouter.makePath(stateName, [stateParameters]) | ||
Returns a path to the state, starting with an octothorpe `#`, suitable for inserting straight into the `href` attribute of a link. | ||
```js | ||
stateRouter.makePath('app.tab2', { pants: 'no' }) | ||
``` | ||
# State change flow | ||
@@ -172,6 +194,5 @@ | ||
# TODO | ||
# Questions/discussion/future development | ||
- the ability to set an "error" state to go to on errors | ||
- help somebody else set up the state router with their favorite rendering image | ||
- [Support for hash-less paths using HTML5/pushState?](https://github.com/TehShrike/abstract-state-router/issues/39) | ||
@@ -178,0 +199,0 @@ # Maintainers |
@@ -1,2 +0,2 @@ | ||
var test = require('tape') | ||
var test = require('tape-catch') | ||
var getTestState = require('./helpers/test-state-factory') | ||
@@ -94,2 +94,4 @@ | ||
t.timeoutAfter(3000) | ||
stateRouter.addState({ | ||
@@ -106,3 +108,3 @@ name: 'hey', | ||
stateRouter.on('stateChangeError', function(e) { | ||
stateRouter.on('stateError', function(e) { | ||
t.pass('Defaulting to a nonexistent state should cause an error to be emitted') | ||
@@ -114,4 +116,2 @@ t.notEqual(e.message.indexOf('nonexistent'), -1, 'the invalid state name is in the error message') | ||
stateRouter.go('hey') | ||
}) | ||
@@ -128,3 +128,3 @@ | ||
route: '/hay', | ||
defaultChild: function () {return 'rofl'}, | ||
defaultChild: function () { return 'rofl' }, | ||
template: {}, | ||
@@ -149,1 +149,58 @@ resolve: resolve, | ||
}) | ||
test('the default child should activate even if it has an empty route string', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var remember = RememberActivation(testState.location) | ||
t.timeoutAfter(5000) | ||
stateRouter.addState({ | ||
name: 'hey', | ||
route: '/hay', | ||
defaultChild: 'rofl', | ||
template: {}, | ||
activate: remember.activate('hey') | ||
}) | ||
stateRouter.addState({ | ||
name: 'hey.rofl', | ||
route: '', | ||
template: {}, | ||
activate: remember.activate('rofl') | ||
}) | ||
t.test('hey -> hey', function (tt) { | ||
tt.timeoutAfter(5000) | ||
stateRouter.once('stateChangeEnd', remember.onEnd(tt, 'rofl', '/hay/')) | ||
stateRouter.go('hey') | ||
}) | ||
}) | ||
test('the default child should activate even if it doesn\'t have a route string', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var remember = RememberActivation(testState.location) | ||
t.timeoutAfter(5000) | ||
stateRouter.addState({ | ||
name: 'hey', | ||
route: '/hay', | ||
defaultChild: 'rofl', | ||
template: {}, | ||
activate: remember.activate('hey') | ||
}) | ||
stateRouter.addState({ | ||
name: 'hey.rofl', | ||
template: {}, | ||
activate: remember.activate('rofl') | ||
}) | ||
t.test('hey -> hey', function (tt) { | ||
tt.timeoutAfter(5000) | ||
stateRouter.once('stateChangeEnd', remember.onEnd(tt, 'rofl', '/hay/')) | ||
stateRouter.go('hey') | ||
}) | ||
}) |
var test = require('tape') | ||
var interpretStateChange = require('../state-change-logic') | ||
var interpretStateChange = require('../lib/state-change-logic') | ||
@@ -5,0 +5,0 @@ test('State change logic', function(t) { |
var test = require('tape') | ||
var StateState = require('../state-state.js') | ||
var stateComparison = require('../state-comparison.js') | ||
var StateState = require('../lib/state-state.js') | ||
var stateComparison = require('../lib/state-comparison.js') | ||
@@ -5,0 +5,0 @@ function simpleState(name, querystringParameters, route) { |
var test = require('tape') | ||
var StateState = require('../state-state') | ||
var StateState = require('../lib/state-state') | ||
@@ -56,15 +56,2 @@ /* | ||
} | ||
function GetParent(t, ss) { | ||
return function (set) { | ||
var unique = Math.random().toString().slice(2) | ||
ss.add(set.parent, unique) | ||
ss.add(set.child, 'child state') | ||
var apparentState = ss.getParent(set.child) | ||
var parentState = ss.get(set.parent) | ||
var msg = 'found parent state of ' + set.child | ||
t.equal(parentState, apparentState, msg) | ||
} | ||
} | ||
return { | ||
@@ -74,6 +61,2 @@ sets: childParentSets, | ||
name: 'stateState.getParentName' | ||
}, { | ||
sets: childParentSets, | ||
fn: GetParent, | ||
name: 'stateState.getParent' | ||
} | ||
@@ -80,0 +63,0 @@ })() |
var test = require('tape') | ||
var parse = require('../state-string-parser') | ||
var parse = require('../lib/state-string-parser') | ||
@@ -4,0 +4,0 @@ function testParsing(t, input, output) { |
127
test/test.js
var test = require('tape') | ||
var assertingRendererFactory = require('./helpers/asserting-renderer-factory') | ||
var getTestState = require('./helpers/test-state-factory') | ||
var Promise = require('promise') | ||
@@ -318,1 +319,127 @@ test('normal, error-less state activation flow for two states', function(t) { | ||
test('propertiesInRoute', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var hashRouter = testState.hashRouter | ||
t.plan(2) | ||
var timesActivatedCalled = 0 | ||
stateRouter.addState({ | ||
name: 'only', | ||
template: '', | ||
route: '/something/:param/whatever', | ||
activate: function(context) { | ||
timesActivatedCalled++ | ||
if (timesActivatedCalled === 1) { | ||
t.equal(context.parameters.param, 'firstTime') | ||
hashRouter.go('/something/secondTime/whatever') | ||
} else { | ||
t.equal(context.parameters.param, 'secondTime') | ||
t.end() | ||
} | ||
} | ||
}) | ||
stateRouter.go('only', { param: 'firstTime' }) | ||
}) | ||
test('evaluateCurrentRoute with url set', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var hashRouter = testState.hashRouter | ||
var correctRouteCalled = false | ||
t.plan(3) | ||
hashRouter.go('/theUrlWhenThePageIsFirstOpened') | ||
stateRouter.addState({ | ||
name: 'whatever', | ||
route: '/ignored', | ||
template: null, | ||
activate: function() { | ||
t.fail() | ||
} | ||
}) | ||
stateRouter.addState({ | ||
name: 'correct', | ||
route: '/theUrlWhenThePageIsFirstOpened', | ||
template: null, | ||
activate: function(context) { | ||
t.notOk(correctRouteCalled) | ||
correctRouteCalled = true | ||
t.notOk(context.parameters.parameterName) | ||
t.end() | ||
} | ||
}) | ||
t.notOk(correctRouteCalled) | ||
stateRouter.evaluateCurrentRoute('whatever', { parameterName: 'wrong' }) | ||
}) | ||
test('evaluateCurrentRoute with no current route should go to the default', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var hashRouter = testState.hashRouter | ||
var correctRouteCalled = false | ||
t.plan(3) | ||
stateRouter.addState({ | ||
name: 'whatever', | ||
route: '/ignored', | ||
template: null, | ||
activate: function() { | ||
t.fail() | ||
} | ||
}) | ||
stateRouter.addState({ | ||
name: 'correct', | ||
route: '/default', | ||
template: null, | ||
activate: function(context) { | ||
t.notOk(correctRouteCalled) | ||
t.equal(context.parameters.parameterName, 'wrong') | ||
correctRouteCalled = true | ||
t.end() | ||
} | ||
}) | ||
t.notOk(correctRouteCalled) | ||
stateRouter.evaluateCurrentRoute('correct', { parameterName: 'wrong' }) | ||
}) | ||
test('resolve that returns a promise', function(t) { | ||
var testState = getTestState(t) | ||
var stateRouter = testState.stateRouter | ||
var hashRouter = testState.hashRouter | ||
t.plan(1) | ||
stateRouter.addState({ | ||
name: 'some-state', | ||
template: null, | ||
resolve: function() { | ||
return new Promise(function(resolve, reject) { | ||
resolve({ | ||
value: 'this is it!' | ||
}) | ||
}) | ||
}, | ||
activate: function(context) { | ||
t.equal(context.content.value, 'this is it!') | ||
t.end() | ||
} | ||
}) | ||
stateRouter.go('some-state') | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
97247
29
2591
202
5
+ Addeddebug@2.2.0(transitive)
+ Addedms@0.7.1(transitive)
+ Addedpromise@7.0.4(transitive)
- Removeddebug@2.6.9(transitive)
- Removedms@2.0.0(transitive)
- Removedpromise@7.3.1(transitive)
Updatedcombine-arrays@~1.0.2
Updateddebug@~2.2.0
Updatedextend@~2.0.1
Updatedhash-brown-router@~1.5.0
Updatedpage-path-builder@~1.0.2
Updatedpromise@~7.0.1
Updatedpromise-map-series@~0.2.1