diff options
| author | Misko Hevery | 2010-07-29 12:50:14 -0700 | 
|---|---|---|
| committer | Misko Hevery | 2010-07-29 12:54:13 -0700 | 
| commit | 1b768b84439e725010acc943ebfda462e49d3704 (patch) | |
| tree | 06476962f7116e8c10ddb35d17c5bd3038528aaa | |
| parent | 6bd8006edcbfe1dc1be8cb865fbcfe25157fe117 (diff) | |
| download | angular.js-1b768b84439e725010acc943ebfda462e49d3704.tar.bz2 | |
refactored $location service so that it correctly updates under all conditions
| -rw-r--r-- | example/temp.html | 86 | ||||
| -rw-r--r-- | scenario/location.html | 15 | ||||
| -rw-r--r-- | src/Angular.js | 6 | ||||
| -rw-r--r-- | src/angular-bootstrap.js | 6 | ||||
| -rw-r--r-- | src/formatters.js | 1 | ||||
| -rw-r--r-- | src/jqLite.js | 2 | ||||
| -rw-r--r-- | src/services.js | 95 | ||||
| -rw-r--r-- | test/servicesSpec.js | 19 | 
8 files changed, 165 insertions, 65 deletions
| diff --git a/example/temp.html b/example/temp.html index f21d3f5c..b238c185 100644 --- a/example/temp.html +++ b/example/temp.html @@ -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>
\ No newline at end of file diff --git a/scenario/location.html b/scenario/location.html new file mode 100644 index 00000000..a162636b --- /dev/null +++ b/scenario/location.html @@ -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> diff --git a/src/Angular.js b/src/Angular.js index 32e3ccf7..80acddf0 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -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;    }  } diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index 90e1104e..e055371a 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -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); diff --git a/src/formatters.js b/src/formatters.js index 40462cf3..ca1ce83e 100644 --- a/src/formatters.js +++ b/src/formatters.js @@ -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){ diff --git a/src/jqLite.js b/src/jqLite.js index 04682754..22b3c070 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -107,7 +107,7 @@ JQLite.prototype = {            if (!event.preventDefault) {              event.preventDefault = function(){                event.returnValue = false; -            } +            };            }            foreach(eventHandler.fns, function(fn){              fn.call(self, event); diff --git a/src/services.js b/src/services.js index 106f8954..3dd7df09 100644 --- a/src/services.js +++ b/src/services.js @@ -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){ diff --git a/test/servicesSpec.js b/test/servicesSpec.js index 91538703..cb5c9b30 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -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(){ | 
