mixdown-router
Advanced tools
Comparing version 1.2.3 to 2.0.0
197
index.js
@@ -17,3 +17,3 @@ var _ = require('lodash'); | ||
* | ||
**/ | ||
**/ | ||
this.attach = function (options) { | ||
@@ -23,2 +23,11 @@ var app = options.app; | ||
// Cached regex for stripping a leading hash/slash and trailing space. | ||
var routeStripper = /^[#\/]|\s+$/g; | ||
// Cached regex for stripping leading and trailing slashes. | ||
var rootStripper = /^\/+|\/+$/g; | ||
// Cached regex for removing a trailing slash. | ||
var trailingSlash = /\/$/; | ||
// attach the generator part of the router. | ||
@@ -30,3 +39,3 @@ Generator.constructor.call(instance, namespace); | ||
var _clientRouter = null; | ||
// attach the server side component | ||
@@ -53,3 +62,3 @@ self.getHandler = function(key) { | ||
} | ||
var addParam = function (param, key) { | ||
@@ -73,3 +82,3 @@ | ||
// if HTML history exists (we are in a browser) and the route is not set to be interpreted | ||
// if HTML history exists (we are in a browser) and the route is not set to be interpreted | ||
// on the client, then we skip it. | ||
@@ -91,6 +100,6 @@ if (!route.browser && global.history) { | ||
newRouter.use( | ||
route.method, | ||
route.path, | ||
{ timeout: route.timeout }, | ||
handlers.constructor.prototype._baseHandler.bind(app, handler, route) | ||
route.method, | ||
route.path, | ||
{ timeout: route.timeout }, | ||
_.bind(handlers.constructor.prototype._baseHandler, app, handler, route) | ||
); | ||
@@ -110,20 +119,82 @@ } | ||
// Gets the true hash value. Cannot use location.hash directly due to bug in Firefox where | ||
// location.hash will always be decoded. | ||
self.getHash = function() { | ||
var match = window.location.href.match(/#(.*)$/); | ||
return match ? match[1] : ''; | ||
}; | ||
// Get the cross-browser normalized URL fragment, either from the URL, | ||
// the hash, or the override. | ||
self.getFragment = function(fragment, forcePushState) { | ||
if(fragment == null) { | ||
if(self._hasPushState || forcePushState) { | ||
fragment = window.location.pathname; | ||
var root = self.root.replace(trailingSlash, ''); | ||
if(!fragment.indexOf(root)) fragment = fragment.slice(root.length); | ||
} else { | ||
fragment = self.getHash(); | ||
} | ||
} | ||
return fragment.replace(rootStripper, ''); | ||
}; | ||
self.listen = function(callback) { | ||
/** | ||
* The popstate event - A popstate event is dispatched to the window every time the active history entry changes. If the history entry being activated was created | ||
* by a call to pushState or affected by a call to replaceState, the popstate event's state property contains a copy of the history entry's state object. | ||
* https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history | ||
* https://developer.mozilla.org/en-US/docs/Web/API/window.onpopstate | ||
*/ | ||
window.onpopstate = function(e) { | ||
if (e.state) { | ||
self._hasPushState = !!(window.history && window.history.pushState); | ||
self.root = '/'; // root can be extended with an option later if necessary | ||
var loc = window.location; | ||
// Normalize root to always include a leading and trailing slash. | ||
self.root = ('/' + self.root + '/').replace(rootStripper, '/'); | ||
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === self.root; | ||
if(self._hasPushState) { | ||
// The popstate event - A popstate event is dispatched to the window every time the active history | ||
// entry changes. If the history entry being activated was created by a call to pushState or affected | ||
// by a call to replaceState, the popstate event's state property contains a copy of the history | ||
// entry's state object. | ||
// https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history | ||
// https://developer.mozilla.org/en-US/docs/Web/API/window.onpopstate | ||
window.onpopstate = function(e) { | ||
if (e.state) { // WebKit-based browsers fire onpopstate on page load. | ||
self.navigate(url.format(loc)); | ||
} | ||
}; | ||
} | ||
else { | ||
window.onhashchange = function() { | ||
// you can access state using history.state | ||
self.navigate(url.format(window.location)); | ||
} | ||
}; | ||
self.navigate(url.format(loc)); | ||
}; | ||
} | ||
// WebKit-based browsers fire onpopstate on page load. | ||
// We are replacing initial state to {} here, to be able to go back to the initial page after history.pushState is called | ||
history.replaceState({}, null, window.location.href); | ||
var replaceStateUrl = url.parse(window.location.href); | ||
// If we've started off with a route from a `pushState`-enabled browser, but we're currently in a | ||
// browser that doesn't support it... | ||
if (!self._hasPushState && !atRoot) { | ||
replaceStateUrl.pathname = self.root; | ||
replaceStateUrl.hash = self.getFragment(null, true); | ||
window.location.replace(url.format(replaceStateUrl)); | ||
// Return immediately as browser will do redirect to new url | ||
return true; | ||
// Or if we've started out with a hash-based route, but we're currently in a browser where it could be | ||
// `pushState`-based instead... | ||
} else if (self._hasPushState && atRoot && loc.hash) { | ||
replaceStateUrl.pathname = self.root; | ||
replaceStateUrl.hash = self.getHash().replace(routeStripper, ''); | ||
window.history.replaceState({}, document.title, url.format(replaceStateUrl)); | ||
} | ||
self.navigate(url.format(window.location), callback); | ||
@@ -140,5 +211,4 @@ }; | ||
// 2. function(url, callback) | ||
// @param url {Object|String}: Can be url string or node url object. | ||
// @param url {Object|String}: Can be url string or node url object. | ||
self.navigate = function(route, params, callback) { | ||
var newUrl = null; | ||
@@ -159,30 +229,69 @@ // handle 2 arg variant function signatures. | ||
// if the route is in the route table, then generate the url. if not, then this is a literal url. | ||
// keep a single instance around in a browser. | ||
if (!_clientRouter) { | ||
_clientRouter = self.create(); | ||
} | ||
var newUrl = null; | ||
var loc = self.location || url.parse(window.location.href); | ||
// If the route is in the route table, then generate the url. If not, check for hash or finally a literal url. | ||
if (self.routes[route]) { | ||
newUrl = app.plugins.router.format(route, params); | ||
newUrl = app.plugins.router.url(route, params); | ||
// Test if this href is going to be the same as the current. | ||
// If same, then return b/c there is no reason to re-route. | ||
// TODO: loc.hash has the query and the hash on it. | ||
if (self.initialized && !self._hasPushState && newUrl.pathname == loc.hash && newUrl.search === loc.search) { | ||
return null; | ||
} | ||
if (self.initialized && self._hasPushState && newUrl.pathname === loc.pathname && newUrl.search === loc.search) { | ||
return null; | ||
} | ||
} | ||
else { | ||
newUrl = route; | ||
} | ||
var newUrl = url.parse(route); | ||
if (!history.pushState) { | ||
window.location.href = newUrl; | ||
return; | ||
} | ||
// Test if this href is going to be the same as the current. | ||
// If same, then return b/c there is no reason to re-route. | ||
if (self.initialized && | ||
newUrl.pathname === loc.pathname && | ||
newUrl.search == loc.search && | ||
newUrl.hash == loc.hash | ||
) { | ||
return null; | ||
} | ||
// keep a single instance around in a browser. | ||
if (!_clientRouter) { | ||
_clientRouter = self.create(); | ||
if(newUrl.hash) { | ||
// move the hash route to the url temporarily. | ||
newUrl.pathname = self.root + newUrl.hash.slice(1); | ||
newUrl.hash = null; | ||
} | ||
} | ||
var req = new MockRequest({ url: newUrl }); | ||
var req = new MockRequest({ url: url.format(newUrl) }); | ||
var res = new MockResponse(); | ||
// if the route was matched, then change the url. This will change the url in the address bar before the handler runs. | ||
// if the route was matched, then change the url. This will change the url in the address bar before the handler runs. | ||
// This is good for devs for the situation where there is a problem with the controller handler which will cause the pipeline to stop. | ||
_clientRouter.once('match', function(routerData) { | ||
var httpContext = routerData.httpContext; | ||
if (httpContext.url.href !== window.location.href) { | ||
history.pushState({}, httpContext.url.pathname, httpContext.url.href); | ||
if(self._hasPushState) { | ||
window.history.pushState({}, document.title, httpContext.url.href); | ||
} else { | ||
window.location.hash = '#' + httpContext.url.path.replace(routeStripper, ''); | ||
} | ||
} | ||
self.location = url.parse(window.location.href); | ||
}); | ||
@@ -193,2 +302,9 @@ | ||
// these are here b/c EventEmitter.once() does not remove the event properly after it executes in | ||
// older browsers (specifically IE8) | ||
if (!self._hasPushState) { | ||
_clientRouter.removeAllListeners('match'); | ||
_clientRouter.removeAllListeners('end'); | ||
} | ||
typeof(callback) === 'function' ? callback(err, { | ||
@@ -207,2 +323,3 @@ matched: results[0].matched, | ||
self.initialized = true; | ||
return req; | ||
@@ -215,3 +332,3 @@ }; | ||
try{ | ||
var attempt = self.create(); | ||
var attempt = self.create(); | ||
} | ||
@@ -238,2 +355,2 @@ catch(err){ | ||
module.exports = Router; | ||
module.exports = Router; |
{ | ||
"name": "mixdown-router", | ||
"version": "1.2.3", | ||
"version": "2.0.0", | ||
"description": "Router for mixdown.js", | ||
@@ -23,3 +23,3 @@ "main": "index.js", | ||
"pipeline-router": "~1.1.7", | ||
"hammock": "~0.1.1" | ||
"hammock": "~0.1.4" | ||
}, | ||
@@ -26,0 +26,0 @@ "devDependencies": { |
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
35565
682
2
Updatedhammock@~0.1.4