mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
Merge branch 'master' of github.com:angular/angular.js
This commit is contained in:
commit
f5027cc375
19 changed files with 167 additions and 64 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ extend(angular, {
|
|||
'isDefined': isDefined,
|
||||
'isString': isString,
|
||||
'isFunction': isFunction,
|
||||
'isObject': isObject,
|
||||
'isNumber': isNumber,
|
||||
'isArray': isArray
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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 ?
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
bulkXHR.url = "/bulk";
|
||||
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)();
|
||||
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.urls = {};
|
||||
bulkXHR.flush = function(callback){
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
4
test.sh
4
test.sh
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}]);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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}));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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({});
|
||||
|
||||
|
|
|
|||
11
test/angular-mocks.js
vendored
11
test/angular-mocks.js
vendored
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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([
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ extend(angular, {
|
|||
'identity':identity,
|
||||
'isUndefined': isUndefined,
|
||||
'isDefined': isDefined,
|
||||
'isObject': isObject,
|
||||
'isString': isString,
|
||||
'isFunction': isFunction,
|
||||
'isNumber': isNumber,
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue