aboutsummaryrefslogtreecommitdiffstats
path: root/src/service/location.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/location.js')
-rw-r--r--src/service/location.js264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/service/location.js b/src/service/location.js
new file mode 100644
index 00000000..31323284
--- /dev/null
+++ b/src/service/location.js
@@ -0,0 +1,264 @@
+var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
+ HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/,
+ DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
+
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.service.$location
+ * @requires $browser
+ *
+ * @property {string} href
+ * @property {string} protocol
+ * @property {string} host
+ * @property {number} port
+ * @property {string} path
+ * @property {Object.<string|boolean>} search
+ * @property {string} hash
+ * @property {string} hashPath
+ * @property {Object.<string|boolean>} hashSearch
+ *
+ * @description
+ * Parses the browser location url and makes it available to your application.
+ * Any changes to the url are reflected into $location service and changes to
+ * $location are reflected to url.
+ * Notice that using browser's forward/back buttons changes the $location.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <a href="#">clear hash</a> |
+ <a href="#myPath?name=misko">test hash</a><br/>
+ <input type='text' name="$location.hash"/>
+ <pre>$location = {{$location}}</pre>
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ */
+angularServiceInject("$location", function($browser) {
+ var scope = this,
+ location = {update:update, updateHash: updateHash},
+ lastLocation = {};
+
+ $browser.onHashChange(function() { //register
+ update($browser.getUrl());
+ copy(location, lastLocation);
+ scope.$eval();
+ })(); //initialize
+
+ this.$onEval(PRIORITY_FIRST, sync);
+ this.$onEval(PRIORITY_LAST, updateBrowser);
+
+ return location;
+
+ // PUBLIC METHODS
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$location#update
+ * @methodOf angular.service.$location
+ *
+ * @description
+ * Update location object
+ * Does not immediately update the browser
+ * Browser is updated at the end of $eval()
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ scope.$location.update('http://www.angularjs.org/path#hash?search=x');
+ scope.$location.update({host: 'www.google.com', protocol: 'https'});
+ scope.$location.update({hashPath: '/path', hashSearch: {a: 'b', x: true}});
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ *
+ * @param {(string|Object)} href Full href as a string or object with properties
+ */
+ function update(href) {
+ if (isString(href)) {
+ extend(location, parseHref(href));
+ } else {
+ if (isDefined(href.hash)) {
+ extend(href, isString(href.hash) ? parseHash(href.hash) : href.hash);
+ }
+
+ extend(location, href);
+
+ if (isDefined(href.hashPath || href.hashSearch)) {
+ location.hash = composeHash(location);
+ }
+
+ location.href = composeHref(location);
+ }
+ }
+
+ /**
+ * @workInProgress
+ * @ngdoc method
+ * @name angular.service.$location#updateHash
+ * @methodOf angular.service.$location
+ *
+ * @description
+ * Update location hash part
+ * @see update()
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ scope.$location.updateHash('/hp')
+ ==> update({hashPath: '/hp'})
+ scope.$location.updateHash({a: true, b: 'val'})
+ ==> update({hashSearch: {a: true, b: 'val'}})
+ scope.$location.updateHash('/hp', {a: true})
+ ==> update({hashPath: '/hp', hashSearch: {a: true}})
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ *
+ * @param {(string|Object)} path A hashPath or hashSearch object
+ * @param {Object=} search A hashSearch object
+ */
+ function updateHash(path, search) {
+ var hash = {};
+
+ if (isString(path)) {
+ hash.hashPath = path;
+ hash.hashSearch = search || {};
+ } else
+ hash.hashSearch = path;
+
+ hash.hash = composeHash(hash);
+
+ update({hash: hash});
+ }
+
+
+ // INNER METHODS
+
+ /**
+ * Synchronizes all location object properties.
+ *
+ * User is allowed to change properties, so after property change,
+ * location object is not in consistent state.
+ *
+ * Properties are synced with the following precedence order:
+ *
+ * - `$location.href`
+ * - `$location.hash`
+ * - everything else
+ *
+ * @example
+ * <pre>
+ * scope.$location.href = 'http://www.angularjs.org/path#a/b'
+ * </pre>
+ * immediately after this call, other properties are still the old ones...
+ *
+ * This method checks the changes and update location to the consistent state
+ */
+ function sync() {
+ if (!equals(location, lastLocation)) {
+ if (location.href != lastLocation.href) {
+ update(location.href);
+ return;
+ }
+ if (location.hash != lastLocation.hash) {
+ var hash = parseHash(location.hash);
+ updateHash(hash.hashPath, hash.hashSearch);
+ } else {
+ location.hash = composeHash(location);
+ location.href = composeHref(location);
+ }
+ update(location.href);
+ }
+ }
+
+
+ /**
+ * If location has changed, update the browser
+ * This method is called at the end of $eval() phase
+ */
+ function updateBrowser() {
+ sync();
+
+ if ($browser.getUrl() != location.href) {
+ $browser.setUrl(location.href);
+ copy(location, lastLocation);
+ }
+ }
+
+ /**
+ * Compose href string from a location object
+ *
+ * @param {Object} loc The location object with all properties
+ * @return {string} Composed href
+ */
+ function composeHref(loc) {
+ var url = toKeyValue(loc.search);
+ var port = (loc.port == DEFAULT_PORTS[loc.protocol] ? _null : loc.port);
+
+ return loc.protocol + '://' + loc.host +
+ (port ? ':' + port : '') + loc.path +
+ (url ? '?' + url : '') + (loc.hash ? '#' + loc.hash : '');
+ }
+
+ /**
+ * Compose hash string from location object
+ *
+ * @param {Object} loc Object with hashPath and hashSearch properties
+ * @return {string} Hash string
+ */
+ function composeHash(loc) {
+ var hashSearch = toKeyValue(loc.hashSearch);
+ //TODO: temporary fix for issue #158
+ return escape(loc.hashPath).replace(/%21/gi, '!').replace(/%3A/gi, ':').replace(/%24/gi, '$') +
+ (hashSearch ? '?' + hashSearch : '');
+ }
+
+ /**
+ * Parse href string into location object
+ *
+ * @param {string} href
+ * @return {Object} The location object
+ */
+ function parseHref(href) {
+ var loc = {};
+ var match = URL_MATCH.exec(href);
+
+ if (match) {
+ loc.href = href.replace(/#$/, '');
+ loc.protocol = match[1];
+ loc.host = match[3] || '';
+ loc.port = match[5] || DEFAULT_PORTS[loc.protocol] || _null;
+ loc.path = match[6] || '';
+ loc.search = parseKeyValue(match[8]);
+ loc.hash = match[10] || '';
+
+ extend(loc, parseHash(loc.hash));
+ }
+
+ return loc;
+ }
+
+ /**
+ * Parse hash string into object
+ *
+ * @param {string} hash
+ */
+ function parseHash(hash) {
+ var h = {};
+ var match = HASH_MATCH.exec(hash);
+
+ if (match) {
+ h.hash = hash;
+ h.hashPath = unescape(match[1] || '');
+ h.hashSearch = parseKeyValue(match[3]);
+ }
+
+ return h;
+ }
+}, ['$browser']);