Skip to content

Instantly share code, notes, and snippets.

@doortts
Created January 26, 2012 02:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save doortts/1680542 to your computer and use it in GitHub Desktop.
Save doortts/1680542 to your computer and use it in GitHub Desktop.
Add bookmark and application state navigation(port from backbone.js) to crossroads.js
// crossroads.navigator
// ----------------
// Handles cross-browser history management, based on URL fragments. If the
// browser does not support `onhashchange`, falls back to polling.
crossroads.navigator = function() {
};
// Cached regex for cleaning hashes.
var hashStrip = /^#*/; ///^\w*#/;
// Cached regex for detecting MSIE.
var isExplorer = /msie [\w.]+/;
// Has the history handling already been started?
var historyStarted = false;
// Set up all inheritable **crossroads.navigator** properties and methods.
$.extend(crossroads.navigator, {
// The default interval to poll for hash changes, if necessary, is
// twenty times a second.
interval: 50,
// Get the cross-browser normalized URL fragment, either from the URL,
// the hash, or the override.
getFragment: function (fragment, forcePushState) {
if (fragment == null) {
if (this._hasPushState || forcePushState) {
fragment = window.location.pathname;
var search = window.location.search;
if (search) fragment += search;
if (fragment.indexOf(this.options.root) == 0) fragment = fragment.substr(this.options.root.length);
} else {
fragment = window.location.hash;
}
}
return fragment.replace(hashStrip, '');
},
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start: function (options) {
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
if (historyStarted) throw new Error("Crossroads.History has already been started");
this.options = $.extend({}, { root: '/' }, this.options, options);
this._wantsPushState = !!this.options.pushState;
this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState);
var fragment = this.getFragment();
var docMode = document.documentMode;
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
if (oldIE) {
this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
this.navigate(fragment);
}
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if (this._hasPushState) {
$(window).bind('popstate', $.proxy(this, 'checkUrl'));
} else if ('onhashchange' in window && !oldIE) {
$(window).bind('hashchange', $.proxy(this, 'checkUrl'));
} else {
setInterval($.proxy(this.checkUrl, this), this.interval);
}
// Determine if we need to change the base url, for a pushState link
// opened by a non-pushState browser.
this.fragment = fragment;
historyStarted = true;
var loc = window.location;
var atRoot = loc.pathname == this.options.root;
if (this._wantsPushState && !this._hasPushState && !atRoot) {
this.fragment = this.getFragment(null, true);
window.location.replace(this.options.root + '#' + this.fragment);
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
this.fragment = loc.hash.replace(hashStrip, '');
window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
}
return this.loadUrl();
},
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`, normalizing across the hidden iframe.
checkUrl: function (e) {
var current = this.getFragment();
if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
if (this.iframe) this.navigate(current);
this.loadUrl() || this.loadUrl(window.location.hash);
},
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl: function (fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
//call crossroads to do the routing using relative path
//var matched = crossroads.route(fragment);
var request = window.location.pathname + window.location.hash;
var matched = crossroads.route(request);
return matched;
},
// Save a fragment into the hash history. You are responsible for properly
// URL-encoding the fragment in advance. This does not trigger
// a `hashchange` event.
navigate: function (fragment, triggerRoute) {
var frag = (fragment || '').replace(hashStrip, '');
if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
if (this._hasPushState) {
var loc = window.location;
if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
this.fragment = frag;
window.history.pushState({}, document.title, loc.protocol + '//' + loc.host + frag);
} else {
window.location.hash = frag //this.fragment = frag;
if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
this.iframe.document.open().close();
this.iframe.location.hash = frag;
}
}
if (triggerRoute) this.loadUrl(fragment);
},
forward: function () {
if (this.iframe) {
this.iframe.contentWindow.history.forward()
}
else {
window.history.forward();
}
},
back: function () {
if (this.iframe) {
this.iframe.contentWindow.history.back()
}
else {
window.history.back();
}
},
go: function (index) {
if (this.iframe) {
this.iframe.contentWindow.history.go(index)
}
else {
window.history.go(index);
}
},
getCurrentHash: function () {
if (this.iframe) {
return this.iframe.contentWindow.location.hash;
}
else {
return window.location.hash;
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment