mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-25 10:50:22 +00:00
refactored $location service so that it correctly updates under all conditions
This commit is contained in:
parent
6bd8006edc
commit
1b768b8443
8 changed files with 165 additions and 65 deletions
|
|
@ -1,15 +1,85 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="../src/angular-bootstrap.js#autobind"></script>
|
||||
</head>
|
||||
<body ng:init="$window.$root = this">
|
||||
<div ng:click="$window.alert('outter')">
|
||||
outter
|
||||
<div ng:click="$window.alert('inner')">inner</div>
|
||||
<a href="#ERROR" ng:click="$window.alert('link')">link</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function TicTacToeCntl(){
|
||||
this.cellStyle= {
|
||||
'height': '20px',
|
||||
'width': '20px',
|
||||
'border': '1px solid black',
|
||||
'text-align': 'center',
|
||||
'vertical-align': 'middle',
|
||||
'cursor': 'pointer'
|
||||
};
|
||||
this.reset();
|
||||
this.$watch('$location.hashPath', this.setMemento);
|
||||
this.$onEval(function(){
|
||||
this.$location.hashPath = this.getMemento();
|
||||
});
|
||||
}
|
||||
TicTacToeCntl.prototype = {
|
||||
dropPiece: function(row, col) {
|
||||
if (!this.winner && !this.board[row][col]) {
|
||||
this.board[row][col] = this.nextMove;
|
||||
this.nextMove = this.nextMove == 'X' ? 'O' : 'X';
|
||||
this.grade();
|
||||
}
|
||||
},
|
||||
reset: function(){
|
||||
this.board = [
|
||||
['', '', ''],
|
||||
['', '', ''],
|
||||
['', '', '']
|
||||
];
|
||||
this.nextMove = 'X';
|
||||
this.winner = '';
|
||||
},
|
||||
grade: function(){
|
||||
var b = this.board;
|
||||
this.winner =
|
||||
row(0) || row(1) || row(2) ||
|
||||
col(0) || col(1) || col(2) ||
|
||||
diagonal(-1) || diagonal(1);
|
||||
function row(r) { return same(b[r][0], b[r][1], b[r][2]);}
|
||||
function col(c) { return same(b[0][c], b[1][c], b[2][c]);}
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
},
|
||||
getMemento: function(){
|
||||
var rows = [];
|
||||
angular.foreach(this.board, function(row){
|
||||
rows.push(row.join(','));
|
||||
});
|
||||
return rows.join(';') + '/' + this.nextMove;
|
||||
},
|
||||
setMemento: function(value) {
|
||||
if (value) {
|
||||
value = value.split('/');
|
||||
this.nextMove = value[1];
|
||||
angular.foreach(value[0].split(';'), function(row, i){
|
||||
this.board[i] = row.split(',');
|
||||
}, this);
|
||||
} else {
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<h3>Tic-Tac-Toe</h3>
|
||||
Next Player: {{nextMove}}
|
||||
<div ng:show="winner">Player {{winner}} has won!</div>
|
||||
<table ng:controller="TicTacToeCntl">
|
||||
<tr ng:repeat="row in board" style="height:15px;">
|
||||
<td ng:repeat="cell in row" ng:style="cellStyle"
|
||||
ng:click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng:click="reset()">reset board</button>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
15
scenario/location.html
Normal file
15
scenario/location.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js#autobind"></script>
|
||||
</head>
|
||||
<body ng:init="$window.$scope = this">
|
||||
<pre>$location={{$location}}</pre>
|
||||
<hr/>
|
||||
href: <input type="text" name="$location.href" size="120"/> <br/>
|
||||
hash: <input type="text" name="$location.hash" size="120"/> <br/>
|
||||
hashPath: <input type="text" name="$location.hashPath" size="120"/> <br/>
|
||||
hashSearch: <input type="text" name="$location.hashSearch" size="120" ng:format="json"/> <br/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -300,10 +300,10 @@ function bind(_this, _function) {
|
|||
} :
|
||||
function() {
|
||||
return _function.apply(_this, curryArgs.concat(slice.call(arguments, 0, arguments.length)));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// in IE, native methonds ore not functions and so they can not be bound (but they don't need to be)
|
||||
return function(a, b, c, d, e){ return _function(a, b, c, d, e); };
|
||||
// in IE, native methods ore not functions and so they can not be bound (but they don't need to be)
|
||||
return _function;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
6
src/angular-bootstrap.js
vendored
6
src/angular-bootstrap.js
vendored
|
|
@ -22,16 +22,14 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
(function(previousOnLoad){
|
||||
var filename = /(.*)\/angular-(.*).js(#(.*))?/,
|
||||
var filename = /(.*)\/angular-(.*).js(#.*)?/,
|
||||
scripts = document.getElementsByTagName("SCRIPT"),
|
||||
serverPath,
|
||||
config,
|
||||
match;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
match = (scripts[j].src || "").match(filename);
|
||||
if (match) {
|
||||
serverPath = match[1];
|
||||
config = match[4];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +61,7 @@
|
|||
try {
|
||||
if (previousOnLoad) previousOnLoad();
|
||||
} catch(e) {}
|
||||
angularInit(parseKeyValue(config));
|
||||
angularInit(parseKeyValue(angularJsConfig(document)));
|
||||
};
|
||||
|
||||
})(window.onload);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ var NUMBER = /^\s*[-+]?\d*(\.\d*)?\s*$/;
|
|||
|
||||
extend(angularFormatter, {
|
||||
'noop':formatter(identity, identity),
|
||||
'json':formatter(toJson, fromJson),
|
||||
'boolean':formatter(toString, toBoolean),
|
||||
'number':formatter(toString,
|
||||
function(obj){
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ JQLite.prototype = {
|
|||
if (!event.preventDefault) {
|
||||
event.preventDefault = function(){
|
||||
event.returnValue = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
foreach(eventHandler.fns, function(fn){
|
||||
fn.call(self, event);
|
||||
|
|
|
|||
|
|
@ -7,61 +7,78 @@ var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+)
|
|||
var HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/;
|
||||
var DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
|
||||
angularService("$location", function(browser){
|
||||
var scope = this, location = {parse:parseUrl, toString:toString};
|
||||
var lastHash, lastUrl;
|
||||
var scope = this,
|
||||
location = {parse:parseUrl, toString:toString, update:update},
|
||||
lastLocation = {};
|
||||
|
||||
browser.watchUrl(function(url){
|
||||
update(url);
|
||||
scope.$root.$eval();
|
||||
});
|
||||
this.$onEval(PRIORITY_FIRST, update);
|
||||
this.$onEval(PRIORITY_LAST, update);
|
||||
update(browser.getUrl());
|
||||
return location;
|
||||
|
||||
function update(href){
|
||||
if (href) {
|
||||
parseUrl(href);
|
||||
} else {
|
||||
href = check('href') || check('protocol', '://', 'host', ':', 'port', '', 'path', '?', 'search');
|
||||
var hash = check('hash');
|
||||
if (isUndefined(hash)) hash = check('hashPath', '?', 'hashSearch');
|
||||
if (isDefined(hash)) {
|
||||
href = (href || location.href).split('#')[0];
|
||||
href+= '#' + hash;
|
||||
}
|
||||
if (isDefined(href)) {
|
||||
parseUrl(href);
|
||||
browser.setUrl(href);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function check() {
|
||||
var i = -1,
|
||||
length=arguments.length,
|
||||
name, seperator, parts = [],
|
||||
value, same = true;
|
||||
for(; i<length; i = i+2) {
|
||||
parts.push(seperator = (arguments[i] || ''));
|
||||
name = arguments[i + 1];
|
||||
value=location[name];
|
||||
parts.push(typeof value == 'object' ? toKeyValue(value) : value);
|
||||
same = same && equals(lastLocation[name], value);
|
||||
}
|
||||
return same ? undefined : parts.join('');
|
||||
}
|
||||
|
||||
function parseUrl(url){
|
||||
if (isDefined(url)) {
|
||||
var match = URL_MATCH.exec(url);
|
||||
if (match) {
|
||||
location.href = url;
|
||||
location.href = url.replace('#$', '');
|
||||
location.protocol = match[1];
|
||||
location.host = match[3] || '';
|
||||
location.port = match[5] || DEFAULT_PORTS[location.href] || null;
|
||||
location.port = match[5] || DEFAULT_PORTS[location.protocol] || null;
|
||||
location.path = match[6];
|
||||
location.search = parseKeyValue(match[8]);
|
||||
location.hash = match[9] || '';
|
||||
if (location.hash)
|
||||
location.hash = location.hash.substr(1);
|
||||
parseHash(location.hash);
|
||||
match = HASH_MATCH.exec(location.hash);
|
||||
location.hashPath = match[1] || '';
|
||||
location.hashSearch = parseKeyValue(match[3]);
|
||||
|
||||
copy(location, lastLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
function parseHash(hash) {
|
||||
var match = HASH_MATCH.exec(hash);
|
||||
location.hashPath = match[1] || '';
|
||||
location.hashSearch = parseKeyValue(match[3]);
|
||||
lastHash = hash;
|
||||
}
|
||||
|
||||
function toString() {
|
||||
if (lastHash === location.hash) {
|
||||
var hashKeyValue = toKeyValue(location.hashSearch),
|
||||
hash = (location.hashPath ? location.hashPath : '') + (hashKeyValue ? '?' + hashKeyValue : ''),
|
||||
url = location.href.split('#')[0] + '#' + (hash ? hash : '');
|
||||
if (url !== location.href) parseUrl(url);
|
||||
return url;
|
||||
} else {
|
||||
parseUrl(location.href.split('#')[0] + '#' + location.hash);
|
||||
return toString();
|
||||
}
|
||||
update();
|
||||
return location.href;
|
||||
}
|
||||
browser.watchUrl(function(url){
|
||||
parseUrl(url);
|
||||
scope.$root.$eval();
|
||||
});
|
||||
parseUrl(browser.getUrl());
|
||||
this.$onEval(PRIORITY_FIRST, function(){
|
||||
if (location.hash != lastHash) {
|
||||
parseHash(location.hash);
|
||||
}
|
||||
});
|
||||
this.$onEval(PRIORITY_LAST, function(){
|
||||
var url = toString();
|
||||
if (lastUrl != url) {
|
||||
browser.setUrl(url);
|
||||
lastUrl = url;
|
||||
}
|
||||
});
|
||||
return location;
|
||||
}, {inject: ['$browser']});
|
||||
|
||||
angularService("$log", function($window){
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ describe("service", function(){
|
|||
expect(scope.$location.hashPath).toEqual('');
|
||||
expect(scope.$location.hashSearch).toEqual({});
|
||||
|
||||
expect(scope.$location.toString()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html#');
|
||||
expect(scope.$location.toString()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html');
|
||||
});
|
||||
|
||||
it('should update url on hash change', function(){
|
||||
|
|
@ -123,6 +123,14 @@ describe("service", function(){
|
|||
expect(scope.$location.hash).toEqual('?a=b');
|
||||
});
|
||||
|
||||
it("should parse url which contains - in host", function(){
|
||||
scope.$location.parse('http://a-b1.c-d.09/path');
|
||||
expect(scope.$location.href).toEqual('http://a-b1.c-d.09/path');
|
||||
expect(scope.$location.protocol).toEqual('http');
|
||||
expect(scope.$location.host).toEqual('a-b1.c-d.09');
|
||||
expect(scope.$location.path).toEqual('/path');
|
||||
});
|
||||
|
||||
it('should update hash before any processing', function(){
|
||||
var scope = compile('<div>');
|
||||
var log = '';
|
||||
|
|
@ -136,15 +144,6 @@ describe("service", function(){
|
|||
scope.$eval();
|
||||
expect(log).toEqual('/abc;');
|
||||
});
|
||||
|
||||
it("should parse url which contains - in host", function(){
|
||||
scope.$location.parse('http://a-b1.c-d.09/path');
|
||||
expect(scope.$location.href).toEqual('http://a-b1.c-d.09/path');
|
||||
expect(scope.$location.protocol).toEqual('http');
|
||||
expect(scope.$location.host).toEqual('a-b1.c-d.09');
|
||||
expect(scope.$location.path).toEqual('/path');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("$invalidWidgets", function(){
|
||||
|
|
|
|||
Loading…
Reference in a new issue