npm install pilotjs
cd pilotjs
npm install
grunt
Multifunctional JavaScript router solves the problem of routing your application,
providing full control over the route. It can work either by itself or as a part
of other framework, such as Backbone, in which it could be an excellent substitute
for the standard Backbone.Router
and even Backbone.View
. Just try it.
- options — parameter object, see an Example
var Ivan = new Pilot({
el: null // container, which is used to intercept clicks
, production: false // this one is needed for switching off logging and error display
});
Get url by route id. You can use this method to construct url by route id and parameters without keeping links inside.
- id — unique route identifier
- params — parameters used to construct url
- extra — additional parameters
var Ivan = new Pilot;
// Add route
Ivan.route('address', '/:city/(street/:id/)', function (evt, req){ });
Ivan.getUrl('address'); // return "/"
Ivan.getUrl('address', { city: 'berlin' }); // "/berlin/"
Ivan.getUrl('address', { city: 'berlin', id: 123 }); // "/berlin/street/10/"
Navigation by the route id. It is often necessary not only to understand how user moved, but also to move user to the correct url, depending on his actions. This method allows you to change the route, operating with its id and parameters only.
- id — unique route identifier
- params — parameters used to construct url
var Ivan = new Pilot;
Ivan.route('coordinates', '/coordinates/:lat/:long', function (evt, req){ });
Ivan.go('coordinates', { lat: 16 }); // location: "/coordinates/16/"
Ivan.go('coordinates', { lat: 16, long: 178 }); // location: "/coordinates/16/178/"
Navigation by url. With the call of this method, router finds all the handlers for this route and goes to it.
If url has not been changed, the transition will not be executed, but you can get round it using force
parameter.
- url — relative or absolute url
- force — force the user to move to the url even if he's already on it
var Ivan = new Pilot
.on('beforeroute', function (req){ })
.on('route', function (req){ })
.on('404', function (req){ console.log('Route not found'); })
.route('/base/moscow/', function (evt, req){
console.log('Greetings from Moscow');
})
;
Ivan.nav('/base/moscow/'); // "Greetings from Moscow"
Ivan.nav('/base/moon/'); // "Route not found"
Start router. When called with no parameters, the initial url is obtained through Pilot.getLocation
.
- url — initial routing point
A simple way to add a route.
- pattern — used for matching request while navigation
- handler — called with
event
andrequest
parameters, ifpattern
matched with url - withEndEvent — calls
handler
in the end of route
// Without "routeend"
Ivan.route('/airport/', function (evt, req){
console.log(evt.type, '-> ', req.path);
});
// With "routeend"
Ivan.route('/:city/(street/:id/)', function (evt, req){
if( evt.type == 'routeend' ){
console.log(evt.type, '-> ', req.referrer.path);
} else {
console.log(evt.type, '-> ', req.params.city, ', ', req.params.id);
}
}, true);
Ivan.nav('/airport/'); // "routestart -> /airport/"
Ivan.nav('/home/street/123'); // "routestart -> home, 123"
Ivan.nav('/home/street/321'); // "routechange -> home, 321"
Ivan.nav('/airport/'); // "routestart -> /airport/"
// "routeend -> /home/street/321/"
A simple way to add a named route.
- id — unique route identifier
- other parameters
Ivan.route('base', '/base/:lat/:long');
// Navigate by id
Ivan.go('base', { lat: 16, lon: 179 }); // location: /base/16/179
Add a route controller. Route controller is a powerful tool. With its help one you can tell the
router (passing Deffered
parameter), that it ought to wait for the receipt of data and only
then make the move to the route. This approach works well in combination with multiple controllers
on one route, where each one performs its small task, e.g., the first one gets a banner, the second
one get list of posts, and the third one gets user profile.
- ctrl — controller methods or successor
Pilot.Route
- options — initialization options
Ivan.route('/base/:id', {
loadData: function (req){
return $.ajax('/base/get/'+req.params.id, function (base){
this.setData(base);
}.bind(this));
},
onRoute: function (evt, req){
// "routestart" and "routeend"
var base = this.getData();
console.log('route:', base.name);
}
});
Ivan.nav('/base/123'); // "route: Kienitz"
Add a named route controller.
var airport = Pilot.Route.extend({
onRoute: function (){
console.log('base:', this.getData().name);
}
});
Ivan
.route('/base/1', airport, { data: { name: 'Moscow' } })
.route('/base/2', airport, { data: { name: 'Yaroslavl' } })
;
Ivan.nav('/base/1'); // "base: Moscow"
Ivan.nav('/base/2'); // "base: Yaroslavl"
Create a group and assign routes relative to it.
- pattern — base pattern
var Ivan = new Pilot;
.createGroup('/base/')
.route('.', function (evt, req){ console.log('def'); })
.route(':id', function (evt, req){
console.log('base: '+.req.params.id);
})
.closeGroup()
;
Ivan.nav('/base/'); // "def"
Ivan.nav('/base/123/'); // "base: 123"
Create a named group
- id — unique route identifier
Close the group and return the last one or router.
Add a handler for one or more events. Pilot has four events: beforeroute
, route
, 404
и error
- events — one or more events,
namespace
can be used - fn — handler function
new Pilot
.on('beforeroute', function (evt/**$.Event*/, req/**Object*/){ })
.on('route', function (evt/**$.Event*/, req/**Object*/){ })
.on('404', function (evt/**$.Event*/, req/**Object*/){ })
.on('error', function (evt/**$.Event*/, err/**Error*/){ })
;
Switch off event handler.
- events — one or more events,
namespace
can be used - fn — handler function
new Pilot
// Subscribe
.on('route.one', function (evt/**$.Event*/, req/**Object*/){
// Unsubscribe using namespace
this.off('.one');
})
;
Emit event.
- event — event name
- args — extra arguments
var Ace = new Pilot
.on('custom', function (evt/**$.Event*/, foo/**String*/){ })
;
// Emit event
Ace.emit('custom', ["foo"]);
Browsing history, the behavior is similar to window.history
.
var Ace = new Pilot;
Ace.nav('/airport/');
Ace.nav('/airport/depot/');
// etc
console.log(Ace.history);
// ["http://site.com/airport/", "http://site.com/airport/depot/", ..]
Verify that you can go back through history
Verify that you can go forward through history
Go to the previous url in history
Go to the next url relative to the current position in history
This class of the route controller allows not only to control events of starting, changing or ending of the route, but also to inform the router that before going to the correct url it has to wait the collection of data necessary to this controller.
Available events: routestart
, routechange
and routeend
.
There is also route
event, which is similar to routestart
and routechange
.
var airbase = Pilot.Route.extend({
init: function (){
this.on('routestart routeend', function (evt/**$.Event*/, req/**Object*/){
// ...
});
},
onRoute: function (evt/**$.Event*/, req/**Object*/){
// You can also define a method with the name of the event
}
});
Route initialization flag.
var airbase = Pilot.Route.extend({
loadData: function (){
if( !this.inited ){
this.setData({ name: 'Ramstein' });
}
}
});
Link to the router.
List of methods that will be executed in the context of the object. It's very convenient for using with functions which will be used as event handlers.
var City = Pilot.Route.extend({
name: 'Moscow',
boundAll: ['matryoshka', 'vodka', 'balalaika'],
init: function (){
$('#take').click(this.matryoshka);
$('#drink').click(this.vodka);
$('#play').click(this.balalaika);
},
matryoshka: function (evt){ console.log(this.city+': take ', evt) },
vodka: function (evt){ console.log(this.city+': drink ', evt) },
balalaika: function (evt){ console.log(this.city+': play ', evt) },
});
Bound the method with the context of the controller.
- fn — function or its name in the controller
var airport = Pilot.View.extend({
el: '#airport',
init: function (){
// Bound function
this.$el.on('mouseenter', this.bound(function (evt){
this._onHover(evt);
}));
// Bound by method name
this.$el.on('mouseleave', this.bound('_onHover'));
},
_onHover: function (evt){
this.$el.toggleClass('hovered', evt.type == 'mouseenter');
}
});
This method is intended to redefine and should be called once at the time of initialization of the controller.
Remember that the initialization is not involved in creating the instance, that occurs in the first
call of the controller after loadData
, but before the routestart
event.
var airport = Pilot.Route.extend({
init: function (){
this.$el = $('#airport');
}
});
This method should be called before routestart
, routechange
.
If $.Deffered
returns, router will wait for the end of the controller data collection
and then execute the navigation.
- req — request object
var airport = Pilot.Route.View.extend({
loadData: function (req){
return $.ajax('/load/data/', req.query, this.bound(function (data){
this.setData( data );
}));
},
onRoute: function (){
var data = this.getData();
}
});
Get url by route id. You can use this method to construct url by route id and parameters without keeping links inside.
- id — unique route identifier
- params — parameters used to construct url
- extra — additional parameters
A simple method to get controller data.
var airport = Pilot.Route.extend({
data: { name: 'default' }
});
(new airport).getData().name; // "default"
(new airport({ data: { name: 'NY' } })).getData().name; // "NY"
Set new controller data or merge with the current ones.
- data — new data
- merge — merge with the current ones
var airport = Pilot.Route.extend({
data: { name: 'default', city: 'unknown' }
});
(new airport).setData({ name: 'Foo' }).getData();
// { name: 'Foo' }
(new airport).setData({ name: 'Foo' }, true).getData();
// { name: 'Foo', city: 'unknown' }
(new airport).setData({ name: 'Foo', city: 'Bar' }).getData();
// { name: 'Foo', city: 'Bar' }
Pilot.Route
successor implements methods for working with DOM elements, events and patterning.
By default, Pilot.View
is subscribed to routestart
and routeend
events and controls the visibility
of a DOM element associated with it, setting it to display: none
or removing it.
Link to the DOM element, with which View
is working.
var airport = Pilot.View.extend({
el: '#airport-default'
});
(new airport).el; // HTMLElement: <div id="airport-default">..</div>
(new airport({ el: '#moscow' })).el; // HTMLElement: <div id="moscow">..</div>
jQuery collection, for more convenient work.
var base = Pilot.View.extend({
el: '#moscow'
});
(new base).el; // jQuery[<div id="moscow">..</div>]
(new base({ el: '#moon' })).el; // jQuery[<div id="moon">..</div>]
If you specify this option, this tag will be created while the initialization.
var base = Pilot.View.extend({
tagName: 'span'
});
(new base).el; // HTMLElement: <span>..</span>
(new base).$el.appendTo('body'); // jQuery[<span>..</span>]
Create a tag and put it in the container.
var base = Pilot.View.extend({
tag: '#box span.base.base_military'
});
(new base).el; // HTMLElement: <span class="base base_military">..</span>
var airbase = Pilot.View.extend({
el: '#aribase',
sigleton: true,
onRouteStart: function (evt, req){
console.log('start:', req.path);
},
onRouteChange: function (evt, req){
console.log('change:', req.path);
},
onRouteStart: function (evt, req){
console.log('end:', req.path);
}
});
var Ivan = new Pilot
.route('/sky/foo/', airbase)
.route('/sky/bar/', airbase)
.route('/sky/baz/', function (evt, req){
console.log('Sky base Baz');
})
.route('/sky/qux/', airbase)
;
Ivan.nav('/sky/foo/'); // "start: /sky/foo/"
Ivan.nav('/sky/bar/'); // "change: /sky/bar/"
Ivan.nav('/sky/qux/'); // "change: /sky/qux/"
Ivan.nav('/sky/baz/'); // "Sky base Baz"
// "end: /sky/baz/"
Here can be any patterning function.
var region = Pilot.View.extend({
template: function (data/**Object*/){
/* "data" is equal this.getData() */
// Use any template engine
return xtpl.fetch('templates/region.xtpl', data);
}
});
This method is called at the start and in the end of the route. Its redefining can help you change the way elements are displayed, e.g., to add the animation.
- state —
true
: route start,false
: route end
var region = Pilot.View.extend({
toggleView: function (state/**Boolean*/){
this.$el.animate({ opacity: +state }, 'fast');
}
});
Set the element with which 'View' is working (automatically changes this.el
and this.$el
properties).
- selector - string containing jQuery selector or HTMLElement, detail
Select elements inside the 'View' (equal to this.$el.find
, but more easy).
- selector - string containing jQuery selector or HTMLElement, detail
Get HTML based on this.template
and sent data or 'View' data.
- data — data for patterning
Refresh HTML this.el
by this.getHtml()
var city = Pilot.View.extend({
templateFile: 'city/default.xtpl',
template: function (obj){
return xtpl.fetch(this.templateFile, obj);
},
onRoute: function (){
this.render();
}
});
<a name="Pattern-syntax>
- `/search/` — strict match
- `/gallery/:tag/` — parameterized
- `/search/result/:page?` — parameterized (optional)
- `/user/:id(\\d+)` — parameter validation
- `/search/(result/:page/)?` — grouping
route: /gallery/:tag/:perPage?(/page/:page)?
request: /gallery/cubism/20/page/123?search=text
Absolute url: http://domain.com/gallery/cubism/20/page/3?search=text
The path relative to the web-site root: /gallery/cubism/20/page/3
GET parameters string: ?search=text
GET parameters object: { search: "text" }
Route parameters: { tag: "cubism", perPage: 20, page: 123 }
Contains url of previous request: http://domain.com/gallery/cubism/20/page/12
By default, the library doesn't contain any polyfills and rely only on native support.
Use the full History API, otherwise location.hash
.
Pilot.pushState = true;
Get current location.
Set a new location.
- req — request object
First release