diff options
| author | Vojta Jina | 2011-06-22 19:57:22 +0200 |
|---|---|---|
| committer | Vojta Jina | 2011-09-08 20:36:33 +0200 |
| commit | 988ed451b508b9d7ea4690b150993ec62d8a3743 (patch) | |
| tree | 70c8a2200ae4b80da04b6eba239a07962f209638 /src/Browser.js | |
| parent | fc2f188d4d8f06aab31979b293d95580e19cbdf1 (diff) | |
| download | angular.js-988ed451b508b9d7ea4690b150993ec62d8a3743.tar.bz2 | |
feat($browser): jQuery style url method, onUrlChange event
This is just basic implementation of $browser.url, $browser.onUrlChange methods:
$browser.url() - returns current location.href
$browser.url('/new') - set url to /new
If supported, history.pushState is used, location.href property otherwise.
$browser.url('/new', true) - replace current url with /new
If supported, history.replaceState is used, location.replace otherwise.
$browser.onUrlChange is only fired when url is changed from the browser:
- user types into address bar
- user clicks on back/forward button
- user clicks on link
It's not fired when url is changed using $browser.url()
Breaks Removed $browser.setUrl(), $browser.getUrl(), use $browser.url()
Breaks Removed $browser.onHashChange(), use $browser.onUrlChange()
Diffstat (limited to 'src/Browser.js')
| -rw-r--r-- | src/Browser.js | 130 |
1 files changed, 78 insertions, 52 deletions
diff --git a/src/Browser.js b/src/Browser.js index f25e8a20..8e8f511a 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -34,15 +34,16 @@ var XHR = window.XMLHttpRequest || function () { * @param {object} body jQuery wrapped document.body. * @param {function()} XHR XMLHttpRequest constructor. * @param {object} $log console.log or an object with the same interface. + * @param {object} $sniffer $sniffer service */ -function Browser(window, document, body, XHR, $log) { +function Browser(window, document, body, XHR, $log, $sniffer) { var self = this, rawDocument = document[0], location = window.location, + history = window.history, setTimeout = window.setTimeout, clearTimeout = window.clearTimeout, - pendingDeferIds = {}, - lastLocationUrl; + pendingDeferIds = {}; self.isMock = false; @@ -194,78 +195,103 @@ function Browser(window, document, body, XHR, $log) { // URL API ////////////////////////////////////////////////////////////// - /** - * @workInProgress - * @ngdoc method - * @name angular.service.$browser#setUrl - * @methodOf angular.service.$browser - * - * @param {string} url New url - * - * @description - * Sets browser's url - */ - self.setUrl = function(url) { - - var existingURL = lastLocationUrl; - if (!existingURL.match(/#/)) existingURL += '#'; - if (!url.match(/#/)) url += '#'; - if (existingURL != url) { - location.href = url; - } - }; + var lastBrowserUrl = location.href; /** * @workInProgress * @ngdoc method - * @name angular.service.$browser#getUrl + * @name angular.service.$browser#url * @methodOf angular.service.$browser * * @description - * Get current browser's url + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining * - * @returns {string} Browser's url + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link angular.service.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record ? */ - self.getUrl = function() { - return lastLocationUrl = location.href; + self.url = function(url, replace) { + // setter + if (url) { + lastBrowserUrl = url; + if ($sniffer.history) { + if (replace) history.replaceState(null, '', url); + else history.pushState(null, '', url); + } else { + if (replace) location.replace(url); + else location.href = url; + } + return self; + // getter + } else { + return location.href; + } }; + var urlChangeListeners = [], + urlChangeInit = false; + + function fireUrlChange() { + if (lastBrowserUrl == self.url()) return; + + lastBrowserUrl = self.url(); + forEach(urlChangeListeners, function(listener) { + listener(self.url()); + }); + } /** * @workInProgress * @ngdoc method - * @name angular.service.$browser#onHashChange + * @name angular.service.$browser#onUrlChange * @methodOf angular.service.$browser + * @TODO(vojta): refactor to use node's syntax for events * * @description - * Detects if browser support onhashchange events and register a listener otherwise registers - * $browser poller. The `listener` will then get called when the hash changes. + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed by outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link * - * The listener gets called with either HashChangeEvent object or simple object that also contains - * `oldURL` and `newURL` properties. + * It's not called when url is changed by $browser.url() method * - * Note: this api is intended for use only by the $location service. Please use the - * {@link angular.service.$location $location service} to monitor hash changes in angular apps. + * The listener gets called with new url as parameter. * - * @param {function(event)} listener Listener function to be called when url hash changes. - * @return {function()} Returns the registered listener fn - handy if the fn is anonymous. + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link angular.service.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. */ - self.onHashChange = function(listener) { - // IE8 comp mode returns true, but doesn't support hashchange event - var dm = window.document.documentMode; - if ('onhashchange' in window && (isUndefined(dm) || dm >= 8)) { - jqLite(window).bind('hashchange', listener); - } else { - var lastBrowserUrl = self.getUrl(); - - self.addPollFn(function() { - if (lastBrowserUrl != self.getUrl()) { - listener(); - lastBrowserUrl = self.getUrl(); - } - }); + self.onUrlChange = function(callback) { + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange); + // hashchange event + if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange); + // polling + else self.addPollFn(fireUrlChange); + + urlChangeInit = true; } - return listener; + + urlChangeListeners.push(callback); + return callback; }; ////////////////////////////////////////////////////////////// |
