-
-
Save jongacnik/aaa30545da91efdeac7d to your computer and use it in GitHub Desktop.
Simple hash-based router with :params and 404
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Router from: https://gist.github.com/jongacnik/aaa30545da91efdeac7d | |
* Added suuuuuper simple options/always method for rbma | |
*/ | |
// Router object | |
var Router = function (opts) { | |
var self = this; | |
// Watch hashchange | |
window.onhashchange = function () { | |
self.process(); | |
}; | |
// Run on load | |
window.onload = function () { | |
self.process(); | |
}; | |
// nice options :) | |
self.options = opts | |
}; | |
// Container object for routes | |
Router.prototype.routes = {}; | |
// Container array for history | |
Router.prototype.history = []; | |
// Processes route change | |
Router.prototype.process = function () { | |
var self = this, | |
fragment = window.location.hash.replace('#', ''), | |
match = self.match(), | |
route = match.route, | |
args = match.args, | |
prevRoute = false, | |
routeObj = []; | |
// console.log('matched', match); | |
// Get prev_route | |
if (self.history.length !== 0) { | |
prevRoute = self.routes[self.history[self.history.length - 1].matcher]; | |
} | |
routeObj = self.routes[route]; | |
// Get current route and unload | |
if (prevRoute && prevRoute.unload) { | |
prevRoute.unload.apply(this); | |
} | |
// nice always :) | |
if (self.hasOwnProperty('options') && self.options.hasOwnProperty('always') && typeof self.options.always === 'function') { | |
self.prevRouteMatcher = self.history.length >= 1 ? self.history[self.history.length - 1].matcher : false | |
self.options.always(routeObj, fragment, self.prevRouteMatcher) | |
} | |
// Check and run 'before' | |
if (routeObj.hasOwnProperty('before') && typeof routeObj.before === 'function') { | |
// Should fire callback with arg 'res' = true/false | |
routeObj.before(function (res) { | |
if (res && routeObj.load) { | |
routeObj.load.apply(this, args); | |
self.history.push({ | |
matcher: route, | |
fragment: fragment | |
}); | |
} else { | |
self.go(self.history[self.history.length - 1].fragment); | |
} | |
}); | |
} else { | |
// No 'before', just load... | |
routeObj.load.apply(this, args); | |
self.history.push({ | |
matcher: route, | |
fragment: fragment | |
}); | |
} | |
// Check and run 'load' if fn exists and before has passed | |
if (routeObj.load && !routeObj.hasOwnProperty('before')) { | |
routeObj.load.apply(this, args); | |
self.history.push({ | |
matcher: route, | |
fragment: fragment | |
}); | |
} | |
}; | |
// Matches routes and fires callback | |
Router.prototype.match = function () { | |
var self = this, | |
fragment = window.location.hash.replace('#', '').replace(/^\/|\/$/g, ''), | |
matcher, | |
route, | |
args = [], | |
matched = false, | |
i, | |
z; | |
fragment = (fragment.substr(0) !== '/') ? '/' + fragment : fragment; | |
// Match root | |
if (fragment === '/' && self.routes.hasOwnProperty('/')) { | |
matched = { route: '/', args: null }; | |
} else { | |
// Match routes | |
for (route in self.routes) { | |
matcher = fragment.match(new RegExp(route.replace(/:[^\s/]+/g, '([\\w-]+)'))); | |
if (matcher !== null && matcher[0] === fragment) { | |
args = []; | |
// Get args | |
if (matcher.length > 1) { | |
for (i = 1, z = matcher.length; i < z; i++) { | |
args.push(matcher[i]); | |
} | |
} | |
matched = { | |
route: route, | |
args: args | |
}; | |
} | |
} | |
} | |
if (!matched) { | |
if (self.routes.hasOwnProperty('/404')) { | |
matched = { route: '/404', args: [fragment] }; | |
} | |
} | |
// Return matched and arguments | |
return matched; | |
}; | |
// Method to reload (refresh) the route | |
Router.prototype.reload = function () { | |
this.process(); | |
}; | |
// Method for binding route to callback | |
Router.prototype.on = function (route, handler) { | |
this.routes[route] = {}; | |
// Build function(s) into route object | |
if (handler && typeof handler === 'function') { | |
// Just passed a single load function | |
this.routes[route].before = false; | |
this.routes[route].load = handler; | |
this.routes[route].unload = false; | |
} else if (handler && typeof handler === 'object') { | |
// Passed an object | |
this.routes[route].before = (handler.before) ? handler.before : false; | |
this.routes[route].load = (handler.load) ? handler.load : false; | |
this.routes[route].unload = (handler.unload) ? handler.unload : false; | |
} else { | |
throw 'Error creating route'; | |
} | |
}; | |
// Method for programatically navigating to route | |
Router.prototype.go = function (path) { | |
var location = window.location, | |
root = location.pathname.replace(/[^\/]$/, '$&'), | |
url, | |
self = this; | |
// Handle url composition | |
if (path.length) { | |
// Fragment exists | |
url = root + location.search + '#' + path; | |
} else { | |
// Null/Blank fragment, nav to root | |
url = root + location.search; | |
} | |
if (history.pushState) { | |
// Browser supports pushState() | |
history.pushState(null, document.title, url); | |
self.process(); | |
} else { | |
// Older browser fallback | |
location.replace(root + url); | |
self.process(); | |
} | |
}; | |
Router.prototype.getPrevRouteMatcher = function () { | |
return this.prevRouteMatcher | |
} | |
module.exports = Router |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var router = new Router(); | |
// Simple single-function | |
router.on('/some/path', function () { | |
// Do something... | |
}); | |
// Params in single-function | |
router.on('/some/path/:thing', function (thing) { | |
// Do something with 'thing'... | |
}); | |
// Full Object: before, load and unload events | |
router.on('/some/path/:thing', { | |
// Check before loading | |
before: function (fn) { | |
if (loggedIn) { | |
// Pass through... | |
fn(true); | |
} else { | |
// You shall not pass | |
fn(false); | |
} | |
}, | |
// Run on load | |
load: function (thing) { | |
// Print variable from params | |
console.log(thing); | |
}, | |
// Run on leave | |
unload: function () { | |
console.log('Outta Here!'); | |
} | |
}); | |
// Handling 404's | |
router.on('/404', function () { | |
// Print 'page not found' or something... | |
// Current (pre-nav) page still appears as URL | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment