View on GitHub

Hash-router

Tiny and lightweight browser router library, developed with SPA in mind :)

download .ZIPdownload .TGZ

Welcome to Hash-Router Pages.

To understand why i create this library take a look to README.md or to the main page on github.

A little excerpt!
With this library you can manage browser url and create your custom browsing path rules; this library give you everything you need to parse hash parts, querystring key-values, listening for any url-hash changes running your code.

What this library does that the other libraries don't do is a great (im my opinion :)) support for lazy loading concept, a perfect companion for SPA with lazy loading mechanism.

But remember that this library don't tell you how to lazy load files, only support the concept independently on choosen mechanism, you can use this library without any lazy loading engine too :) like any javascript router.

Getting Started

Any route rule must have an path: and a at least one callback function :before, :on, :after.

//route definition
var route = {
  path: '#/',
  on: function() { ... }
}

The rule can be customized as you want.

//complex route definition
var route = {
  path: '#/',
  before: function() { ... },
  on: function() { ... },
  after: function() { ... },
  config: {
    k:v,
    ...
  }
}

Add rule to route.

//add rule
Router.add(route);

Initialize router to start listening url-hash changes.

//init rule
Router.init();

Route Definition

A route definition is a simple object that have to contain a property path and at least one callback function before, on and after.

//route definition
var route = {
  path: '#/',
  on: function() { ... }
}

A route definition can be easily customized to contain any object that will be passed to callback function.

//complex route definition
var route = {
  path: '#/',
  before: function() { 
    //The [this] object is a copy of the [route] object plus:
    //this.params containing hash tokens
    //this.event containing data about function execution
    //this.task containing methods to manage functions execution
    //this.url containing the original url hash
    //this.query containing the querystring parameters
  },
  on: function() { ... },
  after: function() { ... },
  config: {
    namespace:'myNameSpace',
    controller: 'myController',
    ...
  }
}

The path property can be a normal static hash like #/access/login or a parametrized string like #/blog/:date/:slug, in this case the route parameters will be completed with hash tokens.

//if my url is http://localhost/blog/20140101/hello-world
var route = {
  path: '#/blog/:date/:slug',
  on: function() { 
    //this.params.date contains '20140101'
    //this.params.slug contains 'hello-world'
  },
}

There are four reserved property that you can't use to extend the route definition.

The params containing route parameters.
The query containing querystring parameters.
The url containing the original hash.
The event containing two property: state indicating which function is executing (before, on, after) and previousResult containing the value returned from previous function.
The task containing only a method done() that is mandatory to call inside the function to instructs the router task manager to execute next function.

The task is very useful in the scenaries where a function make an async call (like ajax), it help you on choose when execute next function.

//task use example
var route = {
  path: '#/',
  before: beforeFunction,
  on: onFunction,
}

//not using task
var beforeFunction = function(){
  //something to do
  // no call to this.task.done()
}

var onFunction = function(){
  //execution never reach this
}

//but if ... use task
var beforeFunction = function(){
  //something to do
  this.task.done();
}

var onFunction = function(){
  //execution reach this :)
  //this.event.previousResult = null
}

//but if ... use task in this way
var beforeFunction = function(){
  //something to do
  this.task.done('an object to pass to next function');
}

var onFunction = function(){
  //execution reach this :)
  //this.event.previousResult = 'an object to pass to next function'
}

//an ajax example
var beforeFunction = function(){
  //use jquery ajax implementation
  $.ajax({
  async:true,
  url: '...',
  success: function(data){
    this.task.done(data);
  },
  error: function(request){
    this.taks.done('error');
  });
}

var onFunction = function(){
  //execution reach this only after ajax request is completed :)
  //this.event.previousResult = ajax result
}

init(), init(onRouteChange,OnRouteNotFound)

Start to listen for url-hash changes.

If page starts with no hash (http://website/) then the router redirect to default empty hash (http://website/#/), else if the page is called with hash (http://website/#/blog) then the router run the functions specified on matching rule.

Pay attention when you call init() method, the suggestion is after dom is ready and after any bootstrap js code is executed.

The init method can be called with a function that will be executed before the first route function is called.

The init method can be called with a function that will be executed when no route match the url hash, this is useful on lazy loading scenary because if no route match existing rules probabily you have to lazy load new js file containing missed rules.
Because this library supports lazy loading but leave to developers to choose the right mechanism, the only value passed to the callback function is an array of hash parts, without '#' Ex. #/account/login = [account][login].
An example is the MVC pattern + "Lazy Loading Controller" concept; the controller should be responsible for its routes, so you have to load controller js file and then register the routes with Router.add() method.
But if you prefer you can use it to catch NotFound errors :)

//init rule with onRouteChange
var onChangeRoute = function(route){
// do something
};
Router.init(onChangeRoute);

//init rule with onRouteNotFound
var onNotFound = function(tokens){
//when a url hash not match with any existing routes (ex. #/access/login)
//this function will be called with array = ['access','login']
//now i can assume for convention that array[0] is the name of my controller
//and with a library that help to load external static file, i can load my controller and register its routes

  $.scripts('/app/controllers/'+token[0]+'.js', function(){
    var loadedController = new window[token[0]]();
    $.each(loadedController.routes, function(index,item){
      Router.add(item,true);
    });
  });

}
Router.init(null,onNotFound);

add(route), add(route,overwrite)

Add a rule.

If a rule with the same path already exist, then it will be ignored.

Is possible to overwrite the existing rules passing true to overwrite parameter.

Can exist only a rule for the same path.

//add rule
var route = {...}
Router.add(route);
//add rule overwriting existing
Router.add(route,true);

navigate(hash)

Change the url hash and run functions defined in rule matching path property.

//navigate to hash passed as first argument
Router.navigate('#/access/login');

run(route)

Execute functions defined in the route.

//execute functions defined in rule passed as first argument
var route = {
  path: '#/',
  before: beforeFunction,
  on: onFunction,
}
Router.run(route);

findRoute(path)

Find a route by path property.

//find route by path passed as first argument
var route = {
  path: '#/blog/:date/:slug',
  before: beforeFunction,
  on: onFunction,
}
var foundRoute = Router.findRoute('#/blog/:date/:slug');

matchRoute(hash)

Find a route definition by path property matching the value passed as first argument.

//find route that match path property
var route = {
  path: '#/blog/:date/:slug',
  before: beforeFunction,
  on: onFunction,
}
var foundRoute = Router.matchRoute('#/blog/20140101/hello-world');

Authors and Contributors

A great thanks for the works that inspired me to the developers of:
path.js
sammy.js
director.js

I will continue to update and maintain this library, and to evolve thanks to your feedbacks.

Michael Sogos, here for serve you :)

Support or Contact

Having trouble with my library? After read documentation in this page you can send me an issue on GitHub

Any feedback about a new function/behaviour/idea is appreciated.