diff options
| author | Misko Hevery | 2010-05-10 10:36:06 -0700 |
|---|---|---|
| committer | Misko Hevery | 2010-05-10 10:36:06 -0700 |
| commit | f5027cc375cf29d8a78679297d9f6bdca9567eb7 (patch) | |
| tree | f415af2b615656562c1d1ac10fe9b4aab83b54c7 | |
| parent | 4542716370ac52f385795f509436104a2a3501d2 (diff) | |
| parent | 664f1c56876f00b885272c39f759641271eef1dc (diff) | |
| download | angular.js-f5027cc375cf29d8a78679297d9f6bdca9567eb7.tar.bz2 | |
Merge branch 'master' of github.com:angular/angular.js
| -rw-r--r-- | src/Angular.js | 19 | ||||
| -rw-r--r-- | src/AngularPublic.js | 1 | ||||
| -rw-r--r-- | src/Browser.js | 7 | ||||
| -rw-r--r-- | src/Compiler.js | 13 | ||||
| -rw-r--r-- | src/JSON.js | 2 | ||||
| -rw-r--r-- | src/Resource.js | 12 | ||||
| -rw-r--r-- | src/Scope.js | 7 | ||||
| -rw-r--r-- | src/directives.js | 8 | ||||
| -rw-r--r-- | src/services.js | 85 | ||||
| -rw-r--r-- | src/widgets.js | 9 | ||||
| -rwxr-xr-x | test.sh | 4 | ||||
| -rw-r--r-- | test/AngularSpec.js | 8 | ||||
| -rw-r--r-- | test/JsonTest.js | 10 | ||||
| -rw-r--r-- | test/ResourceSpec.js | 9 | ||||
| -rw-r--r-- | test/angular-mocks.js | 11 | ||||
| -rw-r--r-- | test/directivesSpec.js | 9 | ||||
| -rw-r--r-- | test/servicesSpec.js | 9 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 1 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 3 |
19 files changed, 165 insertions, 62 deletions
diff --git a/src/Angular.js b/src/Angular.js index 3af21ced..2df6bbef 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -231,13 +231,14 @@ function isLeafNode (node) { function copy(source, destination){ if (!destination) { - if (isArray(source)) { - return copy(source, []); - } else if (isObject(source)) { - return copy(source, {}); - } else { - return source; + if (source) { + if (isArray(source)) { + return copy(source, []); + } else if (isObject(source)) { + return copy(source, {}); + } } + return source; } else { if (isArray(source)) { while(destination.length) { @@ -249,7 +250,11 @@ function copy(source, destination){ }); } foreach(source, function(value, key){ - destination[key] = isArray(value) ? copy(value, []) : (isObject(value) ? copy(value, {}) : value); + destination[key] = value ? + ( isArray(value) ? + copy(value, []) : + (isObject(value) ? copy(value, {}) : value)) : + value; }); return destination; } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 1739ac4b..7230c3e5 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -22,6 +22,7 @@ extend(angular, { 'isDefined': isDefined, 'isString': isString, 'isFunction': isFunction, + 'isObject': isObject, 'isNumber': isNumber, 'isArray': isArray }); diff --git a/src/Browser.js b/src/Browser.js index d2e8608d..2a90b63f 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -8,6 +8,7 @@ function Browser(location, document) { this.expectedUrl = location.href; this.urlListeners = []; this.hoverListener = noop; + this.isMock = false; this.XHR = window.XMLHttpRequest || function () { try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} @@ -53,6 +54,10 @@ Browser.prototype = { }, xhr: function(method, url, post, callback){ + if (isFunction(post)) { + callback = post; + post = null; + } var xhr = new this.XHR(); xhr.open(method, url, true); xhr.onreadystatechange = function() { @@ -60,7 +65,7 @@ Browser.prototype = { callback(xhr.status || 200, xhr.responseText); } }; - xhr.send(''); + xhr.send(post || ''); }, watchUrl: function(fn){ diff --git a/src/Compiler.js b/src/Compiler.js index c77c6b30..c8910c27 100644 --- a/src/Compiler.js +++ b/src/Compiler.js @@ -77,7 +77,18 @@ function Compiler(textMarkup, attrMarkup, directives, widgets){ Compiler.prototype = { compile: function(rawElement) { rawElement = jqLite(rawElement); - var template = this.templatize(rawElement, 0, 0) || new Template(); + var index = 0, + template, + parent = rawElement.parent(); + if (parent && parent[0]) { + parent = parent[0]; + for(var i = 0; i < parent.childNodes.length; i++) { + if (parent.childNodes[i] == rawElement[0]) { + index = i; + } + } + } + template = this.templatize(rawElement, index, 0) || new Template(); return function(element, parentScope){ element = jqLite(element); var scope = parentScope && parentScope.$eval ? diff --git a/src/JSON.js b/src/JSON.js index 5c3e1043..340b075a 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -74,7 +74,7 @@ function toJsonArray(buf, obj, pretty, stack){ var childPretty = pretty ? pretty + " " : false; var keys = []; for(var k in obj) { - if (k.indexOf('$$') === 0) + if (k.indexOf('$$') === 0 || obj[k] === undefined) continue; keys.push(k); } diff --git a/src/Resource.js b/src/Resource.js index 34ad1c5d..c9bad0c0 100644 --- a/src/Resource.js +++ b/src/Resource.js @@ -21,7 +21,7 @@ Route.prototype = { }); url = url.replace(/\/?#$/, ''); var query = []; - foreach(params, function(value, key){ + foreachSorted(params, function(value, key){ if (!self.urlParams[key]) { query.push(encodeURI(key) + '=' + encodeURI(value)); } @@ -69,14 +69,18 @@ ResourceFactory.prototype = { switch(arguments.length) { case 3: callback = a3; case 2: - if (typeof a2 == 'function') { + if (isFunction(a2)) { callback = a2; } else { params = a1; data = a2; break; } - case 1: if (isPost) data = a1; else params = a1; break; + case 1: + if (isFunction(a1)) callback = a1; + else if (isPost) data = a1; + else params = a1; + break; case 0: break; default: throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments."; @@ -109,7 +113,7 @@ ResourceFactory.prototype = { case 1: if (typeof a1 == 'function') callback = a1; else params = a1; case 0: break; default: - throw "Expected between 1-3 arguments [params, data, callback], got " + arguments.length + " arguments."; + throw "Expected between 1-2 arguments [params, callback], got " + arguments.length + " arguments."; } var self = this; Resource[name](params, this, function(response){ diff --git a/src/Scope.js b/src/Scope.js index 1b93418f..687d3628 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -97,7 +97,7 @@ function createScope(parent, services, existing) { $set: bind(instance, setter, instance), $eval: function $eval(exp) { - if (isDefined(exp)) { + if (exp) { return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length)); } else { foreach(evalLists.sorted, function(list) { @@ -127,10 +127,11 @@ function createScope(parent, services, existing) { var watch = expressionCompile(watchExp), last; function watcher(){ - var value = watch.call(instance); + var value = watch.call(instance), + lastValue = last; if (last !== value) { - instance.$tryEval(listener, exceptionHandler, value, last); last = value; + instance.$tryEval(listener, exceptionHandler, value, lastValue); } } instance.$onEval(PRIORITY_WATCH, watcher); diff --git a/src/directives.js b/src/directives.js index d9cf7962..a3575d62 100644 --- a/src/directives.js +++ b/src/directives.js @@ -30,8 +30,9 @@ angularDirective("ng-bind", function(expression){ value = this.$tryEval(expression, function(e){ error = toJson(e); }), - isHtml = value instanceof HTML; - if (!isHtml && isObject(value)) { + isHtml = value instanceof HTML, + isDomElement = isElement(value); + if (!isHtml && !isDomElement && isObject(value)) { value = toJson(value); } if (value != lastValue || error != lastError) { @@ -41,6 +42,9 @@ angularDirective("ng-bind", function(expression){ if (error) value = error; if (isHtml) { element.html(value.html); + } else if (isDomElement) { + element.html(''); + element.append(value); } else { element.text(value); } diff --git a/src/services.js b/src/services.js index 16dbcb35..bdfbfdb4 100644 --- a/src/services.js +++ b/src/services.js @@ -64,6 +64,12 @@ angularService("$location", function(browser){ return location; }, {inject: ['$browser']}); +angularService("$log", function(){ + return { + error: noop + }; +}); + angularService("$hover", function(browser) { var tooltip, self = this, error, width = 300, arrowWidth = 10; browser.hover(function(element, show){ @@ -103,7 +109,7 @@ angularService("$hover", function(browser) { width: width + "px" }); } - } else if (tooltip && false) { + } else if (tooltip) { tooltip.callout.remove(); tooltip = null; } @@ -152,6 +158,7 @@ angularService('$route', function(location, params){ onChange = [], matcher = angularWidget('NG:SWITCH').route, parentScope = this, + dirty = 0, $route = { routes: routes, onChange: bind(onChange, onChange.push), @@ -160,7 +167,7 @@ angularService('$route', function(location, params){ var route = routes[path]; if (!route) route = routes[path] = {}; if (params) angular.extend(route, params); - if (matcher(location.hashPath, path)) updateRoute(); + dirty++; return route; } }; @@ -185,13 +192,17 @@ angularService('$route', function(location, params){ parentScope.$tryEval(childScope.init); } } - this.$watch(function(){return location.hash;}, updateRoute); + this.$watch(function(){return dirty + location.hash;}, updateRoute); return $route; }, {inject: ['$location']}); angularService('$xhr', function($browser){ var self = this; return function(method, url, post, callback){ + if (isFunction(post)) { + callback = post; + post = null; + } if (post && isObject(post)) { post = toJson(post); } @@ -213,33 +224,58 @@ angularService('$xhr.bulk', function($xhr){ callbacks = [], scope = this; function bulkXHR(method, url, post, callback) { - requests.push({method: method, url: url, data:post}); - callbacks.push(callback); + if (isFunction(post)) { + callback = post; + post = null; + } + var currentQueue; + foreach(bulkXHR.urls, function(queue){ + if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) { + currentQueue = queue; + } + }); + if (currentQueue) { + if (!currentQueue.requests) currentQueue.requests = []; + if (!currentQueue.callbacks) currentQueue.callbacks = []; + currentQueue.requests.push({method: method, url: url, data:post}); + currentQueue.callbacks.push(callback); + } else { + $xhr(method, url, post, callback); + } } - bulkXHR.url = "/bulk"; + bulkXHR.urls = {}; bulkXHR.flush = function(callback){ - var currentRequests = requests, - currentCallbacks = callbacks; - requests = []; - callbacks = []; - $xhr('POST', bulkXHR.url, {requests:currentRequests}, function(code, response){ - foreach(response, function(response, i){ - try { - (currentCallbacks[i] || noop)(response.status, response.response); - } catch(e) { - self.$log.error(e); - } - }); - (callback || noop)(); + foreach(bulkXHR.urls, function(queue, url){ + var currentRequests = queue.requests, + currentCallbacks = queue.callbacks; + if (currentRequests && currentRequests.length) { + queue.requests = []; + queue.callbacks = []; + $xhr('POST', url, {requests:currentRequests}, function(code, response){ + foreach(response, function(response, i){ + try { + (currentCallbacks[i] || noop)(response.status, response.response); + } catch(e) { + scope.$log.error(e); + } + }); + (callback || noop)(); + }); + scope.$eval(); + } }); - scope.$eval(); }; + this.$onEval(PRIORITY_LAST, bulkXHR.flush); return bulkXHR; }, {inject:['$xhr']}); angularService('$xhr.cache', function($xhr){ - var inflight = {}; + var inflight = {}, self = this;; function cache(method, url, post, callback){ + if (isFunction(post)) { + callback = post; + post = null; + } if (method == 'GET') { var data; if (data = cache.data[url]) { @@ -251,14 +287,15 @@ angularService('$xhr.cache', function($xhr){ cache.delegate(method, url, post, function(status, response){ if (status == 200) cache.data[url] = { value: response }; - foreach(inflight[url].callbacks, function(callback){ + var callbacks = inflight[url].callbacks; + delete inflight[url]; + foreach(callbacks, function(callback){ try { (callback||noop)(status, copy(response)); } catch(e) { self.$log.error(e); } }); - delete inflight[url]; }); } } else { @@ -269,7 +306,7 @@ angularService('$xhr.cache', function($xhr){ cache.data = {}; cache.delegate = $xhr; return cache; -}, {inject:['$xhr']}); +}, {inject:['$xhr.bulk']}); angularService('$resource', function($xhr){ var resource = new ResourceFactory($xhr); diff --git a/src/widgets.js b/src/widgets.js index 1dadfe51..064b27fe 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -192,22 +192,21 @@ angularWidget('NG:INCLUDE', function(element){ function incrementChange(){ changeCounter++;} this.$watch(srcExp, incrementChange); this.$watch(scopeExp, incrementChange); + scope.$onEval(function(){ + if (childScope) childScope.$eval(); + }); this.$watch(function(){return changeCounter;}, function(){ var src = this.$eval(srcExp), useScope = this.$eval(scopeExp); if (src) { - scope.$browser.xhr('GET', src, function(code, response){ + scope.$xhr.cache('GET', src, function(code, response){ element.html(response); childScope = useScope || createScope(scope); compiler.compile(element)(element, childScope); childScope.$init(); - scope.$root.$eval(); }); } }); - scope.$onEval(function(){ - if (childScope) childScope.$eval(); - }); }; } }); @@ -1,2 +1,2 @@ -# java -jar lib/jstestdriver/JsTestDriver.jar --tests all -java -jar lib/jstestdriver/JsTestDriver.jar --tests all --config jsTestDriver-jquery.conf +java -jar lib/jstestdriver/JsTestDriver.jar --tests all +# java -jar lib/jstestdriver/JsTestDriver.jar --tests all --config jsTestDriver-jquery.conf diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 60079c47..de724f03 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -41,4 +41,12 @@ describe("copy", function(){ assertEquals(src.a, dst.a); assertNotSame(src.a, dst.a); }); + + it("should copy primitives", function(){ + expect(copy(null)).toEqual(null); + expect(copy('')).toEqual(''); + expect(copy(123)).toEqual(123); + expect(copy([{key:null}])).toEqual([{key:null}]); + }); + }); diff --git a/test/JsonTest.js b/test/JsonTest.js index 9b275248..1ed56da8 100644 --- a/test/JsonTest.js +++ b/test/JsonTest.js @@ -63,9 +63,9 @@ JsonTest.prototype.testItShouldEscapeUnicode = function () { JsonTest.prototype.testItShouldUTCDates = function() { var date = angular.String.toDate("2009-10-09T01:02:03Z"); - assertEquals('"2009-10-09T01:02:03Z"', toJson(date)); - assertEquals(date.getTime(), - fromJson('"2009-10-09T01:02:03Z"').getTime()); + assertEquals('"2009-10-09T01:02:03Z"', toJson(date)); + assertEquals(date.getTime(), + fromJson('"2009-10-09T01:02:03Z"').getTime()); }; JsonTest.prototype.testItShouldPreventRecursion = function () { @@ -78,3 +78,7 @@ JsonTest.prototype.testItShouldSerializeSameObjectsMultipleTimes = function () { var obj = {a:'b'}; assertEquals('{"A":{"a":"b"},"B":{"a":"b"}}', angular.toJson({A:obj, B:obj})); }; + +JsonTest.prototype.testItShouldNotSerializeUndefinedValues = function () { + assertEquals('{}', angular.toJson({A:undefined})); +}; diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js index f0bb6770..d2d52d47 100644 --- a/test/ResourceSpec.js +++ b/test/ResourceSpec.js @@ -81,6 +81,15 @@ describe("resource", function() { expect(callback).wasCalledWith(ccs); }); + it("should have all arguments optional", function(){ + xhr.expectGET('/CreditCard').respond([{id:1}]); + var log = ''; + var ccs = CreditCard.query(function(){ log += 'cb;'; }); + xhr.flush(); + nakedExpect(ccs).toEqual([{id:1}]); + expect(log).toEqual('cb;'); + }); + it('should delete resource', function(){ xhr.expectDELETE("/CreditCard/123").respond({}); diff --git a/test/angular-mocks.js b/test/angular-mocks.js index 715b4d75..6ae91596 100644 --- a/test/angular-mocks.js +++ b/test/angular-mocks.js @@ -26,16 +26,17 @@ function MockBrowser() { var self = this, expectations = {}, requests = []; + this.isMock = true; self.url = "http://server"; self.watches = []; self.xhr = function(method, url, data, callback) { - if (isFunction(data)) { + if (angular.isFunction(data)) { callback = data; data = null; } - if (data && isObject(data)) data = angular.toJson(data); - if (data && isString(data)) url += "|" + data; + if (data && angular.isObject(data)) data = angular.toJson(data); + if (data && angular.isString(data)) url += "|" + data; var expect = expectations[method] || {}; var response = expect[url]; if (!response) { @@ -48,8 +49,8 @@ function MockBrowser() { self.xhr.expectations = expectations; self.xhr.requests = requests; self.xhr.expect = function(method, url, data) { - if (data && isObject(data)) data = angular.toJson(data); - if (data && isString(data)) url += "|" + data; + if (data && angular.isObject(data)) data = angular.toJson(data); + if (data && angular.isString(data)) url += "|" + data; var expect = expectations[method] || (expectations[method] = {}); return { respond: function(response) { diff --git a/test/directivesSpec.js b/test/directivesSpec.js index eb8a9785..42869a05 100644 --- a/test/directivesSpec.js +++ b/test/directivesSpec.js @@ -44,6 +44,15 @@ describe("directives", function(){ expect(lowercase(element.html())).toEqual('<div>hello</div>'); }); + it('should ng-bind element', function() { + angularFilter.myElement = function() { + return jqLite('<a>hello</a>'); + }; + var scope = compile('<div ng-bind="0|myElement"></div>'); + scope.$eval(); + expect(lowercase(element.html())).toEqual('<a>hello</a>'); + }); + it('should ng-bind-template', function() { var scope = compile('<div ng-bind-template="Hello {{name}}!"></div>'); scope.$set('name', 'Misko'); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index 7b17e150..de2d2b2b 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -149,6 +149,7 @@ describe("service", function(){ expect(scope.$route.current).toEqual(null); scope.$route.when('/NONE', {template:'instant update'}); + scope.$eval(); expect(scope.$route.current.template).toEqual('instant update'); }); }); @@ -187,7 +188,7 @@ describe("service", function(){ describe('bulk', function(){ it('should collect requests', function(){ - scope.$xhr.bulk.url = "/"; + scope.$xhr.bulk.urls["/"] = {match:/.*/}; scope.$xhr.bulk('GET', '/req1', null, callback); scope.$xhr.bulk('POST', '/req2', {post:'data'}, callback); @@ -225,7 +226,11 @@ describe("service", function(){ }); it('should keep track of in flight requests and request only once', function(){ - cache.delegate = scope.$xhr.bulk; + scope.$xhr.bulk.urls['/bulk'] = { + match:function(url){ + return url == '/url'; + } + }; xhr.expectPOST('/bulk', { requests:[{method:'GET', url:'/url', data: null}] }).respond([ diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 572e6a36..4d129f60 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -19,6 +19,7 @@ extend(angular, { 'identity':identity, 'isUndefined': isUndefined, 'isDefined': isDefined, + 'isObject': isObject, 'isString': isString, 'isFunction': isFunction, 'isNumber': isNumber, diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index b38ca2a1..c9665f1e 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -276,9 +276,8 @@ describe("widget", function(){ scope.childScope = createScope(); scope.childScope.name = 'misko'; scope.url = 'myUrl'; - scope.$browser.xhr.expect('GET', 'myUrl').respond('{{name}}'); + scope.$xhr.cache.data.myUrl = {value:'{{name}}'}; scope.$init(); - scope.$browser.xhr.flush(); expect(element.text()).toEqual('misko'); }); }); |
