diff --git a/src/ng/location.js b/src/ng/location.js index 4fa37b440f67..80e4ba154653 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -259,6 +259,8 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) { if ( appBase == stripHash(url) ) { return url; + } else if ( (appUrl = beginsWith(appBaseNoFile + hashPrefix + '/', url)) ) { + return appBase + hashPrefix + appUrl; } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { return appBase + hashPrefix + appUrl; } else if ( appBaseNoFile === url + '/') { @@ -636,7 +638,7 @@ function $LocationProvider(){ LocationMode = LocationHashbangUrl; } $location = new LocationMode(appBase, '#' + hashPrefix); - $location.$$parse($location.$$rewrite(initialUrl)); + $location.$$parse($location.$$rewrite(initialUrl) || appBase); var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; @@ -675,26 +677,22 @@ function $LocationProvider(){ if (href && href.indexOf('://') < 0) { // Ignore absolute URLs var prefix = '#' + hashPrefix; - if (href[0] == '/') { - // absolute path - replace old path - absHref = appBase + prefix + href; + var appBaseNoFile = stripFile(appBase); + var html5Url = appBase + $location.url().substr(1); + var baseUri = baseHref ? urlResolveShim(baseHref, appBase) : html5Url; + var appUrl; + if (appUrl = beginsWith(prefix + '/', href)) { + // hashbang beginning with / refers to appbase + absHref = urlResolveShim(appBaseNoFile + appUrl); + } else if (appUrl = beginsWith(prefix, href)) { + // hashbang relative path + absHref = urlResolveShim(appUrl, baseUri); } 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= 0) { + absHref = url; + } else if (url.charAt(0) === '/') { + absHref = serverBase(base) + url; + } else { + absHref = stripFile(base) + url; + } + + var resolvedUrl = urlResolve(absHref); + var hash = resolvedUrl.hash ? '#' + resolvedUrl.hash : ''; + var search = resolvedUrl.search; + var path = resolvedUrl.pathname; + var normalizedPath = normalizePath(path); // only for IE7 compatibility + return serverBase(resolvedUrl.href) + normalizedPath + (search ? '?' + search : '') + hash; + } + + function normalizePath(path) { + path = path || ''; + var inputSegments = path.split('/'); + var outputSegments = []; + var inputSegment; + for (var i = 0; i < inputSegments.length; i++) { + inputSegment = inputSegments[i]; + + if ((inputSegment.length === 0) + || (inputSegment == '.')) { + // Do nothing + continue; + } else if (inputSegment == '..') { + if (outputSegments.length) { + outputSegments.pop(); + } + } else { + outputSegments.push(inputSegment); + } + } + + var outputSegment, output = ''; + for (i = 0; i < outputSegments.length; i++) { + outputSegment = outputSegments[i]; + output += '/' + outputSegment; + } + + if (path.lastIndexOf('/') == path.length - 1) { + // path.endsWith("/") || path.equals("") + output += '/'; + } + + return output; + } }]; } diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index b7c656c78e7d..e299ed1fe640 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -875,10 +875,15 @@ describe('$location', function() { }); } - function initBrowser() { + function initBrowser(atRoot, noBase) { return function($browser){ - $browser.url('http://host.com/base'); - $browser.$$baseHref = '/base/index.html'; + if (atRoot) { + $browser.url('http://host.com/'); + $browser.$$baseHref = noBase ? '' : '/index.html'; + } else { + $browser.url('http://host.com/base'); + $browser.$$baseHref = noBase ? '' : '/base/index.html'; + } }; } @@ -1192,7 +1197,27 @@ describe('$location', function() { }); - it('should rewrite relative links relative to current path when history disabled', function() { + it('should rewrite relative links relative to current path when no base and history enabled on old browser', function() { + configureService('link', true, false, true); + inject( + initBrowser(false, true), + initLocation(), + function($browser, $location) { + $location.path('/some/'); + expect($browser.url(), 'http://host.com/#!/some/'); + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/#!/some/link'); + + $location.path('/some'); + expect($browser.url(), 'http://host.com/#!/some'); + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/#!/link'); + } + ); + }); + + + it('should rewrite relative links relative to base href when history enabled on old browser', function() { configureService('link', true, false, true); inject( initBrowser(), @@ -1200,14 +1225,55 @@ describe('$location', function() { function($browser, $location) { $location.path('/some'); browserTrigger(link, 'click'); - expectRewriteTo($browser, 'http://host.com/base/index.html#!/some/link'); + expectRewriteTo($browser, 'http://host.com/base/index.html#!/link'); } ); }); - it('should replace current path when link begins with "/" and history disabled', function() { + it('should replace current path when link begins with "/" and app is on root and history enabled on old browser', function() { configureService('/link', true, false, true); + inject( + initBrowser(true), + initLocation(), + function($browser, $location) { + $location.path('/some'); + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/index.html#!/link'); + } + ); + }); + + + it('should replace current path when relative link begins with "/base/" and history enabled on old browser', function() { + configureService('/base/link', true, false, true); + inject( + initBrowser(), + initLocation(), + function($browser, $location) { + $location.path('/some'); + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/base/index.html#!/link'); + } + ); + }); + + + it('should replace current path when relative link leads to base and history enabled on old browser', function() { + configureService('../base/link', true, false, true); + inject( + initBrowser(), + initLocation(), + function($browser, $location) { + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/base/index.html#!/link'); + } + ); + }); + + + it('should replace current path when relative link begins with "/base/" and history enabled on old browser', function() { + configureService('/base/#!/link', true, false, true); inject( initBrowser(), initLocation(), @@ -1220,7 +1286,62 @@ describe('$location', function() { }); - it('should replace current hash fragment when link begins with "#" history disabled', function() { + it('should rewrite relative hashbang links with respect to base when history enabled on old browser', 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#!/link'); + } + ); + }); + + + it('should replace current path when link begins with "#!/" and history enabled on old browser', 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#!/link'); + } + ); + }); + + + it('should rewrite when relative link begins with "/" and app is on the root and there is no base tag and history enabled on old browser', function() { + configureService('/link', true, false, true); + inject( + initBrowser(true, true), + initLocation(), + function($browser, $location) { + browserTrigger(link, 'click'); + expectRewriteTo($browser, 'http://host.com/#!/link'); + } + ); + }); + + + it('should not rewrite when relative link begins with "/" and history enabled on old browser', function() { + configureService('/other_base/link', true, false, true); + inject( + initBrowser(), + initLocation(), + function($browser, $location) { + $location.path('/some'); + browserTrigger(link, 'click'); + expectNoRewrite($browser); + } + ); + }); + + + it('should replace current hash fragment when link begins with "#" and history enabled on old browser', function() { configureService('#link', true, false, true); inject( initBrowser(),