diff --git a/docs/content/error/$location/nobase.ngdoc b/docs/content/error/$location/nobase.ngdoc new file mode 100644 index 000000000000..b1a489019d30 --- /dev/null +++ b/docs/content/error/$location/nobase.ngdoc @@ -0,0 +1,51 @@ +@ngdoc error +@name $location:nobase +@fullName $location in HTML5 mode requires a tag to be present! +@description + +If you configure {@link ng.$location `$location`} to use +{@ng.provider.$locationProvider `html5Mode`} (`history.pushState`), you need to specify the base URL for the application with a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag. + +The base URL is then used to resolve all relative URLs throughout the application regardless of the +entry point into the app. + +If you are deploying your app into the root context (e.g. `https://myapp.com/`), set the base URL to `/`: + +```html + + + ... + +``` + +If you are deploying your app into a sub-context (e.g. `https://myapp.com/subapp/`), set the base URL to the +URL of the subcontext: + +```html + + + ... + +``` + +Before Angular 1.3 we didn't have this hard requirement and it was easy to write apps that worked +when deployed in the root context but were broken when moved to a sub-context because in the +sub-context all absolute urls would resolve to the root context of the app. To prevent this, +use relative URLs throughout your app: + +```html + +User Profile + + + +User Profile + +``` + +Additionally, if you want to support [browsers that don't have the `history.pushState` +API](http://caniuse.com/#feat=history), the fallback mechanism provided by `$location` +won't work well without specifying the base url of the application. + +In order to make it easier to migrate from hashbang mode to html5 mode, we require that the base +URL is always specified when `$location`'s `html5mode` is enabled. \ No newline at end of file diff --git a/docs/content/guide/$location.ngdoc b/docs/content/guide/$location.ngdoc index f7a1557a90f8..241bed03f2fe 100644 --- a/docs/content/guide/$location.ngdoc +++ b/docs/content/guide/$location.ngdoc @@ -325,20 +325,22 @@ to URLs that should be handled with `.`. Now, links to locations, which are not are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`. -### Server side +### Relative links -Using this mode requires URL rewriting on server side, basically you have to rewrite all your links -to entry point of your application (e.g. index.html) +Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url base in +the head of your main html file (``). With that, relative urls will +always be resolved to this base url, event if the initial url of the document was different. -### Relative links +There is one exception: Links that only contain a hash fragment (e.g. ``) +will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling +to anchors on the same page without needing to know on which page the user currently is. -Be sure to check all relative links, images, scripts etc. You must either specify the url base in -the head of your main html file (``) or you must use absolute urls -(starting with `/`) everywhere because relative urls will be resolved to absolute urls using the -initial absolute url of the document, which is often different from the root of the application. +### Server side -Running Angular apps with the History API enabled from document root is strongly encouraged as it -takes care of all relative link issues. +Using this mode requires URL rewriting on server side, basically you have to rewrite all your links +to entry point of your application (e.g. index.html). Requiring a `` tag is also important for +this case, as it allows Angular to differentiate between the part of the url that is the application +base and the path that should be handeled by the application. ### Sending links among different browsers diff --git a/src/ng/location.js b/src/ng/location.js index 98dec092dd26..2afd68818f60 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -127,21 +127,32 @@ function LocationHtml5Url(appBase, basePrefix) { this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' }; - this.$$rewrite = function(url) { + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } var appUrl, prevAppUrl; + var rewrittenUrl; if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { prevAppUrl = appUrl; if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { - return appBaseNoFile + (beginsWith('/', appUrl) || appUrl); + rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); } else { - return appBase + prevAppUrl; + rewrittenUrl = appBase + prevAppUrl; } } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { - return appBaseNoFile + appUrl; + rewrittenUrl = appBaseNoFile + appUrl; } else if (appBaseNoFile == url + '/') { - return appBaseNoFile; + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); } + return !!rewrittenUrl; }; } @@ -231,10 +242,12 @@ function LocationHashbangUrl(appBase, hashPrefix) { this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); }; - this.$$rewrite = function(url) { + this.$$parseLinkUrl = function(url, relHref) { if(stripHash(appBase) == stripHash(url)) { - return url; + this.$$parse(url); + return true; } + return false; }; } @@ -254,16 +267,28 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) { var appBaseNoFile = stripFile(appBase); - this.$$rewrite = function(url) { + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + + var rewrittenUrl; var appUrl; if ( appBase == stripHash(url) ) { - return url; + rewrittenUrl = url; } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { - return appBase + hashPrefix + appUrl; + rewrittenUrl = appBase + hashPrefix + appUrl; } else if ( appBaseNoFile === url + '/') { - return appBaseNoFile; + rewrittenUrl = appBaseNoFile; } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; }; this.$$compose = function() { @@ -626,6 +651,10 @@ function $LocationProvider(){ appBase; if (html5Mode) { + if (!baseHref) { + throw $locationMinErr('nobase', + "$location in HTML5 mode requires a tag to be present!"); + } appBase = serverBase(initialUrl) + (baseHref || '/'); LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; } else { @@ -633,7 +662,7 @@ function $LocationProvider(){ LocationMode = LocationHashbangUrl; } $location = new LocationMode(appBase, '#' + hashPrefix); - $location.$$parse($location.$$rewrite(initialUrl)); + $location.$$parseLinkUrl(initialUrl, initialUrl); var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; @@ -652,6 +681,9 @@ function $LocationProvider(){ } var absHref = elm.prop('href'); + // get the actual href attribute - see + // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx + var relHref = elm.attr('href') || elm.attr('xlink:href'); if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during @@ -662,50 +694,15 @@ function $LocationProvider(){ // Ignore when url is started with javascript: or mailto: if (IGNORE_URI_REGEXP.test(absHref)) return; - // Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9) - // The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or - // somewhere#anchor or http://example.com/somewhere - if (LocationMode === LocationHashbangInHtml5Url) { - // get the actual href attribute - see - // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx - var href = elm.attr('href') || elm.attr('xlink:href'); - - if (href && href.indexOf('://') < 0) { // Ignore absolute URLs - var prefix = '#' + hashPrefix; - if (href[0] == '/') { - // absolute path - replace old path - absHref = appBase + prefix + href; - } else if (href[0] == '#') { - // local anchor - absHref = appBase + prefix + ($location.path() || '/') + href; - } else { - // relative path - join with current path - var stack = $location.path().split("/"), - parts = href.split("/"); - if (stack.length === 2 && !stack[1]) stack.length = 1; - for (var i=0; i'); + $documentProvider.$get = function() { + return { + 0: window.document, + find: jasmine.createSpy('find').andReturn(baseElement) + }; + }; $browserProvider.$get = function($document, $window) { var sniffer = {history: true, hashchange: false}; var logs = {log:[], warn:[], info:[], error:[]}; @@ -93,6 +100,7 @@ describe('$location', function() { /* global Browser: false */ var b = new Browser($window, $document, fakeLog, sniffer); b.pollFns = []; + b.$$baseHref = '/'; return b; }; }); @@ -297,9 +305,9 @@ describe('$location', function() { }); it('should not rewrite when hashbang url is not given', function() { - initService(true, '!', true); + initService({html5Mode:true,hashPrefix: '!',supportHistory: true}); inject( - initBrowser('http://domain.com/base/a/b', '/base'), + initBrowser({url:'http://domain.com/base/a/b',basePath: '/base'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/a/b'); } @@ -532,24 +540,24 @@ describe('$location', function() { }); - function initService(html5Mode, hashPrefix, supportHistory) { + function initService(options) { return module(function($provide, $locationProvider){ - $locationProvider.html5Mode(html5Mode); - $locationProvider.hashPrefix(hashPrefix); - $provide.value('$sniffer', {history: supportHistory}); + $locationProvider.html5Mode(options.html5Mode); + $locationProvider.hashPrefix(options.hashPrefix); + $provide.value('$sniffer', {history: options.supportHistory}); }); } - function initBrowser(url, basePath) { + function initBrowser(options) { return function($browser){ - $browser.url(url); - $browser.$$baseHref = basePath; + $browser.url(options.url); + $browser.$$baseHref = options.basePath; }; } describe('wiring', function() { - beforeEach(initService(false, '!', true)); - beforeEach(inject(initBrowser('http://new.com/a/b#!', 'http://new.com/a/b'))); + beforeEach(initService({html5Mode:false,hashPrefix: '!',supportHistory: true})); + beforeEach(inject(initBrowser({url:'http://new.com/a/b#!',basePath: 'http://new.com/a/b'}))); it('should update $location when browser url changes', inject(function($browser, $location) { @@ -673,9 +681,9 @@ describe('$location', function() { describe('disabled history', function() { it('should use hashbang url with hash prefix', function() { - initService(false, '!'); + initService({html5Mode:false,hashPrefix: '!'}); inject( - initBrowser('http://domain.com/base/index.html#!/a/b', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html#!/a/b',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/index.html#!/a/b'); $location.path('/new'); @@ -688,9 +696,9 @@ describe('$location', function() { it('should use hashbang url without hash prefix', function() { - initService(false, ''); + initService({html5Mode:false,hashPrefix: ''}); inject( - initBrowser('http://domain.com/base/index.html#/a/b', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html#/a/b',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/index.html#/a/b'); $location.path('/new'); @@ -711,9 +719,9 @@ describe('$location', function() { })); it('should use hashbang url with hash prefix', function() { - initService(true, '!!', false); + initService({html5Mode:true,hashPrefix: '!!',supportHistory: false}); inject( - initBrowser('http://domain.com/base/index.html#!!/a/b', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html#!!/a/b',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/index.html#!!/a/b'); $location.path('/new'); @@ -726,9 +734,9 @@ describe('$location', function() { it('should redirect to hashbang url when new url given', function() { - initService(true, '!'); + initService({html5Mode:true,hashPrefix: '!'}); inject( - initBrowser('http://domain.com/base/new-path/index.html', '/base/index.html'), + initBrowser({url:'http://domain.com/base/new-path/index.html',basePath: '/base/index.html'}), function($browser, $location) { expect($browser.url()).toBe('http://domain.com/base/index.html#!/new-path/index.html'); } @@ -736,9 +744,9 @@ describe('$location', function() { }); it('should correctly convert html5 url with path matching basepath to hashbang url', function () { - initService(true, '!', false); + initService({html5Mode:true,hashPrefix: '!',supportHistory: false}); inject( - initBrowser('http://domain.com/base/index.html', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html',basePath: '/base/index.html'}), function($browser, $location) { expect($browser.url()).toBe('http://domain.com/base/index.html#!/index.html'); } @@ -755,9 +763,9 @@ describe('$location', function() { })); it('should use new url', function() { - initService(true, '', true); + initService({html5Mode:true,hashPrefix: '',supportHistory: true}); inject( - initBrowser('http://domain.com/base/old/index.html#a', '/base/index.html'), + initBrowser({url:'http://domain.com/base/old/index.html#a',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/old/index.html#a'); $location.path('/new'); @@ -770,9 +778,9 @@ describe('$location', function() { it('should rewrite when hashbang url given', function() { - initService(true, '!', true); + initService({html5Mode:true,hashPrefix: '!',supportHistory: true}); inject( - initBrowser('http://domain.com/base/index.html#!/a/b', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html#!/a/b',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/a/b'); $location.path('/new'); @@ -786,9 +794,9 @@ describe('$location', function() { it('should rewrite when hashbang url given (without hash prefix)', function() { - initService(true, '', true); + initService({html5Mode:true,hashPrefix: '',supportHistory: true}); inject( - initBrowser('http://domain.com/base/index.html#/a/b', '/base/index.html'), + initBrowser({url:'http://domain.com/base/index.html#/a/b',basePath: '/base/index.html'}), function($rootScope, $location, $browser) { expect($browser.url()).toBe('http://domain.com/base/a/b'); expect($location.path()).toBe('/a/b'); @@ -796,18 +804,6 @@ describe('$location', function() { ); }); - - it('should set appBase to serverBase if base[href] is missing', function() { - initService(true, '!', true); - inject( - initBrowser('http://domain.com/my/view1#anchor1', ''), - function($rootScope, $location, $browser) { - expect($browser.url()).toBe('http://domain.com/my/view1#anchor1'); - expect($location.path()).toBe('/my/view1'); - expect($location.hash()).toBe('anchor1'); - } - ); - }); }); describe('PATH_MATCH', function() { @@ -843,21 +839,23 @@ describe('$location', function() { var root, link, originalBrowser, lastEventPreventDefault; - function configureService(linkHref, html5Mode, supportHist, relLink, attrs, content) { - if (typeof relLink !== "boolean") { - content = attrs; - attrs = relLink; - relLink = false; - } + function configureService(options) { + var linkHref = options.linkHref, + html5Mode = options.html5Mode, + supportHist = options.supportHist, + relLink = options.relLink, + attrs = options.attrs, + content = options.content; + module(function($provide, $locationProvider) { attrs = attrs ? ' ' + attrs + ' ' : ''; - // fake the base behavior if (typeof linkHref === 'string') { if (!relLink) { if (linkHref[0] == '/') { linkHref = 'http://host.com' + linkHref; } else if(!linkHref.match(/:\/\//)) { + // fake the behavior of tag linkHref = 'http://host.com/base/' + linkHref; } } @@ -882,8 +880,8 @@ describe('$location', function() { } function initBrowser() { - return function($browser){ - $browser.url('http://host.com/base'); + return function($browser, $document){ + $browser.url('http://host.com/base/index.html'); $browser.$$baseHref = '/base/index.html'; }; } @@ -917,7 +915,7 @@ describe('$location', function() { it('should rewrite rel link to new url when history enabled on new browser', function() { - configureService('link?a#b', true, true); + configureService({linkHref: 'link?a#b', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -930,7 +928,7 @@ describe('$location', function() { it('should do nothing if already on the same URL', function() { - configureService('/base/', true, true); + configureService({linkHref: '/base/', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -957,7 +955,7 @@ describe('$location', function() { it('should rewrite abs link to new url when history enabled on new browser', function() { - configureService('/base/link?a#b', true, true); + configureService({linkHref: '/base/link?a#b', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -970,7 +968,7 @@ describe('$location', function() { it('should rewrite rel link to hashbang url when history enabled on old browser', function() { - configureService('link?a#b', true, false); + configureService({linkHref: 'link?a#b', html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), @@ -984,7 +982,7 @@ describe('$location', function() { // Regression (gh-7721) it('should not throw when clicking anchor with no href attribute when history enabled on old browser', function() { - configureService(null, true, false); + configureService({linkHref: null, html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), @@ -997,12 +995,14 @@ describe('$location', function() { it('should produce relative paths correctly when $location.path() is "/" when history enabled on old browser', function() { - configureService('partial1', true, false, true); + configureService({linkHref: 'partial1', html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), - function($browser, $location) { - $location.path('/'); + function($browser, $location, $rootScope) { + $rootScope.$apply(function() { + $location.path('/'); + }); browserTrigger(link, 'click'); expectRewriteTo($browser, 'http://host.com/base/index.html#!/partial1'); } @@ -1011,7 +1011,7 @@ describe('$location', function() { it('should rewrite abs link to hashbang url when history enabled on old browser', function() { - configureService('/base/link?a#b', true, false); + configureService({linkHref: '/base/link?a#b', html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), @@ -1024,7 +1024,7 @@ describe('$location', function() { it('should not rewrite full url links do different domain', function() { - configureService('http://www.dot.abc/a?b=c', true); + configureService({linkHref: 'http://www.dot.abc/a?b=c', html5Mode: true}); inject( initBrowser(), initLocation(), @@ -1037,7 +1037,7 @@ describe('$location', function() { it('should not rewrite links with target="_blank"', function() { - configureService('/a?b=c', true, true, 'target="_blank"'); + configureService({linkHref: '/a?b=c', html5Mode: true, supportHist: true, attrs: 'target="_blank"'}); inject( initBrowser(), initLocation(), @@ -1050,7 +1050,7 @@ describe('$location', function() { it('should not rewrite links with target specified', function() { - configureService('/a?b=c', true, true, 'target="some-frame"'); + configureService({linkHref: '/a?b=c', html5Mode: true, supportHist: true, attrs: 'target="some-frame"'}); inject( initBrowser(), initLocation(), @@ -1063,7 +1063,7 @@ describe('$location', function() { it('should not rewrite links with `javascript:` URI', function() { - configureService(' jAvAsCrIpT:throw new Error("Boom!")', true, true, true); + configureService({linkHref: ' jAvAsCrIpT:throw new Error("Boom!")', html5Mode: true, supportHist: true, relLink: true}); inject( initBrowser(), initLocation(), @@ -1076,7 +1076,7 @@ describe('$location', function() { it('should not rewrite links with `mailto:` URI', function() { - configureService(' mAiLtO:foo@bar.com', true, true, true); + configureService({linkHref: ' mAiLtO:foo@bar.com', html5Mode: true, supportHist: true, relLink: true}); inject( initBrowser(), initLocation(), @@ -1089,7 +1089,7 @@ describe('$location', function() { it('should rewrite full url links to same domain and base path', function() { - configureService('http://host.com/base/new', true); + configureService({linkHref: 'http://host.com/base/new', html5Mode: true}); inject( initBrowser(), initLocation(), @@ -1102,7 +1102,7 @@ describe('$location', function() { it('should rewrite when clicked span inside link', function() { - configureService('some/link', true, true, '', 'link'); + configureService({linkHref: 'some/link', html5Mode: true, supportHist: true, attrs: '', content: 'link'}); inject( initBrowser(), initLocation(), @@ -1118,7 +1118,7 @@ describe('$location', function() { it('should not rewrite when link to different base path when history enabled on new browser', function() { - configureService('/other_base/link', true, true); + configureService({linkHref: '/other_base/link', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -1132,7 +1132,7 @@ describe('$location', function() { it('should not rewrite when link to different base path when history enabled on old browser', function() { - configureService('/other_base/link', true, false); + configureService({linkHref: '/other_base/link', html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), @@ -1145,7 +1145,7 @@ describe('$location', function() { it('should not rewrite when link to different base path when history disabled', function() { - configureService('/other_base/link', false); + configureService({linkHref: '/other_base/link', html5Mode: false}); inject( initBrowser(), initLocation(), @@ -1159,7 +1159,7 @@ describe('$location', function() { it('should not rewrite when full link to different base path when history enabled on new browser', function() { - configureService('http://host.com/other_base/link', true, true); + configureService({linkHref: 'http://host.com/other_base/link', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -1173,7 +1173,7 @@ describe('$location', function() { it('should not rewrite when full link to different base path when history enabled on old browser', function() { - configureService('http://host.com/other_base/link', true, false); + configureService({linkHref: 'http://host.com/other_base/link', html5Mode: true, supportHist: false}); inject( initBrowser(), initLocation(), @@ -1186,7 +1186,7 @@ describe('$location', function() { it('should not rewrite when full link to different base path when history disabled', function() { - configureService('http://host.com/other_base/link', false); + configureService({linkHref: 'http://host.com/other_base/link', html5Mode: false}); inject( initBrowser(), initLocation(), @@ -1197,57 +1197,45 @@ describe('$location', function() { ); }); - - it('should rewrite relative links relative to current path when history disabled', function() { - configureService('link', true, false, true); - inject( - initBrowser(), - initLocation(), - function($browser, $location) { - $location.path('/some'); - browserTrigger(link, 'click'); - expectRewriteTo($browser, 'http://host.com/base/index.html#!/some/link'); - } - ); - }); - - - it('should replace current path when link begins with "/" and history disabled', function() { - configureService('/link', true, false, true); + it('should replace current hash fragment when link begins with "#" history disabled', function() { + configureService({linkHref: '#link', html5Mode: true, supportHist: false, relLink: true}); inject( initBrowser(), initLocation(), - function($browser, $location) { - $location.path('/some'); + function($browser, $location, $rootScope) { + $rootScope.$apply(function() { + $location.path('/some'); + $location.hash('foo'); + }); browserTrigger(link, 'click'); - expectRewriteTo($browser, 'http://host.com/base/index.html#!/link'); + expect($location.hash()).toBe('link'); + expectRewriteTo($browser, 'http://host.com/base/index.html#!/some#link'); } ); }); - - it('should replace current hash fragment when link begins with "#" history disabled', function() { - configureService('#link', true, false, true); + it('should replace current hash fragment when link begins with "#" history enabled', function() { + configureService({linkHref: '#link', html5Mode: true, supportHist: true, relLink: true}); inject( initBrowser(), initLocation(), - function($browser, $location) { - // Initialize browser URL - $location.path('/some'); - $location.hash('foo'); + function($browser, $location, $rootScope) { + $rootScope.$apply(function() { + $location.path('/some'); + $location.hash('foo'); + }); browserTrigger(link, 'click'); expect($location.hash()).toBe('link'); - expectRewriteTo($browser, 'http://host.com/base/index.html#!/some#link'); + expectRewriteTo($browser, 'http://host.com/base/some#link'); } ); }); - // don't run next tests on IE<9, as browserTrigger does not simulate pressed keys if (!msie || msie >= 9) { it('should not rewrite when clicked with ctrl pressed', function() { - configureService('/a?b=c', true, true); + configureService({linkHref: '/a?b=c', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -1260,7 +1248,7 @@ describe('$location', function() { it('should not rewrite when clicked with meta pressed', function() { - configureService('/a?b=c', true, true); + configureService({linkHref: '/a?b=c', html5Mode: true, supportHist: true}); inject( initBrowser(), initLocation(), @@ -1354,7 +1342,8 @@ describe('$location', function() { var event = { target: jqLite(window.document.body).find('a')[0], - preventDefault: jasmine.createSpy('preventDefault') + preventDefault: jasmine.createSpy('preventDefault'), + isDefaultPrevented: jasmine.createSpy().andReturn(false) }; @@ -1384,7 +1373,8 @@ describe('$location', function() { var event = { target: jqLite(window.document.body).find('a')[0], - preventDefault: jasmine.createSpy('preventDefault') + preventDefault: jasmine.createSpy('preventDefault'), + isDefaultPrevented: jasmine.createSpy().andReturn(false) }; @@ -1549,8 +1539,12 @@ describe('$location', function() { it('should listen on click events on href and prevent browser default in html5 mode', function() { - module(function($locationProvider) { + module(function($locationProvider, $provide) { $locationProvider.html5Mode(true); + $provide.decorator('$browser', function($delegate) { + $delegate.$$baseHref = '/'; + return $delegate; + }); return function($rootElement, $compile, $rootScope) { $rootElement.html('link'); $compile($rootElement)($rootScope); @@ -1607,6 +1601,13 @@ describe('$location', function() { ); }); + function parseLinkAndReturn(location, url, relHref) { + if (location.$$parseLinkUrl(url, relHref)) { + return location.absUrl(); + } + return undefined; + } + describe('LocationHtml5Url', function() { var location, locationIndex; @@ -1616,13 +1617,18 @@ describe('$location', function() { }); it('should rewrite URL', function() { - expect(location.$$rewrite('http://other')).toEqual(undefined); - expect(location.$$rewrite('http://server/pre')).toEqual('http://server/pre/'); - expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/'); - expect(location.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/otherPath'); - expect(locationIndex.$$rewrite('http://server/pre')).toEqual('http://server/pre/'); - expect(locationIndex.$$rewrite('http://server/pre/')).toEqual('http://server/pre/'); - expect(locationIndex.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/otherPath'); + expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined); + expect(parseLinkAndReturn(location, 'http://server/pre')).toEqual('http://server/pre/'); + expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/'); + expect(parseLinkAndReturn(location, 'http://server/pre/otherPath')).toEqual('http://server/pre/otherPath'); + // Note: relies on the previous state! + expect(parseLinkAndReturn(location, 'someIgnoredAbsoluteHref', '#test')).toEqual('http://server/pre/otherPath#test'); + + expect(parseLinkAndReturn(locationIndex, 'http://server/pre')).toEqual('http://server/pre/'); + expect(parseLinkAndReturn(locationIndex, 'http://server/pre/')).toEqual('http://server/pre/'); + expect(parseLinkAndReturn(locationIndex, 'http://server/pre/otherPath')).toEqual('http://server/pre/otherPath'); + // Note: relies on the previous state! + expect(parseLinkAndReturn(location, 'someIgnoredAbsoluteHref', '#test')).toEqual('http://server/pre/otherPath#test'); }); }); @@ -1630,14 +1636,21 @@ describe('$location', function() { describe('LocationHashbangUrl', function() { var location; + function parseLinkAndReturn(location, url, relHref) { + if (location.$$parseLinkUrl(url, relHref)) { + return location.absUrl(); + } + return undefined; + } + it('should rewrite URL', function() { /* jshint scripturl: true */ location = new LocationHashbangUrl('http://server/pre/', '#'); - expect(location.$$rewrite('http://other')).toEqual(undefined); - expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/'); - expect(location.$$rewrite('http://server/pre/#otherPath')).toEqual('http://server/pre/#otherPath'); - expect(location.$$rewrite('javascript:void(0)')).toEqual(undefined); + expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined); + expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/'); + expect(parseLinkAndReturn(location, 'http://server/pre/#otherPath')).toEqual('http://server/pre/#/otherPath'); + expect(parseLinkAndReturn(location, 'javascript:void(0)')).toEqual(undefined); }); it("should not set hash if one was not originally specified", function() { @@ -1687,13 +1700,18 @@ describe('$location', function() { }); it('should rewrite URL', function() { - expect(location.$$rewrite('http://other')).toEqual(undefined); - expect(location.$$rewrite('http://server/pre')).toEqual('http://server/pre/'); - expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/'); - expect(location.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/#!otherPath'); - expect(locationIndex.$$rewrite('http://server/pre')).toEqual('http://server/pre/'); - expect(locationIndex.$$rewrite('http://server/pre/')).toEqual(undefined); - expect(locationIndex.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/index.html#!otherPath'); + expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined); + expect(parseLinkAndReturn(location, 'http://server/pre')).toEqual('http://server/pre/#!'); + expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/#!'); + expect(parseLinkAndReturn(location, 'http://server/pre/otherPath')).toEqual('http://server/pre/#!/otherPath'); + // Note: relies on the previous state! + expect(parseLinkAndReturn(location, 'someIgnoredAbsoluteHref', '#test')).toEqual('http://server/pre/#!/otherPath#test'); + + expect(parseLinkAndReturn(locationIndex, 'http://server/pre')).toEqual('http://server/pre/index.html#!'); + expect(parseLinkAndReturn(locationIndex, 'http://server/pre/')).toEqual(undefined); + expect(parseLinkAndReturn(locationIndex, 'http://server/pre/otherPath')).toEqual('http://server/pre/index.html#!/otherPath'); + // Note: relies on the previous state! + expect(parseLinkAndReturn(locationIndex, 'someIgnoredAbsoluteHref', '#test')).toEqual('http://server/pre/index.html#!/otherPath#test'); }); }); });