aboutsummaryrefslogtreecommitdiffstats
path: root/src/Browser.js
diff options
context:
space:
mode:
authorVojta Jina2011-06-22 19:57:22 +0200
committerVojta Jina2011-09-08 20:36:33 +0200
commit988ed451b508b9d7ea4690b150993ec62d8a3743 (patch)
tree70c8a2200ae4b80da04b6eba239a07962f209638 /src/Browser.js
parentfc2f188d4d8f06aab31979b293d95580e19cbdf1 (diff)
downloadangular.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.js130
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;
};
//////////////////////////////////////////////////////////////