From 92a2e1807657c69e1372106b0727675a30f4cbd7 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Tue, 22 May 2012 16:45:56 -0700 Subject: feat($location): add $locatonChange[begin|completed] event This allows location change cancelation --- src/ng/location.js | 110 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 43 deletions(-) (limited to 'src/ng/location.js') diff --git a/src/ng/location.js b/src/ng/location.js index c7b41605..64a234ad 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -408,7 +408,10 @@ function locationGetterSetter(property, preprocess) { * @requires $rootElement * * @description - * The $location service parses the URL in the browser address bar (based on the {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL available to your application. Changes to the URL in the address bar are reflected into $location service and changes to $location are reflected into the browser address bar. + * The $location service parses the URL in the browser address bar (based on the + * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. * * **The $location service:** * @@ -421,7 +424,8 @@ function locationGetterSetter(property, preprocess) { * - Clicks on a link. * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). * - * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location} + * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular + * Services: Using $location} */ /** @@ -470,65 +474,73 @@ function $LocationProvider(){ this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', function( $rootScope, $browser, $sniffer, $rootElement) { - var currentUrl, + var $location, basePath = $browser.baseHref() || '/', pathPrefix = pathPrefixFromBase(basePath), - initUrl = $browser.url(); + initUrl = $browser.url(), + absUrlPrefix; if (html5Mode) { if ($sniffer.history) { - currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix); + $location = new LocationUrl( + convertToHtml5Url(initUrl, basePath, hashPrefix), + pathPrefix); } else { - currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix), - hashPrefix); + $location = new LocationHashbangUrl( + convertToHashbangUrl(initUrl, basePath, hashPrefix), + hashPrefix); } + } else { + $location = new LocationHashbangUrl(initUrl, hashPrefix); + } - // link rewriting - var u = currentUrl, - absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix; + // link rewriting + absUrlPrefix = composeProtocolHostPort( + $location.protocol(), $location.host(), $location.port()) + pathPrefix; - $rootElement.bind('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then + $rootElement.bind('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then - if (event.ctrlKey || event.metaKey || event.which == 2) return; + if (event.ctrlKey || event.metaKey || event.which == 2) return; - var elm = jqLite(event.target); + var elm = jqLite(event.target); - // traverse the DOM up to find first A tag - while (elm.length && lowercase(elm[0].nodeName) !== 'a') { - elm = elm.parent(); - } + // traverse the DOM up to find first A tag + while (elm.length && lowercase(elm[0].nodeName) !== 'a') { + elm = elm.parent(); + } - var absHref = elm.prop('href'); + var absHref = elm.prop('href'); - if (!absHref || - elm.attr('target') || - absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path - return; - } + if (!absHref || + elm.attr('target') || + absHref.indexOf(absUrlPrefix) !== 0) { // link to different domain or base path + return; + } + + // update location with href without the prefix + $location.url(absHref.substr(absUrlPrefix.length)); + $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 - currentUrl.url(absHref.substr(absUrlPrefix.length)); - $rootScope.$apply(); - event.preventDefault(); - // hack to work around FF6 bug 684208 when scenario runner clicks on links - window.angular['ff-684208-preventDefault'] = true; - }); - } else { - currentUrl = new LocationHashbangUrl(initUrl, hashPrefix); - } // rewrite hashbang url <> html5 url - if (currentUrl.absUrl() != initUrl) { - $browser.url(currentUrl.absUrl(), true); + if ($location.absUrl() != initUrl) { + $browser.url($location.absUrl(), true); } // update $location when $browser url changes $browser.onUrlChange(function(newUrl) { - if (currentUrl.absUrl() != newUrl) { + if ($location.absUrl() != newUrl) { $rootScope.$evalAsync(function() { - currentUrl.$$parse(newUrl); + var oldUrl = $location.absUrl(); + + $location.$$parse(newUrl); + afterLocationChange(oldUrl); }); if (!$rootScope.$$phase) $rootScope.$digest(); } @@ -537,17 +549,29 @@ function $LocationProvider(){ // update browser var changeCounter = 0; $rootScope.$watch(function $locationWatch() { - if ($browser.url() != currentUrl.absUrl()) { + var oldUrl = $browser.url(); + + if (!changeCounter || oldUrl != $location.absUrl()) { changeCounter++; $rootScope.$evalAsync(function() { - $browser.url(currentUrl.absUrl(), currentUrl.$$replace); - currentUrl.$$replace = false; + if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). + defaultPrevented) { + $location.$$parse(oldUrl); + } else { + $browser.url($location.absUrl(), $location.$$replace); + $location.$$replace = false; + afterLocationChange(oldUrl); + } }); } return changeCounter; }); - return currentUrl; + return $location; + + function afterLocationChange(oldUrl) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); + } }]; } -- cgit v1.2.3