From 8dc10a46fa3d126a1ecad74ebba85111080978e5 Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Wed, 17 Sep 2014 02:41:59 -0700 Subject: [PATCH 1/4] fix($location): respect absolute paths correctly Fixes absolute links on browsers with html5Mode enabled. Previously clicking on a link out of the base href would still rewrite the URL. --- src/ng/location.js | 48 +++++++++++++++------------ test/ng/locationSpec.js | 72 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/src/ng/location.js b/src/ng/location.js index 4fa37b440f67..50ab86aabd58 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -636,7 +636,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 +675,34 @@ 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; - } 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 Date: Wed, 17 Sep 2014 15:46:21 -0700 Subject: [PATCH 2/4] fix($location): respect base href --- src/ng/location.js | 7 ++++++- test/ng/locationSpec.js | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/ng/location.js b/src/ng/location.js index 50ab86aabd58..d42b87df5f9c 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -682,13 +682,18 @@ function $LocationProvider(){ var hrefNoBase = baseHrefNoFile ? href.substr(baseHrefNoFile.length - 1) : href; absHref = appBase + prefix + hrefNoBase; } else if (href[0] != '/') { // Ignore absolute path outside of base + var beginsWithPrefix = beginsWith(prefix, href); if (beginsWith(prefix + '/', href)) { // local anchor with absolute path absHref = appBase + href; - } else if (href[0] == '#') { + } else if (!beginsWithPrefix && href[0] == '#') { // local anchor absHref = appBase + prefix + ($location.path() || '/') + href; + } else if (!beginsWithPrefix && baseHref) { + // local anchor and base[href] exists + absHref = appBase + prefix + '/' + href; } else { + href = beginsWithPrefix ? href.substr(prefix.length) : href; // relative path - join with current path var stack = $location.path().split("/"), parts = href.split("/"); diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index fe37a054836b..f1d6cd26f6c2 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -879,14 +879,10 @@ describe('$location', function() { return function($browser){ if (atRoot) { $browser.url('http://host.com/'); - if (!noBase) { - $browser.$$baseHref = '/index.html'; - } + $browser.$$baseHref = noBase ? '' : '/index.html'; } else { $browser.url('http://host.com/base'); - if (!noBase) { - $browser.$$baseHref = '/base/index.html'; - } + $browser.$$baseHref = noBase ? '' : '/base/index.html'; } }; } @@ -1201,7 +1197,22 @@ 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'); + } + ); + }); + + + it('should rewrite relative links relative to base href when history enabled on old browser', function() { configureService('link', true, false, true); inject( initBrowser(), @@ -1209,7 +1220,7 @@ 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'); } ); }); @@ -1243,6 +1254,20 @@ describe('$location', function() { }); + it('should rewrite relative hashbang links 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#!/some/link'); + } + ); + }); + + it('should replace current path when link begins with "#!/" and history enabled on old browser', function() { configureService('#!/link', true, false, true); inject( From 5e5a45fb5d7de430ea967faff91dce8823dc0ff3 Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Wed, 17 Sep 2014 15:54:00 -0700 Subject: [PATCH 3/4] tests($location): make test name clearer --- test/ng/locationSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index f1d6cd26f6c2..d5eec949daf6 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -1309,7 +1309,7 @@ describe('$location', function() { }); - it('should replace current hash fragment when link begins with "#" history disabled', function() { + it('should replace current hash fragment when link begins with "#" and history enabled on old browser', function() { configureService('#link', true, false, true); inject( initBrowser(), From 7a276c30a80760e0bd0065ffe7bdb539c1aba0a9 Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Mon, 22 Sep 2014 23:47:42 -0700 Subject: [PATCH 4/4] fix($location): use url resolve shim --- src/ng/location.js | 105 +++++++++++++++++++++++++++------------- test/ng/locationSpec.js | 40 +++++++++++++-- 2 files changed, 108 insertions(+), 37 deletions(-) diff --git a/src/ng/location.js b/src/ng/location.js index d42b87df5f9c..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 + '/') { @@ -675,39 +677,22 @@ function $LocationProvider(){ if (href && href.indexOf('://') < 0) { // Ignore absolute URLs var prefix = '#' + hashPrefix; - var baseHrefNoFile = stripFile(baseHref); - var appOnRoot = stripFile(appBase) == (serverBase(appBase) + '/'); - if (href[0] == '/' && (appOnRoot || (baseHref && beginsWith(baseHrefNoFile, href)))) { - // absolute path - within base or when the app is on the root - var hrefNoBase = baseHrefNoFile ? href.substr(baseHrefNoFile.length - 1) : href; - absHref = appBase + prefix + hrefNoBase; - } else if (href[0] != '/') { // Ignore absolute path outside of base - var beginsWithPrefix = beginsWith(prefix, href); - if (beginsWith(prefix + '/', href)) { - // local anchor with absolute path - absHref = appBase + href; - } else if (!beginsWithPrefix && href[0] == '#') { - // local anchor - absHref = appBase + prefix + ($location.path() || '/') + href; - } else if (!beginsWithPrefix && baseHref) { - // local anchor and base[href] exists - absHref = appBase + prefix + '/' + href; - } else { - href = beginsWithPrefix ? href.substr(prefix.length) : href; - // 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 d5eec949daf6..e299ed1fe640 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -1203,10 +1203,15 @@ describe('$location', function() { 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/#!/some/link'); + expectRewriteTo($browser, 'http://host.com/#!/link'); } ); }); @@ -1254,15 +1259,42 @@ describe('$location', function() { }); - it('should rewrite relative hashbang links when history enabled on old browser', function() { - configureService('#!link', true, false, true); + 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(), 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 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'); } ); });