diff --git a/src/ng/location.js b/src/ng/location.js
index aeb3a3f7ca9c..40ef256725c5 100644
--- a/src/ng/location.js
+++ b/src/ng/location.js
@@ -914,9 +914,10 @@ function $LocationProvider() {
var elm = jqLite(event.target);
- // traverse the DOM up to find first A tag
- while (nodeName_(elm[0]) !== 'a') {
- // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
+
+ // traverse the DOM up to find first A or AREA tag
+ while (nodeName_(elm[0]) !== 'a' && nodeName_(elm[0]) !== 'area') {
+ // ignore rewriting if no A or AREA tag (reached root element, or no parent - removed from document)
if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
}
diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js
index 5814b405b090..93e47bc2d2a2 100644
--- a/test/ng/locationSpec.js
+++ b/test/ng/locationSpec.js
@@ -1540,7 +1540,476 @@ describe('$location', function() {
});
- describe('link rewriting', function() {
+ describe('link () rewriting', function() {
+
+ var root, link, originalBrowser, lastEventPreventDefault;
+
+ function configureService(linkHref, html5Mode, supportHist, attrs, content) {
+ module(function($provide, $locationProvider) {
+ attrs = attrs ? ' ' + attrs + ' ' : '';
+
+ // fake the base behavior
+ if (linkHref[0] == '/') {
+ linkHref = 'http://host.com' + linkHref;
+ } else if(!linkHref.match(/:\/\//)) {
+ linkHref = 'http://host.com/base/' + linkHref;
+ }
+
+ link = jqLite('' + content + '')[0];
+
+ $provide.value('$sniffer', {history: supportHist});
+ $locationProvider.html5Mode(html5Mode);
+ $locationProvider.hashPrefix('!');
+ return function($rootElement, $document) {
+ $rootElement.append(link);
+ root = $rootElement[0];
+ // we need to do this otherwise we can't simulate events
+ $document.find('body').append($rootElement);
+ };
+ });
+ }
+
+ function initBrowser() {
+ return function($browser){
+ $browser.url('http://host.com/base');
+ $browser.$$baseHref = '/base/index.html';
+ };
+ }
+
+ function initLocation() {
+ return function($browser, $location, $rootElement) {
+ originalBrowser = $browser.url();
+ // we have to prevent the default operation, as we need to test absolute links (http://...)
+ // and navigating to these links would kill jstd
+ $rootElement.on('click', function(e) {
+ lastEventPreventDefault = e.isDefaultPrevented();
+ e.preventDefault();
+ });
+ };
+ }
+
+ function expectRewriteTo($browser, url) {
+ expect(lastEventPreventDefault).toBe(true);
+ expect($browser.url()).toBe(url);
+ }
+
+ function expectNoRewrite($browser) {
+ expect(lastEventPreventDefault).toBe(false);
+ expect($browser.url()).toBe(originalBrowser);
+ }
+
+ afterEach(function() {
+ dealoc(root);
+ dealoc(document.body);
+ });
+
+
+ it('should rewrite rel link to new url when history enabled on new browser', function() {
+ configureService('link?a#b', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/link?a#b');
+ }
+ );
+ });
+
+
+ it('should do nothing if already on the same URL', function() {
+ configureService('/base/', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/');
+
+ jqLite(link).attr('href', 'http://host.com/base/foo');
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/foo');
+
+ jqLite(link).attr('href', 'http://host.com/base/');
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/');
+
+ jqLite(link).
+ attr('href', 'http://host.com/base/foo').
+ on('click', function(e) { e.preventDefault(); });
+ browserTrigger(link, 'click');
+ expect($browser.url()).toBe('http://host.com/base/');
+ }
+ );
+ });
+
+
+ it('should rewrite abs link to new url when history enabled on new browser', function() {
+ configureService('/base/link?a#b', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/link?a#b');
+ }
+ );
+ });
+
+
+ it('should rewrite rel link to hashbang url when history enabled on old browser', function() {
+ configureService('link?a#b', true, false);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/index.html#!/link?a#b');
+ }
+ );
+ });
+
+
+ it('should rewrite abs link to hashbang url when history enabled on old browser', function() {
+ configureService('/base/link?a#b', true, false);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/index.html#!/link?a#b');
+ }
+ );
+ });
+
+
+ it('should not rewrite full url links do different domain', function() {
+ configureService('http://www.dot.abc/a?b=c', true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite links with target="_blank"', function() {
+ configureService('/a?b=c', true, true, 'target="_blank"');
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite links with target specified', function() {
+ configureService('/a?b=c', true, true, 'target="some-frame"');
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should rewrite full url links to same domain and base path', function() {
+ configureService('http://host.com/base/new', true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectRewriteTo($browser, 'http://host.com/base/index.html#!/new');
+ }
+ );
+ });
+
+
+ it('should not rewrite when link to different base path when history enabled on new browser',
+ function() {
+ configureService('/other_base/link', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite when link to different base path when history enabled on old browser',
+ function() {
+ configureService('/other_base/link', true, false);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite when link to different base path when history disabled', function() {
+ configureService('/other_base/link', false);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ 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);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ 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);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite when full link to different base path when history disabled', function() {
+ configureService('http://host.com/other_base/link', false);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click');
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ // don't run next tests on IE<9, as browserTrigger does not simulate pressed keys
+ if (!(msie < 9)) {
+
+ it('should not rewrite when clicked with ctrl pressed', function() {
+ configureService('/a?b=c', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click', ['ctrl']);
+ expectNoRewrite($browser);
+ }
+ );
+ });
+
+
+ it('should not rewrite when clicked with meta pressed', function() {
+ configureService('/a?b=c', true, true);
+ inject(
+ initBrowser(),
+ initLocation(),
+ function($browser) {
+ browserTrigger(link, 'click', ['meta']);
+ expectNoRewrite($browser);
+ }
+ );
+ });
+ }
+
+
+ it('should not mess up hash urls when clicking on links in hashbang mode', function() {
+ var base;
+ module(function() {
+ return function($browser) {
+ window.location.hash = 'someHash';
+ base = window.location.href
+ $browser.url(base);
+ base = base.split('#')[0];
+ }
+ });
+ inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
+ // we need to do this otherwise we can't simulate events
+ $document.find('body').append($rootElement);
+
+ var element = $compile('v1v2')($rootScope);
+ $rootElement.append(element);
+ var av1 = $rootElement.find('a').eq(0);
+ var av2 = $rootElement.find('a').eq(1);
+
+
+ browserTrigger(av1, 'click');
+ expect($browser.url()).toEqual(base + '#/view1');
+
+ browserTrigger(av2, 'click');
+ expect($browser.url()).toEqual(base + '#/view2');
+
+ $rootElement.remove();
+ });
+ });
+
+
+ it('should not mess up hash urls when clicking on links in hashbang mode with a prefix',
+ function() {
+ var base;
+ module(function($locationProvider) {
+ return function($browser) {
+ window.location.hash = '!someHash';
+ $browser.url(base = window.location.href);
+ base = base.split('#')[0];
+ $locationProvider.hashPrefix('!');
+ }
+ });
+ inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
+ // we need to do this otherwise we can't simulate events
+ $document.find('body').append($rootElement);
+
+ var element = $compile('v1v2')($rootScope);
+ $rootElement.append(element);
+ var av1 = $rootElement.find('a').eq(0);
+ var av2 = $rootElement.find('a').eq(1);
+
+
+ browserTrigger(av1, 'click');
+ expect($browser.url()).toEqual(base + '#!/view1');
+
+ browserTrigger(av2, 'click');
+ expect($browser.url()).toEqual(base + '#!/view2');
+ });
+ });
+
+
+ it('should not intercept clicks outside the current hash prefix', function() {
+ var base, clickHandler;
+ module(function($provide) {
+ $provide.value('$rootElement', {
+ on: function(event, handler) {
+ expect(event).toEqual('click');
+ clickHandler = handler;
+ },
+ off: noop
+ });
+ return function($browser) {
+ $browser.url(base = 'http://server/');
+ }
+ });
+ inject(function($location) {
+ // make IE happy
+ jqLite(window.document.body).html('link');
+
+ 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', {
+ on: function(event, handler) {
+ expect(event).toEqual('click');
+ clickHandler = handler;
+ },
+ off: angular.noop
+ });
+ return function($browser) {
+ $browser.url(base = 'http://server/');
+ }
+ });
+ inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
+ // make IE happy
+ jqLite(window.document.body).html('link');
+
+ var event = {
+ target: jqLite(window.document.body).find('a')[0],
+ preventDefault: jasmine.createSpy('preventDefault')
+ };
+
+
+ clickHandler(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+ });
+
+
+ // regression https://github.com/angular/angular.js/issues/1058
+ it('should not throw if element was removed', inject(function($document, $rootElement, $location) {
+ // we need to do this otherwise we can't simulate events
+ $document.find('body').append($rootElement);
+
+ $rootElement.html('');
+ var button = $rootElement.find('button');
+
+ button.on('click', function() {
+ button.remove();
+ });
+ browserTrigger(button, 'click');
+ }));
+
+
+ it('should not throw when clicking an SVGAElement link', function() {
+ var base;
+ module(function($locationProvider) {
+ return function($browser) {
+ window.location.hash = '!someHash';
+ $browser.url(base = window.location.href);
+ base = base.split('#')[0];
+ $locationProvider.hashPrefix('!');
+ }
+ });
+ inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
+ // we need to do this otherwise we can't simulate events
+ $document.find('body').append($rootElement);
+ var template = '';
+ var element = $compile(template)($rootScope);
+
+ $rootElement.append(element);
+ var av1 = $rootElement.find('a').eq(0);
+ expect(function() {
+ browserTrigger(av1, 'click');
+ }).not.toThrow();
+ });
+ });
+ });
+
+
+ describe('link () rewriting', function() {
var root, link, originalBrowser, lastEventPreventDefault;