Browse files

BUG Don't double unescape URLs in history.js (fixes #8170)

Merged in pull request https://github.com/balupton/history.js/pull/108.
This has been a serious problem with the library for more than a year,
see https://github.com/balupton/history.js/issues/228.
  • Loading branch information...
1 parent dadd9e1 commit 64d3a3dafc8d7e3c3b218e9e20d9c0cdacd58043 @chillu chillu committed Jan 15, 2013
View
41 admin/thirdparty/history-js/scripts/uncompressed/history.html4.js
@@ -79,6 +79,19 @@
};
/**
+ * History.isHashEqual(newHash, oldHash)
+ * Checks to see if two hashes are functionally equal
+ * @param {string} newHash
+ * @param {string} oldHash
+ * @return {boolean} true
+ */
+ History.isHashEqual = function(newHash, oldHash){
+ newHash = encodeURIComponent(newHash).replace(/%25/g, "%");
+ oldHash = encodeURIComponent(oldHash).replace(/%25/g, "%");
+ return newHash === oldHash;
+ };
+
+ /**
* History.saveHash(newHash)
* Push a Hash
* @param {string} newHash
@@ -300,8 +313,9 @@
checkerRunning = true;
// Fetch
- var documentHash = History.getHash()||'',
- iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';
+ var
+ documentHash = History.getHash(),
+ iframeHash = History.getHash(iframe.contentWindow.document.location);
// The Document Hash has changed (application caused)
if ( documentHash !== lastDocumentHash ) {
@@ -398,7 +412,7 @@
//History.debug('History.onHashChange', arguments);
// Prepare
- var currentUrl = ((event && event.newURL) || document.location.href),
+ var currentUrl = ((event && event.newURL) || History.getLocationHref()),
currentHash = History.getHashByUrl(currentUrl),
currentState = null,
currentStateHash = null,
@@ -429,9 +443,10 @@
}
// Create State
+
// MODIFIED ischommer: URL normalization needs to respect our <base> tag,
// otherwise will go into infinite loops
- currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,true),true);
+ currentState = History.extractState(History.getFullUrl(currentHash||History.getLocationHref(),true),true);
// END MODIFIED
// Check if we are the same state
@@ -463,7 +478,7 @@
// Push the new HTML5 State
//History.debug('History.onHashChange: success hashchange');
- History.pushState(currentState.data,currentState.title,currentState.url,false);
+ History.pushState(currentState.data,currentState.title,encodeURI(currentState.url),false);
// End onHashChange closure
return true;
@@ -482,6 +497,11 @@
History.pushState = function(data,title,url,queue){
//History.debug('History.pushState: called', arguments);
+ // We assume that the URL passed in is URI-encoded, but this makes
+ // sure that it's fully URI encoded; any '%'s that are encoded are
+ // converted back into '%'s
+ url = encodeURI(url).replace(/%25/g, "%");
+
// Check the State
if ( History.getHashByUrl(url) ) {
throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
@@ -528,7 +548,7 @@
}
// Update HTML4 Hash
- if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
+ if ( !History.isHashEqual(newStateHash, html4Hash) && !History.isHashEqual(newStateHash, History.getShortUrl(History.getLocationHref())) ) {
//History.debug('History.pushState: update hash', newStateHash, html4Hash);
History.setHash(newStateHash,false);
return false;
@@ -558,9 +578,14 @@
History.replaceState = function(data,title,url,queue){
//History.debug('History.replaceState: called', arguments);
+ // We assume that the URL passed in is URI-encoded, but this makes
+ // sure that it's fully URI encoded; any '%'s that are encoded are
+ // converted back into '%'s
+ url = encodeURI(url).replace(/%25/g, "%");
+
// Check the State
if ( History.getHashByUrl(url) ) {
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
+ throw new Error('History.js does not support states with fragment-identifiers (hashes/anchors).');
}
// Handle Queueing
@@ -621,4 +646,4 @@
History.init();
}
-})(window);
+})(window);
View
98 admin/thirdparty/history-js/scripts/uncompressed/history.js
@@ -416,7 +416,7 @@
// Fetch
var
State = History.getState(false,false),
- stateUrl = (State||{}).url||document.location.href,
+ stateUrl = (State||{}).url||History.getLocationHref(),
pageUrl;
// Create
@@ -435,7 +435,7 @@
*/
History.getBasePageUrl = function(){
// Create
- var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
+ var basePageUrl = (History.getLocationHref()).replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
return (/[^\/]$/).test(part) ? '' : part;
}).replace(/\/+$/,'')+'/';
@@ -522,6 +522,36 @@
return shortUrl;
};
+ /**
+ * History.getLocationHref(document)
+ * Returns a normalized version of document.location.href
+ * accounting for browser inconsistencies, etc.
+ *
+ * This URL will be URI-encoded and will include the hash
+ *
+ * @param {object} document
+ * @return {string} url
+ */
+ History.getLocationHref = function(doc) {
+ doc = doc || document;
+
+ // most of the time, this will be true
+ if (doc.URL === doc.location.href)
+ return doc.location.href;
+
+ // some versions of webkit URI-decode document.location.href
+ // but they leave document.URL in an encoded state
+ if (doc.location.href === decodeURIComponent(doc.URL))
+ return doc.URL;
+
+ // FF 3.6 only updates document.URL when a page is reloaded
+ // document.location.href is updated correctly
+ if (doc.location.hash && decodeURIComponent(doc.location.href.replace(/^[^#]+/, "")) === doc.location.hash)
+ return doc.location.href;
+
+ return doc.URL || doc.location.href;
+ };
+
// ====================================================================
// State Storage
@@ -673,7 +703,7 @@
newState = {};
newState.normalized = true;
newState.title = oldState.title||'';
- newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
+ newState.url = History.getFullUrl(oldState.url?decodeURIComponent(oldState.url):(History.getLocationHref()));
newState.hash = History.getShortUrl(newState.url);
newState.data = History.cloneObject(oldState.data);
@@ -728,7 +758,7 @@
var State = {
'data': data,
'title': title,
- 'url': url
+ 'url': encodeURIComponent(url||"")
};
// Expand the State
@@ -1035,36 +1065,15 @@
/**
* History.getHash()
+ * @param {Location=} location
* Gets the current document hash
+ * Note: unlike location.hash, this is guaranteed to return the escaped hash in all browsers
* @return {string}
*/
- History.getHash = function(){
- var hash = History.unescapeHash(document.location.hash);
- return hash;
- };
-
- /**
- * History.unescapeString()
- * Unescape a string
- * @param {String} str
- * @return {string}
- */
- History.unescapeString = function(str){
- // Prepare
- var result = str,
- tmp;
-
- // Unescape hash
- while ( true ) {
- tmp = window.unescape(result);
- if ( tmp === result ) {
- break;
- }
- result = tmp;
- }
-
- // Return result
- return result;
+ History.getHash = function(location){
+ if ( !location ) location = document.location;
+ var href = location.href.replace( /^[^#]*/, "" );
+ return href.substr(1);
};
/**
@@ -1078,7 +1087,7 @@
var result = History.normalizeHash(hash);
// Unescape hash
- result = History.unescapeString(result);
+ result = decodeURIComponent(result);
// Return result
return result;
@@ -1105,7 +1114,7 @@
*/
History.setHash = function(hash,queue){
// Prepare
- var adjustedHash, State, pageUrl;
+ var State, pageUrl;
// Handle Queueing
if ( queue !== false && History.busy() ) {
@@ -1123,9 +1132,6 @@
// Log
//History.debug('History.setHash: called',hash);
- // Prepare
- adjustedHash = History.escapeHash(hash);
-
// Make Busy + Continue
History.busy(true);
@@ -1138,7 +1144,7 @@
// PushState
History.pushState(State.data,State.title,State.url,false);
}
- else if ( document.location.hash !== adjustedHash ) {
+ else if ( History.getHash() !== hash ) {
// Hash is a proper hash, so apply it
// Handle browser bugs
@@ -1149,11 +1155,11 @@
pageUrl = History.getPageUrl();
// Safari hash apply
- History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
+ History.pushState(null,null,pageUrl+'#'+hash,false);
}
else {
// Normal hash apply
- document.location.hash = adjustedHash;
+ document.location.hash = hash;
}
}
@@ -1171,7 +1177,7 @@
var result = History.normalizeHash(hash);
// Escape hash
- result = window.escape(result);
+ result = window.encodeURIComponent(result);
// IE6 Escape Bug
if ( !History.bugs.hashEscape ) {
@@ -1446,7 +1452,7 @@
// Get the Last State which has the new URL
var
- urlState = History.extractState(document.location.href),
+ urlState = History.extractState(History.getLocationHref()),
newState;
// Check for a difference
@@ -1617,7 +1623,7 @@
currentHash = History.getHash();
if ( currentHash ) {
// Expand Hash
- currentState = History.extractState(currentHash||document.location.href,true);
+ currentState = History.extractState(currentHash||History.getLocationHref(),true);
if ( currentState ) {
// We were able to parse it, it must be a State!
// Let's forward to replaceState
@@ -1650,13 +1656,13 @@
}
else {
// Initial State
- newState = History.extractState(document.location.href);
+ newState = History.extractState(History.getLocationHref());
}
// The State did not exist in our store
if ( !newState ) {
// Regenerate the State
- newState = History.createStateObject(null,null,document.location.href);
+ newState = History.createStateObject(null,null,History.getLocationHref());
}
// Clean
@@ -1836,7 +1842,7 @@
/**
* Create the initial State
*/
- History.saveState(History.storeState(History.extractState(document.location.href,true)));
+ History.saveState(History.storeState(History.extractState(History.getLocationHref(),true)));
/**
* Bind for Saving Store
@@ -1940,4 +1946,4 @@
// Try and Initialise History
History.init();
-})(window);
+})(window);

0 comments on commit 64d3a3d

Please sign in to comment.