diff --git a/src/ng/location.js b/src/ng/location.js index 7891d62f5..1efe5a8b4 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -127,21 +127,26 @@ function LocationHtml5Url(appBase, basePrefix) { this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' }; - this.$$rewrite = function(url) { + this.$$parseLinkUrl = function(url, relHref) { 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 +236,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 +261,21 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) { var appBaseNoFile = stripFile(appBase); - this.$$rewrite = function(url) { + this.$$parseLinkUrl = function(url, relHref) { + 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() { @@ -636,7 +648,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; @@ -655,6 +667,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 @@ -665,50 +680,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 tag linkHref = 'http://host.com/base/' + linkHref; } } @@ -888,8 +888,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'; }; } @@ -1003,12 +1003,14 @@ describe('$location', function() { it('should produce relative paths correctly when $location.path() is "/" when history enabled on old browser', function() { - configureService({linkHref: 'partial1', html5Mode: true, supportHist: false, relLink: 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'); } @@ -1203,52 +1205,22 @@ describe('$location', function() { ); }); - - it('should rewrite relative links relative to current path when history disabled', function() { - configureService({linkHref: 'link', html5Mode: true, supportHist: false, relLink: 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({linkHref: '/link', html5Mode: true, supportHist: false, relLink: 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 hash fragment when link begins with "#" history disabled', function() { - configureService({linkHref: '#link', html5Mode: true, supportHist: false, relLink: true}); + configureService({linkHref: '#link', html5Mode: true, supportHist: false, relLink: false}); inject( initBrowser(), initLocation(), - function($browser, $location) { - // Initialize browser URL - $location.path('/some'); - $location.hash('foo'); + function($browser, $location, $rootScope) { + $rootScope.$apply(function() { + $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/index.html#!#link'); } ); }); - // don't run next tests on IE<9, as browserTrigger does not simulate pressed keys if (!msie || msie >= 9) { @@ -1360,7 +1332,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) }; @@ -1390,7 +1363,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) }; @@ -1555,8 +1529,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); @@ -1613,6 +1591,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; @@ -1622,13 +1607,14 @@ 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'); + + 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'); }); }); @@ -1636,14 +1622,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() { @@ -1693,13 +1686,14 @@ 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'); + + 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'); }); }); });