diff --git a/src/ng/browser.js b/src/ng/browser.js index e87e5e184e93..b149ee66f4f3 100644 --- a/src/ng/browser.js +++ b/src/ng/browser.js @@ -62,6 +62,11 @@ function Browser(window, document, $log, $sniffer) { } } + function getHash(url) { + var index = url.indexOf('#'); + return index === -1 ? '' : url.substr(index + 1); + } + /** * @private * Note: this method is used only by scenario runner @@ -173,8 +178,10 @@ function Browser(window, document, $log, $sniffer) { } if (replace) { location.replace(url); - } else { + } else if (!sameBase) { location.href = url; + } else { + location.hash = getHash(url); } } return self; diff --git a/test/ng/browserSpecs.js b/test/ng/browserSpecs.js index 62b0a80c5298..b6200cb2c296 100755 --- a/test/ng/browserSpecs.js +++ b/test/ng/browserSpecs.js @@ -1,8 +1,18 @@ 'use strict'; -function MockWindow() { +/* global getHash:true, stripHash:true */ + +var historyEntriesLength; +var sniffer = {}; + +function MockWindow(options) { + if (typeof options !== 'object') { + options = {}; + } var events = {}; var timeouts = this.timeouts = []; + var locationHref = 'http://server/'; + var mockWindow = this; this.setTimeout = function(fn) { return timeouts.push(fn) - 1; @@ -35,10 +45,37 @@ function MockWindow() { }); }; - this.location = { - href: 'http://server/', - replace: noop + //IE8 hack. defineProperty doesn't work with POJS, just with certain DOM elements + this.location = document.createElement('div'); + this.location.href = {}; + this.location.hash = {}; + this.location.replace = function(url) { + locationHref = url; + mockWindow.history.state = null; }; + Object.defineProperty(this.location, 'href', { + enumerable: false, + configurable: true, + set: function(value) { + locationHref = value; + mockWindow.history.state = null; + historyEntriesLength++; + }, + get: function() { + return locationHref; + } + }); + + Object.defineProperty(this.location, 'hash', { + enumerable: false, + configurable: true, + set: function(value) { + locationHref = stripHash(locationHref) + '#' + value; + }, + get: function() { + return getHash(locationHref); + } + }); this.history = { replaceState: noop, @@ -89,7 +126,6 @@ describe('browser', function() { warn: function() { logs.warn.push(slice.call(arguments)); }, info: function() { logs.info.push(slice.call(arguments)); }, error: function() { logs.error.push(slice.call(arguments)); }}; - browser = new Browser(fakeWindow, fakeDocument, fakeLog, sniffer); }); @@ -451,6 +487,17 @@ describe('browser', function() { expect(locationReplace).not.toHaveBeenCalled(); }); + it("should retain the # character when the only change is clearing the hash fragment, to prevent page reload", function() { + sniffer.history = true; + + browser.url('http://server/#123'); + expect(fakeWindow.location.href).toEqual('http://server/#123'); + + browser.url('http://server/'); + expect(fakeWindow.location.href).toEqual('http://server/#'); + + }); + it('should use location.replace when history.replaceState not available', function() { sniffer.history = false; browser.url('http://new.org', true); @@ -462,6 +509,7 @@ describe('browser', function() { expect(fakeWindow.location.href).toEqual('http://server/'); }); + it('should use location.replace and not use replaceState when the url only changed in the hash fragment to please IE10/11', function() { sniffer.history = true; browser.url('http://server/#123', true); @@ -473,6 +521,7 @@ describe('browser', function() { expect(fakeWindow.location.href).toEqual('http://server/'); }); + it('should return $browser to allow chaining', function() { expect(browser.url('http://any.com')).toBe(browser); }); @@ -835,7 +884,6 @@ describe('browser', function() { expect(browser.url()).toBe(newUrl); $rootScope.$digest(); expect(browser.url()).toBe(newUrl); - expect(fakeWindow.location.href).toBe(current); }); });