Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix($location): fix URL interception in hash-bang mode
Browse files Browse the repository at this point in the history
Closes #1051
  • Loading branch information
mhevery authored and IgorMinar committed Jun 14, 2012
1 parent 0f44964 commit 6593a3e
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 34 deletions.
96 changes: 65 additions & 31 deletions src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ function encodePath(path) {
return segments.join('/');
}

function stripHash(url) {
return url.split('#')[0];
}


function matchUrl(url, obj) {
var match = URL_MATCH.exec(url);
Expand Down Expand Up @@ -102,19 +106,19 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
* @param {string} url HTML5 url
* @param {string} pathPrefix
*/
function LocationUrl(url, pathPrefix) {
function LocationUrl(url, pathPrefix, appBaseUrl) {
pathPrefix = pathPrefix || '';

/**
* Parse given html5 (regular) url string into properties
* @param {string} url HTML5 url
* @param {string} newAbsoluteUrl HTML5 url
* @private
*/
this.$$parse = function(url) {
var match = matchUrl(url, this);
this.$$parse = function(newAbsoluteUrl) {
var match = matchUrl(newAbsoluteUrl, this);

if (match.path.indexOf(pathPrefix) !== 0) {
throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !');
throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !');
}

this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length));
Expand All @@ -137,6 +141,14 @@ function LocationUrl(url, pathPrefix) {
pathPrefix + this.$$url;
};


this.$$rewriteAppUrl = function(absoluteLinkUrl) {
if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
return absoluteLinkUrl;
}
}


this.$$parse(url);
}

Expand All @@ -149,7 +161,7 @@ function LocationUrl(url, pathPrefix) {
* @param {string} url Legacy url
* @param {string} hashPrefix Prefix for hash part (containing path and search)
*/
function LocationHashbangUrl(url, hashPrefix) {
function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
var basePath;

/**
Expand Down Expand Up @@ -192,6 +204,13 @@ function LocationHashbangUrl(url, hashPrefix) {
basePath + (this.$$url ? '#' + hashPrefix + this.$$url : '');
};

this.$$rewriteAppUrl = function(absoluteLinkUrl) {
if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
return absoluteLinkUrl;
}
}


this.$$parse(url);
}

Expand Down Expand Up @@ -380,6 +399,19 @@ LocationUrl.prototype = {

LocationHashbangUrl.prototype = inherit(LocationUrl.prototype);

function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) {
LocationHashbangUrl.apply(this, arguments);


this.$$rewriteAppUrl = function(absoluteLinkUrl) {
if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length);
}
}
}

LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype);

function locationGetter(property) {
return function() {
return this[property];
Expand Down Expand Up @@ -479,26 +511,33 @@ function $LocationProvider(){
basePath,
pathPrefix,
initUrl = $browser.url(),
absUrlPrefix;
initUrlParts = matchUrl(initUrl),
appBaseUrl;

if (html5Mode) {
basePath = $browser.baseHref() || '/';
pathPrefix = pathPrefixFromBase(basePath);
appBaseUrl =
composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
pathPrefix + '/';

if ($sniffer.history) {
$location = new LocationUrl(
convertToHtml5Url(initUrl, basePath, hashPrefix),
pathPrefix);
pathPrefix, appBaseUrl);
} else {
$location = new LocationHashbangUrl(
$location = new LocationHashbangInHtml5Url(
convertToHashbangUrl(initUrl, basePath, hashPrefix),
hashPrefix);
hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1));
}
// link rewriting
absUrlPrefix = composeProtocolHostPort(
$location.protocol(), $location.host(), $location.port()) + pathPrefix;
} else {
$location = new LocationHashbangUrl(initUrl, hashPrefix);
absUrlPrefix = $location.absUrl().split('#')[0];
appBaseUrl =
composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
(initUrlParts.path || '') +
(initUrlParts.search ? ('?' + initUrlParts.search) : '') +
'#' + hashPrefix + '/';

$location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl);
}

$rootElement.bind('click', function(event) {
Expand All @@ -510,27 +549,22 @@ function $LocationProvider(){
var elm = jqLite(event.target);

// traverse the DOM up to find first A tag
while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
while (lowercase(elm[0].nodeName) !== 'a') {
if (elm[0] === $rootElement[0]) return;
elm = elm.parent();
}

var absHref = elm.prop('href'),
href;

if (!absHref ||
elm.attr('target') ||
absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path
return;
rewrittenUrl = $location.$$rewriteAppUrl(absHref);

if (absHref && !elm.attr('target') && rewrittenUrl) {
// update location manually
$location.$$parse(rewrittenUrl);
$rootScope.$apply();
event.preventDefault();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
}

// update location with href without the prefix
href = absHref.substr(absUrlPrefix.length);
if (href.indexOf('#' + hashPrefix) == 0) href = href.substr(hashPrefix.length + 1);
$location.url(href);
$rootScope.$apply();
event.preventDefault();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
});


Expand Down
2 changes: 1 addition & 1 deletion src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ angular.mock.$Browser = function() {
var self = this;

this.isMock = true;
self.$$url = "http://server";
self.$$url = "http://server/";
self.$$lastUrl = self.$$url; // used by url polling fn
self.pollFns = [];

Expand Down
62 changes: 60 additions & 2 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,64 @@ describe('$location', function() {
expect($browser.url()).toEqual(base + '#!/view2');
});
});


it('should not intercept link clicks outside the app base url space', function() {
var base, clickHandler;
module(function($provide) {
$provide.value('$rootElement', {
bind: function(event, handler) {
expect(event).toEqual('click');
clickHandler = handler;
}
});
return function($browser) {
$browser.url(base = 'http://server/');
}
});
inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
// make IE happy
jqLite(window.document.body).html('<a href="http://server/test.html">link</a>');

var event = {
target: jqLite(window.document.body).find('a')[0],
preventDefault: jasmine.createSpy('preventDefault')
};


clickHandler(event);
expect(event.preventDefault).not.toHaveBeenCalled();
});
});


it('should not intercept hash link clicks outside the app base url space', function() {
var base, clickHandler;
module(function($provide) {
$provide.value('$rootElement', {
bind: function(event, handler) {
expect(event).toEqual('click');
clickHandler = handler;
}
});
return function($browser) {
$browser.url(base = 'http://server/');
}
});
inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
// make IE happy
jqLite(window.document.body).html('<a href="http://server/index.html#test">link</a>');

var event = {
target: jqLite(window.document.body).find('a')[0],
preventDefault: jasmine.createSpy('preventDefault')
};


clickHandler(event);
expect(event.preventDefault).not.toHaveBeenCalled();
});
});
});


Expand Down Expand Up @@ -1111,7 +1169,7 @@ describe('$location', function() {
);


it('should listen on click events on href and prevent browser default in hasbang mode', function() {
it('should listen on click events on href and prevent browser default in hashbang mode', function() {
module(function() {
return function($rootElement, $compile, $rootScope) {
$rootElement.html('<a href="http://server/#/somePath">link</a>');
Expand Down Expand Up @@ -1162,7 +1220,7 @@ describe('$location', function() {
log += '$locationChangeStart';
});
$rootScope.$on('$locationChangeSuccess', function() {
throw new Error('after cancalation in html5 mode');
throw new Error('after cancelation in html5 mode');
});

browserTrigger(link, 'click');
Expand Down

0 comments on commit 6593a3e

Please sign in to comment.