Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added support for HTML5 History API

Added support for configure option html5history and related browser side
implementation. With this option routing can use pretty URIs instead of
the hash URIs. It's worth to notice that switching the method most
likely requires changes also to backend since the browser will, upon
initial page load, ask for the URI it is loading instead of the service
index (e.g. yourhost.com/users/1).

There is a bug in Chrome which causes it to trigger onpopstate event
when performing initial page load. Since the event handler must be
called upon init manually to ensure correct content, the bug would cause
two handler calls in Chrome. Now the handler is set in a setTimeout in
order to avoid (most) double calls in Chrome.

See Chrome bug for reference:
http://code.google.com/p/chromium/issues/detail?id=63040
  • Loading branch information...
commit 793a213844aca5f600f8a69fc82dcceb0800a251 1 parent 34df309
@vesse authored
Showing with 55 additions and 13 deletions.
  1. +52 −13 lib/director/browser.js
  2. +3 −0  lib/director/router.js
View
65 lib/director/browser.js
@@ -30,6 +30,7 @@ var dloc = document.location;
var listener = {
mode: 'modern',
hash: dloc.hash,
+ history: false,
check: function () {
var h = dloc.hash;
@@ -41,15 +42,16 @@ var listener = {
fire: function () {
if (this.mode === 'modern') {
- window.onhashchange();
+ this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
- init: function (fn) {
+ init: function (fn, history) {
var self = this;
+ this.history = history;
if (!window.Router.listeners) {
window.Router.listeners = [];
@@ -64,7 +66,20 @@ var listener = {
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
- window.onhashchange = onchange;
+ // At least for now HTML5 history is available for 'modern' browsers only
+ if (this.history === true) {
+ // There is an old bug in Chrome that causes onpopstate to fire even
+ // upon initial page load. Since the handler is run manually in init(),
+ // this would cause Chrome to run it twise. Currently the only
+ // workaround seems to be to set the handler after the initial page load
+ // http://code.google.com/p/chromium/issues/detail?id=63040
+ setTimeout(function() {
+ window.onpopstate = onchange;
+ }, 500);
+ }
+ else {
+ window.onhashchange = onchange;
+ }
this.mode = 'modern';
}
else {
@@ -116,7 +131,14 @@ var listener = {
this.writeFrame(s);
}
- dloc.hash = (s[0] === '/') ? s : '/' + s;
+ if (this.history === true) {
+ window.history.pushState({}, document.title, s);
+ // Fire an onpopstate event manually since pushing does not obviously
+ // trigger the pop event.
+ this.fire();
+ } else {
+ dloc.hash = (s[0] === '/') ? s : '/' + s;
+ }
return this;
},
@@ -152,31 +174,40 @@ var Router = exports.Router = function (routes) {
this._insert = this.insert;
this.insert = this.insertEx;
+ this.historySupport = (window.history !== null ? window.history.pushState : null) !== null
+
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
- this.handler = function() {
- var hash = dloc.hash.replace(/^#/, '');
+ this.handler = function(e) {
+ var hash = self.history === true ? self.getPath() : dloc.hash.replace(/^#/, '');
self.dispatch('on', hash);
};
- if (dloc.hash === '' && r) {
- dloc.hash = r;
- }
+ if (this.history === false) {
+ if (dloc.hash === '' && r) {
+ dloc.hash = r;
+ }
- if (dloc.hash.length > 0) {
+ if (dloc.hash.length > 0) {
+ this.handler();
+ }
+ }
+ else {
+ // When using HTML5 History API, always run the handler. Unlike with
+ // hash routing we cannot really know if we should execute or not.
this.handler();
}
- listener.init(this.handler);
+ listener.init(this.handler, this.history);
return this;
};
Router.prototype.explode = function () {
- var v = dloc.hash;
+ var v = this.history === true ? this.getPath() : dloc.hash;
if (v[1] === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
@@ -246,4 +277,12 @@ Router.prototype.getRoute = function (v) {
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
-};
+};
+
+Router.prototype.getPath = function () {
+ var path = window.location.pathname;
+ if (path.substr(0, 1) !== '/') {
+ path = '/' + path;
+ }
+ return path;
+};
View
3  lib/director/router.js
@@ -144,6 +144,9 @@ Router.prototype.configure = function (options) {
this.notfound = options.notfound;
this.resource = options.resource;
+ // Client only, but browser.js does not include a super implementation
+ this.history = (options.html5history && this.historySupport) || false;
+
//
// TODO: Global once
//
Please sign in to comment.
Something went wrong with that request. Please try again.