mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-20 08:20:25 +00:00
- split up services into files under src/service - split up specs into files under test/service - rewrite all specs so that they don't depend on one global forEach - get rid of obsolete code and tests in ng:switch - rename mock $log spec from "$log" to "$log mock"
264 lines
6.9 KiB
JavaScript
264 lines
6.9 KiB
JavaScript
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']);
|