Skip to content

Instantly share code, notes, and snippets.

@lazd
Created February 17, 2015 06:21
Show Gist options
  • Select an option

  • Save lazd/fcb94fa63f3d67ca2a48 to your computer and use it in GitHub Desktop.

Select an option

Save lazd/fcb94fa63f3d67ca2a48 to your computer and use it in GitHub Desktop.
A super-charged Backbone router
Backbone.SuperRouter = Backbone.Router.extend({
// The name of the application (used when setting title)
appName: 'MyApp',
// The default route
homeRoute: '/',
// List of URLs to avoid tracking
noTrack: [],
// Handle starting of backbone history
start: function() {
if (!this._started) {
Backbone.history.start();
this._started = true;
}
},
stop: function() {
Backbone.history.stop();
this._started = false;
},
initialize: function() {
// Build reverse map
this.map = {};
for (var route in this.routes) {
this.map[this.routes[route]] = route.replace(/\/:id/, '').replace(/:/, '');
}
// Default page
this.url = '/' + this.homeRoute;
this.bind('all', this._handleEvent);
},
/**
Set the title of the page
*/
setTitle: function() {
var newTitle = '';
// Add each part, passed as separate argument
for (var i = 0; i <= arguments.length; i++) { // Iterate one past arguments length to get the last dash
if (newTitle) {
newTitle += ' - ';
}
if (arguments[i]) {
newTitle += arguments[i];
}
}
// Throw app name on the end
newTitle += this.appName;
// Set the title
document.title = newTitle;
},
/**
Overridden routing method
Triggers a navigation event, can call event.preventDefault() to prevent routing
*/
route: function (route, name, callback) {
// Reset current component
vs.currentComponent = null;
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (!callback) callback = this[name];
Backbone.history.route(route, _.bind(function(fragment) {
// Continue to navigate
var next = _.bind(function() {
var args = this._extractParameters(route, fragment);
if (callback) callback.apply(this, args);
this.trigger.apply(this, ['route:' + name].concat(args));
Backbone.history.trigger('route', this, name, args);
}, this);
var canNavigate = true;
var navigatingTo = window.location.hash;
// Trigger an event
this.trigger('navigation', {
// Provide a preventDefault function to stop navigation
preventDefault: function() {
canNavigate = false;
},
// Provide a next function to continue navigation
next: _.bind(function() {
// Change the hash back
this.navigate(navigatingTo);
// Continue to navigate
next();
}, this)
});
// Check if we're allowed to navigate away
if (canNavigate) {
// Continue to navigate
next();
}
else {
// Navigate without trigger, changing the hash back
this.navigate(this.previousHash);
}
}, this));
return this;
},
_handleEvent: function(route) {
// Only allow route events to be tracked
if (route.slice(0, 5) !== 'route')
return;
// Get the route function name
var url = route.slice(6);
// Get out immediately, homepage is a redirect
if (url === 'homepage') {
return;
}
// Get the actual route
url = this.map[url];
// Store previous URL
this.previousHash = window.location.hash;
this._storeLastPage();
this._trackPageview(url);
},
redirect: function(routeHash) {
// Remove the part of the history referencing the current page
// Keep the same state, and just pass an empty string for title (does nothing)
// This allows users to hit back and end up where they were before
window.history.replaceState(window.history.state, '', routeHash);
// Tell the router to navigate to the new route
this.navigate(routeHash, { trigger: true });
},
navigate: function(fragment, options) {
Backbone.history.navigate(fragment, options);
// Manually track navigation if specified and if it's done without a trigger
if (options && !options.trigger && options.track) {
// Remove IDs from the end of the fragment
var url = Backbone.history.fragment.replace(/\/[0-9a-f]{24}$/, '');
// Don't track the same URL twice in a row, due to a navigate call within the app
if (this.url === '/' + url) {
return;
}
this._trackPageview(url);
}
},
/**
Load the last route viewed
*/
startAtLastPage: function() {
if (Modernizr.localstorage && window.localStorage['lastHash']) {
window.location.hash = window.localStorage['lastHash'];
}
},
/**
Store the hash of the last route viewed
*/
_storeLastPage: function(url) {
if (Modernizr.localstorage) {
window.localStorage['lastHash'] = url || window.location.hash;
}
},
/**
Track page views for the given URL with Google Analytics
*/
_trackPageview: function(url) {
// Store current URL
this.url = '/' + url;
// Stop certain URLs from being tracked
if (this.noTrack.indexOf(url) !== -1) {
return;
}
if (url) {
// Track page views with Google Analytics
ga('send', 'pageview', {
page: this.url,
title: document.title // Capture immediately, in case this fires later and catches a customer's scene/photo name
});
}
},
routes: {
'': 'root',
'dashboard': 'dashboard'
},
root: function() {
// Navigate to the home route
this.navigate(this.homeRoute, { trigger: true });
},
dashboard: function() {
// Do stuff
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment