Created
April 17, 2014 09:15
-
-
Save sunnyluthra/10967550 to your computer and use it in GitHub Desktop.
Changed discourse_location.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
@module Discourse | |
*/ | |
var get = Ember.get, set = Ember.set; | |
var popstateFired = false; | |
var supportsHistoryState = window.history && 'state' in window.history; | |
// Thanks: https://gist.github.com/kares/956897 | |
var re = /([^&=]+)=?([^&]*)/g; | |
var decode = function(str) { | |
return decodeURIComponent(str.replace(/\+/g, ' ')); | |
}; | |
$.parseParams = function(query) { | |
var params = {}, e; | |
if (query) { | |
if (query.substr(0, 1) === '?') { | |
query = query.substr(1); | |
} | |
while (e = re.exec(query)) { | |
var k = decode(e[1]); | |
var v = decode(e[2]); | |
if (params[k] !== undefined) { | |
if (!$.isArray(params[k])) { | |
params[k] = [params[k]]; | |
} | |
params[k].push(v); | |
} else { | |
params[k] = v; | |
} | |
} | |
} | |
return params; | |
}; | |
var popstateCallbacks = []; | |
/** | |
`Ember.DiscourseLocation` implements the location API using the browser's | |
`history.pushState` API. | |
@class DiscourseLocation | |
@namespace Discourse | |
@extends Ember.Object | |
*/ | |
Ember.DiscourseLocation = Ember.Object.extend({ | |
init: function() { | |
set(this, 'location', get(this, 'location') || window.location); | |
this.initState(); | |
}, | |
/** | |
@private | |
Used to set state on first call to setURL | |
@method initState | |
*/ | |
initState: function() { | |
var location = this.get('location'); | |
if (location && location.search) { | |
this.set('queryParams', $.parseParams(location.search)); | |
} | |
set(this, 'history', get(this, 'history') || window.history); | |
this.replaceState(this.formatURL(this.getURL())); | |
}, | |
/** | |
Will be pre-pended to path upon state change | |
@property rootURL | |
@default '/' | |
*/ | |
rootURL: '/', | |
/** | |
@private | |
Returns the current `location.pathname` without rootURL | |
@method getURL | |
*/ | |
getURL: function() { | |
var rootURL = (Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri), | |
url = get(this, 'location').pathname; | |
rootURL = rootURL.replace(/\/$/, ''); | |
url = url.replace(rootURL, ''); | |
return url; | |
}, | |
/** | |
@private | |
Uses `history.pushState` to update the url without a page reload. | |
@method setURL | |
@param path {String} | |
*/ | |
setURL: function(path) { | |
var state = this.getState(); | |
path = this.formatURL(path); | |
if (state && state.path !== path) { | |
this.pushState(path); | |
} | |
}, | |
/** | |
@private | |
Uses `history.replaceState` to update the url without a page reload | |
or history modification. | |
@method replaceURL | |
@param path {String} | |
*/ | |
replaceURL: function(path) { | |
var state = this.getState(); | |
path = this.formatURL(path); | |
if (state && state.path !== path) { | |
this.replaceState(path); | |
} | |
}, | |
/** | |
@private | |
Get the current `history.state` | |
Polyfill checks for native browser support and falls back to retrieving | |
from a private _historyState variable | |
@method getState | |
*/ | |
getState: function() { | |
return supportsHistoryState ? get(this, 'history').state : this._historyState; | |
}, | |
/** | |
@private | |
Pushes a new state | |
@method pushState | |
@param path {String} | |
*/ | |
pushState: function(path) { | |
var state = { path: path }; | |
get(this, 'history').pushState(state, null, path); | |
// store state if browser doesn't support `history.state` | |
if (!supportsHistoryState) { | |
this._historyState = state; | |
} else { | |
} | |
// used for webkit workaround | |
this._previousURL = this.getURL(); | |
}, | |
/** | |
@private | |
Replaces the current state | |
@method replaceState | |
@param path {String} | |
*/ | |
replaceState: function(path) { | |
var state = { path: path }; | |
get(this, 'history').replaceState(state, null, path); | |
// store state if browser doesn't support `history.state` | |
if (!supportsHistoryState) { | |
this._historyState = state; | |
} else { | |
} | |
// used for webkit workaround | |
this._previousURL = this.getURL(); | |
}, | |
queryParamsString: function() { | |
var params = this.get('queryParams'); | |
if (Em.isEmpty(params) || Em.isEmpty(Object.keys(params))) { | |
return ""; | |
} else { | |
return "?" + decodeURIComponent($.param(params, true)); | |
} | |
}.property('queryParams'), | |
// When our query params change, update the URL | |
queryParamsStringChanged: function() { | |
var loc = this; | |
Em.run.next(function () { | |
loc.replaceState(loc.formatURL(loc.getURL())); | |
}); | |
}.observes('queryParamsString'), | |
/** | |
@private | |
Register a callback to be invoked whenever the browser | |
history changes, including using forward and back buttons. | |
@method onUpdateURL | |
@param callback {Function} | |
*/ | |
onUpdateURL: function(callback) { | |
var guid = Ember.guidFor(this), | |
self = this; | |
Ember.$(window).on('popstate.ember-location-'+guid, function() { | |
// Ignore initial page load popstate event in Chrome | |
if (!popstateFired) { | |
popstateFired = true; | |
if (self.getURL() === self._previousURL) { return; } | |
} | |
var url = self.getURL(); | |
popstateCallbacks.forEach(function(cb) { cb(url); }); | |
callback(url); | |
}); | |
}, | |
/** | |
@private | |
Used when using `{{action}}` helper. The url is always appended to the rootURL. | |
@method formatURL | |
@param url {String} | |
*/ | |
formatURL: function(url) { | |
var rootURL = get(this, 'rootURL'); | |
if (url !== '') { | |
rootURL = rootURL.replace(/\/$/, ''); | |
} | |
return rootURL + url + this.get('queryParamsString'); | |
}, | |
willDestroy: function() { | |
var guid = Ember.guidFor(this); | |
Ember.$(window).off('popstate.ember-location-'+guid); | |
} | |
}); | |
Ember.Location.registerImplementation('discourse_location', Ember.DiscourseLocation); | |
/** | |
Since we're using pushState/replaceState let's add extra hooks to cloakedView to | |
eject itself when the popState occurs. This results in better back button | |
behavior. | |
**/ | |
Ember.CloakedCollectionView.reopen({ | |
_watchForPopState: function() { | |
var self = this, | |
cb = function() { | |
self.cleanUp(); | |
self.set('controller.postStream.loaded', false); | |
}; | |
this.set('_callback', cb); | |
popstateCallbacks.addObject(cb); | |
}.on('didInsertElement'), | |
_disbandWatcher: function() { | |
popstateCallbacks.removeObject(this.get('_callback')); | |
this.set('_callback', null); | |
}.on('willDestroyElement') | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment