Change API angular.compile(element)([scope], [element/true])

This commit is contained in:
Misko Hevery 2011-02-12 10:13:28 -08:00
parent 496e6bf901
commit ef4bb28be1
15 changed files with 516 additions and 453 deletions

View file

@ -8,6 +8,7 @@
recommended way to deal with initializing scope is to put it in the root constructor controller. recommended way to deal with initializing scope is to put it in the root constructor controller.
To migrate simply remove the call to $init() and move any code you had before $init() to the To migrate simply remove the call to $init() and move any code you had before $init() to the
root controller. root controller.
- Change API angular.compile(..) to angular.compile(element)([scope], [element/true])
<a name="0.9.11"><a/> <a name="0.9.11"><a/>

View file

@ -793,21 +793,50 @@ function merge(src, dst) {
* @function * @function
* *
* @description * @description
* Compiles a piece of HTML or DOM into a {@link angular.scope scope} object. * Compiles a piece of HTML string or DOM into a view and produces a linking function, which can
* then be used to link {@link angular.scope scope} and the template together. The compilation
* process walks the DOM tree and tries to match DOM elements to {@link angular.markup markup},
* {@link angular.attrMarkup attrMarkup}, {@link angular.widget widgets}, and
* {@link angular.directive directives}. For each match it executes coresponding markup, \
* attrMarkup, widget or directive template function and collects the instance functions into a
* single linking function which is then returned. The linking function can then be used
* many-times-over on clones of compiled DOM structure, (For example when compiling
* {@link angular.widget.@ng:repeat repeater} the resulting linking function is called once for
* each item in the collection. The `ng:repeat` does this by cloning the template DOM once for
* each item in collection and then calling the linking function to link the cloned template
* with the a new scope for each item in the collection.)
*
<pre> <pre>
var scope1 = angular.compile(window.document); var mvc1 = angular.compile(window.document)();
mvc1.view; // compiled view elment
mvc1.scope; // scope bound to the element
var scope2 = angular.compile('<div ng:click="clicked = true">click me</div>'); var mvc2 = angular.compile('<div ng:click="clicked = true">click me</div>')();
</pre> </pre>
* *
* @param {string|DOMElement} element Element to compile. * @param {string|DOMElement} element Element or HTML to compile into a template function.
* @param {Object=} parentScope Scope to become the parent scope of the newly compiled scope. * @returns {function([scope][, element])} a template function which is used to bind element
* @returns {Object} Compiled scope object. * and scope. Where:
*
* * `scope` - {@link angular.scope scope} A scope to bind to. If none specified, then a new
* root scope is created.
* * `element` - {@link angular.element element} Element to use as the template. If none
* specified then reuse the element from `angular.compile(element)`. If `true`
* then clone the `angular.compile(element)`. The element must be either the same
* element as `angular.compile(element)` or an identical clone to
* `angular.compile(element)`. Using an element with differnt structure will cause
* unpredictable behavior.
*
* Calling the template function returns object: `{scope:?, view:?}`, where:
*
* * `view` - the DOM element which represents the compiled template. Either same or clone of
* `element` specifed in compile or template function.
* * `scope` - scope to which the element is bound to. Either a root scope or scope specified
* in the template function.
*/ */
function compile(element, parentScope) { function compile(element) {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget), return new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget)
$element = jqLite(element); .compile(element);
return compiler.compile($element)($element, parentScope);
} }
///////////////////////////////////////////////// /////////////////////////////////////////////////
@ -989,7 +1018,7 @@ function toKeyValue(obj) {
function angularInit(config){ function angularInit(config){
if (config.autobind) { if (config.autobind) {
// TODO default to the source of angular.js // TODO default to the source of angular.js
var scope = compile(window.document, _null, {'$config':config}), var scope = compile(window.document)(null, createScope({'$config':config})),
$browser = scope.$service('$browser'); $browser = scope.$service('$browser');
if (config.css) if (config.css)

View file

@ -80,30 +80,30 @@ function Compiler(markup, attrMarkup, directives, widgets){
} }
Compiler.prototype = { Compiler.prototype = {
compile: function(element) { compile: function(templateElement) {
element = jqLite(element); templateElement = jqLite(templateElement);
var index = 0, var index = 0,
template, template,
parent = element.parent(); parent = templateElement.parent();
if (parent && parent[0]) { if (parent && parent[0]) {
parent = parent[0]; parent = parent[0];
for(var i = 0; i < parent.childNodes.length; i++) { for(var i = 0; i < parent.childNodes.length; i++) {
if (parent.childNodes[i] == element[0]) { if (parent.childNodes[i] == templateElement[0]) {
index = i; index = i;
} }
} }
} }
template = this.templatize(element, index, 0) || new Template(); template = this.templatize(templateElement, index, 0) || new Template();
return function(element, parentScope){ return function(scope, element){
element = jqLite(element); scope = scope || createScope();
var scope = parentScope && parentScope.$eval element = element === true
? parentScope ? templateElement.cloneNode()
: createScope(parentScope); : (jqLite(element) || templateElement);
element.data($$scope, scope); element.data($$scope, scope);
template.attach(element, scope); template.attach(element, scope);
scope.$element = element; scope.$element = element;
scope.$eval(); scope.$eval();
return scope; return {scope:scope, view:element};
}; };
}, },

View file

@ -676,7 +676,7 @@ angularWidget('ng:include', function(element){
xhr('GET', src, function(code, response){ xhr('GET', src, function(code, response){
element.html(response); element.html(response);
childScope = useScope || createScope(scope); childScope = useScope || createScope(scope);
compiler.compile(element)(element, childScope); compiler.compile(element)(childScope);
scope.$eval(onloadExp); scope.$eval(onloadExp);
}); });
} else { } else {
@ -793,7 +793,7 @@ var ngSwitch = angularWidget('ng:switch', function (element){
var caseElement = switchCase.element.cloneNode(); var caseElement = switchCase.element.cloneNode();
element.append(caseElement); element.append(caseElement);
childScope.$tryEval(switchCase.change, element); childScope.$tryEval(switchCase.change, element);
switchCase.template(caseElement, childScope); switchCase.template(childScope, caseElement);
} }
}); });
}); });
@ -945,7 +945,7 @@ angularWidget("@ng:repeat", function(expression, element){
(index == collectionLength - 1 ? 'last' : 'middle'); (index == collectionLength - 1 ? 'last' : 'middle');
lastElement.after(cloneElement = element.cloneNode()); lastElement.after(cloneElement = element.cloneNode());
cloneElement.attr('ng:repeat-index', index); cloneElement.attr('ng:repeat-index', index);
linker(cloneElement, childScope); linker(childScope, cloneElement);
children.push(childScope); children.push(childScope);
} }
childScope.$eval(); childScope.$eval();
@ -1067,7 +1067,7 @@ angularWidget('ng:view', function(element) {
if (src) { if (src) {
$xhr('GET', src, function(code, response){ $xhr('GET', src, function(code, response){
element.html(response); element.html(response);
compiler.compile(element)(element, childScope); compiler.compile(element)(childScope);
}); });
} else { } else {
element.html(''); element.html('');

View file

@ -1,350 +1,389 @@
beforeEach(function(){ describe('angular', function(){
compileCache = {}; describe('case', function(){
}); it('should change case', function(){
expect(lowercase('ABC90')).toEqual('abc90');
describe('case', function(){ expect(manualLowercase('ABC90')).toEqual('abc90');
it('should change case', function(){ expect(uppercase('abc90')).toEqual('ABC90');
expect(lowercase('ABC90')).toEqual('abc90'); expect(manualUppercase('abc90')).toEqual('ABC90');
expect(manualLowercase('ABC90')).toEqual('abc90'); });
expect(uppercase('abc90')).toEqual('ABC90');
expect(manualUppercase('abc90')).toEqual('ABC90');
});
});
describe("copy", function(){
it("should return same object", function (){
var obj = {};
var arr = [];
expect(copy({}, obj)).toBe(obj);
expect(copy([], arr)).toBe(arr);
}); });
it("should copy Date", function(){ describe("copy", function(){
var date = new Date(123); it("should return same object", function (){
expect(copy(date) instanceof Date).toBeTruthy(); var obj = {};
expect(copy(date).getTime()).toEqual(123); var arr = [];
expect(copy(date) === date).toBeFalsy(); expect(copy({}, obj)).toBe(obj);
expect(copy([], arr)).toBe(arr);
});
it("should copy Date", function(){
var date = new Date(123);
expect(copy(date) instanceof Date).toBeTruthy();
expect(copy(date).getTime()).toEqual(123);
expect(copy(date) === date).toBeFalsy();
});
it("should copy array", function(){
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
expect(copy(src, dst)).toBe(dst);
expect(dst).toEqual([1, {name:"value"}]);
expect(dst[1]).toEqual({name:"value"});
expect(dst[1]).not.toBe(src[1]);
});
it('should copy empty array', function() {
var src = [];
var dst = [{key: "v"}];
expect(copy(src, dst)).toEqual([]);
expect(dst).toEqual([]);
});
it("should copy object", function(){
var src = {a:{name:"value"}};
var dst = {b:{key:"v"}};
expect(copy(src, dst)).toBe(dst);
expect(dst).toEqual({a:{name:"value"}});
expect(dst.a).toEqual(src.a);
expect(dst.a).not.toBe(src.a);
});
it("should copy primitives", function(){
expect(copy(null)).toEqual(null);
expect(copy('')).toBe('');
expect(copy('lala')).toBe('lala');
expect(copy(123)).toEqual(123);
expect(copy([{key:null}])).toEqual([{key:null}]);
});
}); });
it("should copy array", function(){ describe('equals', function(){
var src = [1, {name:"value"}]; it('should return true if same object', function(){
var dst = [{key:"v"}]; var o = {};
expect(copy(src, dst)).toBe(dst); expect(equals(o, o)).toEqual(true);
expect(dst).toEqual([1, {name:"value"}]); expect(equals(1, '1')).toEqual(true);
expect(dst[1]).toEqual({name:"value"}); expect(equals(1, '2')).toEqual(false);
expect(dst[1]).not.toBe(src[1]); });
it('should recurse into object', function(){
expect(equals({}, {})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko'})).toEqual(true);
expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false);
expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false);
expect(equals({name:'misko'}, {name:'adam'})).toEqual(false);
expect(equals(['misko'], ['misko'])).toEqual(true);
expect(equals(['misko'], ['adam'])).toEqual(false);
expect(equals(['misko'], ['misko', 'adam'])).toEqual(false);
});
it('should ignore $ member variables', function(){
expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true);
});
it('should ignore functions', function(){
expect(equals({func: function() {}}, {bar: function() {}})).toEqual(true);
});
it('should work well with nulls', function() {
expect(equals(null, '123')).toBe(false);
expect(equals('123', null)).toBe(false);
var obj = {foo:'bar'};
expect(equals(null, obj)).toBe(false);
expect(equals(obj, null)).toBe(false);
expect(equals(null, null)).toBe(true);
});
it('should work well with undefined', function() {
expect(equals(undefined, '123')).toBe(false);
expect(equals('123', undefined)).toBe(false);
var obj = {foo:'bar'};
expect(equals(undefined, obj)).toBe(false);
expect(equals(obj, undefined)).toBe(false);
expect(equals(undefined, undefined)).toBe(true);
});
}); });
it('should copy empty array', function() { describe('parseKeyValue', function() {
var src = []; it('should parse a string into key-value pairs', function() {
var dst = [{key: "v"}]; expect(parseKeyValue('')).toEqual({});
expect(copy(src, dst)).toEqual([]); expect(parseKeyValue('simple=pair')).toEqual({simple: 'pair'});
expect(dst).toEqual([]); expect(parseKeyValue('first=1&second=2')).toEqual({first: '1', second: '2'});
}); expect(parseKeyValue('escaped%20key=escaped%20value')).
it("should copy object", function(){
var src = {a:{name:"value"}};
var dst = {b:{key:"v"}};
expect(copy(src, dst)).toBe(dst);
expect(dst).toEqual({a:{name:"value"}});
expect(dst.a).toEqual(src.a);
expect(dst.a).not.toBe(src.a);
});
it("should copy primitives", function(){
expect(copy(null)).toEqual(null);
expect(copy('')).toBe('');
expect(copy('lala')).toBe('lala');
expect(copy(123)).toEqual(123);
expect(copy([{key:null}])).toEqual([{key:null}]);
});
});
describe('equals', function(){
it('should return true if same object', function(){
var o = {};
expect(equals(o, o)).toEqual(true);
expect(equals(1, '1')).toEqual(true);
expect(equals(1, '2')).toEqual(false);
});
it('should recurse into object', function(){
expect(equals({}, {})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko'})).toEqual(true);
expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false);
expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false);
expect(equals({name:'misko'}, {name:'adam'})).toEqual(false);
expect(equals(['misko'], ['misko'])).toEqual(true);
expect(equals(['misko'], ['adam'])).toEqual(false);
expect(equals(['misko'], ['misko', 'adam'])).toEqual(false);
});
it('should ignore $ member variables', function(){
expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true);
expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true);
});
it('should ignore functions', function(){
expect(equals({func: function() {}}, {bar: function() {}})).toEqual(true);
});
it('should work well with nulls', function() {
expect(equals(null, '123')).toBe(false);
expect(equals('123', null)).toBe(false);
var obj = {foo:'bar'};
expect(equals(null, obj)).toBe(false);
expect(equals(obj, null)).toBe(false);
expect(equals(null, null)).toBe(true);
});
it('should work well with undefined', function() {
expect(equals(undefined, '123')).toBe(false);
expect(equals('123', undefined)).toBe(false);
var obj = {foo:'bar'};
expect(equals(undefined, obj)).toBe(false);
expect(equals(obj, undefined)).toBe(false);
expect(equals(undefined, undefined)).toBe(true);
});
});
describe('parseKeyValue', function() {
it('should parse a string into key-value pairs', function() {
expect(parseKeyValue('')).toEqual({});
expect(parseKeyValue('simple=pair')).toEqual({simple: 'pair'});
expect(parseKeyValue('first=1&second=2')).toEqual({first: '1', second: '2'});
expect(parseKeyValue('escaped%20key=escaped%20value')).
toEqual({'escaped key': 'escaped value'}); toEqual({'escaped key': 'escaped value'});
expect(parseKeyValue('emptyKey=')).toEqual({emptyKey: ''}); expect(parseKeyValue('emptyKey=')).toEqual({emptyKey: ''});
expect(parseKeyValue('flag1&key=value&flag2')). expect(parseKeyValue('flag1&key=value&flag2')).
toEqual({flag1: true, key: 'value', flag2: true}); toEqual({flag1: true, key: 'value', flag2: true});
});
}); });
});
describe('toKeyValue', function() { describe('toKeyValue', function() {
it('should parse key-value pairs into string', function() { it('should parse key-value pairs into string', function() {
expect(toKeyValue({})).toEqual(''); expect(toKeyValue({})).toEqual('');
expect(toKeyValue({simple: 'pair'})).toEqual('simple=pair'); expect(toKeyValue({simple: 'pair'})).toEqual('simple=pair');
expect(toKeyValue({first: '1', second: '2'})).toEqual('first=1&second=2'); expect(toKeyValue({first: '1', second: '2'})).toEqual('first=1&second=2');
expect(toKeyValue({'escaped key': 'escaped value'})). expect(toKeyValue({'escaped key': 'escaped value'})).
toEqual('escaped%20key=escaped%20value'); toEqual('escaped%20key=escaped%20value');
expect(toKeyValue({emptyKey: ''})).toEqual('emptyKey='); expect(toKeyValue({emptyKey: ''})).toEqual('emptyKey=');
});
it('should parse true values into flags', function() {
expect(toKeyValue({flag1: true, key: 'value', flag2: true})).toEqual('flag1&key=value&flag2');
});
}); });
it('should parse true values into flags', function() {
expect(toKeyValue({flag1: true, key: 'value', flag2: true})).toEqual('flag1&key=value&flag2'); describe ('rngScript', function() {
}); it('should match angular.js', function() {
}); expect('angular.js'.match(rngScript)).not.toBeNull();
expect('../angular.js'.match(rngScript)).not.toBeNull();
expect('foo/angular.js'.match(rngScript)).not.toBeNull();
describe ('rngScript', function() {
it('should match angular.js', function() { expect('foo.js'.match(rngScript)).toBeNull();
expect('angular.js'.match(rngScript)).not.toBeNull(); expect('foo/foo.js'.match(rngScript)).toBeNull();
expect('../angular.js'.match(rngScript)).not.toBeNull(); expect('my-angular-app.js'.match(rngScript)).toBeNull();
expect('foo/angular.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app.js'.match(rngScript)).toBeNull();
});
expect('foo.js'.match(rngScript)).toBeNull();
expect('foo/foo.js'.match(rngScript)).toBeNull(); it('should match angular.min.js', function() {
expect('my-angular-app.js'.match(rngScript)).toBeNull(); expect('angular.min.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app.js'.match(rngScript)).toBeNull(); expect('../angular.min.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular.min.js'.match(rngScript)).not.toBeNull();
it('should match angular.min.js', function() { expect('my-angular-app.min.js'.match(rngScript)).toBeNull();
expect('angular.min.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app.min.js'.match(rngScript)).toBeNull();
expect('../angular.min.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-bootstrap.js', function() {
expect('my-angular-app.min.js'.match(rngScript)).toBeNull(); expect('angular-bootstrap.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app.min.js'.match(rngScript)).toBeNull(); expect('../angular-bootstrap.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular-bootstrap.js'.match(rngScript)).not.toBeNull();
it('should match angular-bootstrap.js', function() { expect('my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
expect('angular-bootstrap.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
expect('../angular-bootstrap.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular-bootstrap.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0.js', function() {
expect('my-angular-app-bootstrap.js'.match(rngScript)).toBeNull(); expect('angular-0.9.0.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app-bootstrap.js'.match(rngScript)).toBeNull(); expect('../angular-0.9.0.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular-0.9.0.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0.js', function() { expect('my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
expect('angular-0.9.0.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
expect('../angular-0.9.0.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular-0.9.0.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0.min.js', function() {
expect('my-angular-app-0.9.0.js'.match(rngScript)).toBeNull(); expect('angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app-0.9.0.js'.match(rngScript)).toBeNull(); expect('../angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0.min.js', function() { expect('my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
expect('angular-0.9.0.min.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
expect('../angular-0.9.0.min.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0-de0a8612.js', function() {
expect('my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull(); expect('angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull(); expect('../angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0-de0a8612.js', function() { expect('my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
expect('angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
expect('../angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0-de0a8612.min.js', function() {
expect('my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull(); expect('angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull(); expect('../angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
}); expect('foo/angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-0.9.0-de0a8612.min.js', function() { expect('my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
expect('angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull(); expect('foo/../my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
expect('../angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull(); });
expect('foo/angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-scenario.js', function() {
expect('my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull(); expect('angular-scenario.js'.match(rngScript)).not.toBeNull();
expect('foo/../my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull(); expect('angular-scenario.min.js'.match(rngScript)).not.toBeNull();
}); expect('../angular-scenario.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-scenario.js', function() { });
expect('angular-scenario.js'.match(rngScript)).not.toBeNull();
expect('angular-scenario.min.js'.match(rngScript)).not.toBeNull(); it('should match angular-scenario-0.9.0(.min).js', function() {
expect('../angular-scenario.js'.match(rngScript)).not.toBeNull(); expect('angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario.min.js'.match(rngScript)).not.toBeNull(); expect('angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
}); expect('../angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-scenario-0.9.0(.min).js', function() { });
expect('angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
expect('angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull(); it('should match angular-scenario-0.9.0-de0a8612(.min).js', function() {
expect('../angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull(); expect('angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull(); expect('angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
}); expect('../angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
it('should match angular-scenario-0.9.0-de0a8612(.min).js', function() { });
expect('angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull(); });
expect('angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
expect('../angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
expect('foo/angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull(); describe('angularJsConfig', function() {
}); it('should find angular.js script tag and config', function() {
}); var doc = { getElementsByTagName: function(tagName) {
expect(tagName).toEqual('script');
return [{nodeName: 'SCRIPT', src: 'random.js'},
describe('angularJsConfig', function() { {nodeName: 'SCRIPT', src: 'angular.js'},
it('should find angular.js script tag and config', function() { {nodeName: 'SCRIPT', src: 'my-angular-app.js'}];
var doc = { getElementsByTagName: function(tagName) { }
expect(tagName).toEqual('script'); };
return [{nodeName: 'SCRIPT', src: 'random.js'},
{nodeName: 'SCRIPT', src: 'angular.js'}, expect(angularJsConfig(doc)).toEqual({base_url: '',
{nodeName: 'SCRIPT', src: 'my-angular-app.js'}]; ie_compat: 'angular-ie-compat.js',
} ie_compat_id: 'ng-ie-compat'});
}; });
expect(angularJsConfig(doc)).toEqual({base_url: '',
ie_compat: 'angular-ie-compat.js', it('should extract angular config from the ng: attributes', function() {
ie_compat_id: 'ng-ie-compat'}); var doc = { getElementsByTagName: function(tagName) {
}); expect(lowercase(tagName)).toEqual('script');
return [{nodeName: 'SCRIPT',
src: 'angularjs/angular.js',
it('should extract angular config from the ng: attributes', function() { attributes: [{name: 'ng:autobind', value:undefined},
var doc = { getElementsByTagName: function(tagName) { {name: 'ng:css', value: 'css/my_custom_angular.css'},
expect(lowercase(tagName)).toEqual('script'); {name: 'ng:ie-compat', value: 'myjs/angular-ie-compat.js'},
return [{nodeName: 'SCRIPT', {name: 'ng:ie-compat-id', value: 'ngcompat'}] }];
src: 'angularjs/angular.js', }};
attributes: [{name: 'ng:autobind', value:undefined},
{name: 'ng:css', value: 'css/my_custom_angular.css'}, expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
{name: 'ng:ie-compat', value: 'myjs/angular-ie-compat.js'}, autobind: true,
{name: 'ng:ie-compat-id', value: 'ngcompat'}] }]; css: 'css/my_custom_angular.css',
}}; ie_compat: 'myjs/angular-ie-compat.js',
ie_compat_id: 'ngcompat'});
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/', });
autobind: true,
css: 'css/my_custom_angular.css',
ie_compat: 'myjs/angular-ie-compat.js', it('should extract angular autobind config from the script hashpath attributes', function() {
ie_compat_id: 'ngcompat'}); var doc = { getElementsByTagName: function(tagName) {
}); expect(lowercase(tagName)).toEqual('script');
return [{nodeName: 'SCRIPT',
src: 'angularjs/angular.js#autobind'}];
it('should extract angular autobind config from the script hashpath attributes', function() { }};
var doc = { getElementsByTagName: function(tagName) {
expect(lowercase(tagName)).toEqual('script'); expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
return [{nodeName: 'SCRIPT', autobind: true,
src: 'angularjs/angular.js#autobind'}]; ie_compat: 'angularjs/angular-ie-compat.js',
}}; ie_compat_id: 'ng-ie-compat'});
});
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
autobind: true,
ie_compat: 'angularjs/angular-ie-compat.js', it("should default to versioned ie-compat file if angular file is versioned", function() {
ie_compat_id: 'ng-ie-compat'}); var doc = { getElementsByTagName: function(tagName) {
}); expect(lowercase(tagName)).toEqual('script');
return [{nodeName: 'SCRIPT',
src: 'js/angular-0.9.0.js'}];
it("should default to versioned ie-compat file if angular file is versioned", function() { }};
var doc = { getElementsByTagName: function(tagName) {
expect(lowercase(tagName)).toEqual('script'); expect(angularJsConfig(doc)).toEqual({base_url: 'js/',
return [{nodeName: 'SCRIPT', ie_compat: 'js/angular-ie-compat-0.9.0.js',
src: 'js/angular-0.9.0.js'}]; ie_compat_id: 'ng-ie-compat'});
}}; });
expect(angularJsConfig(doc)).toEqual({base_url: 'js/',
ie_compat: 'js/angular-ie-compat-0.9.0.js', it("should default to versioned ie-compat file if angular file is versioned and minified", function() {
ie_compat_id: 'ng-ie-compat'}); var doc = { getElementsByTagName: function(tagName) {
}); expect(lowercase(tagName)).toEqual('script');
return [{nodeName: 'SCRIPT',
src: 'js/angular-0.9.0-cba23f00.min.js'}];
it("should default to versioned ie-compat file if angular file is versioned and minified", function() { }};
var doc = { getElementsByTagName: function(tagName) {
expect(lowercase(tagName)).toEqual('script'); expect(angularJsConfig(doc)).toEqual({base_url: 'js/',
return [{nodeName: 'SCRIPT', ie_compat: 'js/angular-ie-compat-0.9.0-cba23f00.js',
src: 'js/angular-0.9.0-cba23f00.min.js'}]; ie_compat_id: 'ng-ie-compat'});
}}; });
});
expect(angularJsConfig(doc)).toEqual({base_url: 'js/',
ie_compat: 'js/angular-ie-compat-0.9.0-cba23f00.js',
ie_compat_id: 'ng-ie-compat'}); describe('angular service', function() {
}); it('should override services', function() {
}); var scope = createScope();
angular.service('fake', function() { return 'old'; });
angular.service('fake', function() { return 'new'; });
describe('angular service', function() {
it('should override services', function() { expect(scope.$service('fake')).toEqual('new');
var scope = createScope(); });
angular.service('fake', function() { return 'old'; });
angular.service('fake', function() { return 'new'; }); it('should not preserve properties on override', function() {
angular.service('fake', {$one: true}, {$two: true}, {three: true});
expect(scope.$service('fake')).toEqual('new'); var result = angular.service('fake', {$four: true});
});
expect(result.$one).toBeUndefined();
it('should not preserve properties on override', function() { expect(result.$two).toBeUndefined();
angular.service('fake', {$one: true}, {$two: true}, {three: true}); expect(result.three).toBeUndefined();
var result = angular.service('fake', {$four: true}); expect(result.$four).toBe(true);
});
expect(result.$one).toBeUndefined();
expect(result.$two).toBeUndefined(); it('should not preserve non-angular properties on override', function() {
expect(result.three).toBeUndefined(); angular.service('fake', {one: true}, {two: true});
expect(result.$four).toBe(true); var result = angular.service('fake', {third: true});
});
expect(result.one).not.toBeDefined();
it('should not preserve non-angular properties on override', function() { expect(result.two).not.toBeDefined();
angular.service('fake', {one: true}, {two: true}); expect(result.third).toBeTruthy();
var result = angular.service('fake', {third: true}); });
});
expect(result.one).not.toBeDefined();
expect(result.two).not.toBeDefined(); describe('isDate', function() {
expect(result.third).toBeTruthy(); it('should return true for Date object', function() {
}); expect(isDate(new Date())).toBe(true);
}); });
describe('isDate', function() { it('should return false for non Date objects', function() {
it('should return true for Date object', function() { expect(isDate([])).toBe(false);
expect(isDate(new Date())).toBe(true); expect(isDate('')).toBe(false);
}); expect(isDate(23)).toBe(false);
expect(isDate({})).toBe(false);
it('should return false for non Date objects', function() { });
expect(isDate([])).toBe(false); });
expect(isDate('')).toBe(false);
expect(isDate(23)).toBe(false); describe('compile', function(){
expect(isDate({})).toBe(false); var mvc;
afterEach(function(){
dealoc(mvc.view);
});
it('should link to existing node and create scope', function(){
mvc = angular.compile('<div>{{greeting = "hello world"}}</div>')();
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.scope.greeting).toEqual('hello world');
});
it('should link to existing node and given scope', function(){
var scope = angular.scope();
mvc = angular.compile('<div>{{greeting = "hello world"}}</div>')(scope);
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.scope).toEqual(scope);
});
it('should link to new node and given scope', function(){
var scope = angular.scope();
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
var templateFn = angular.compile(template);
var templateClone = template.cloneNode();
mvc = templateFn(scope, templateClone);
expect(template.text()).toEqual('');
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.view).toEqual(templateClone);
expect(mvc.scope.greeting).toEqual('hello world');
});
it('should link to cloned node and create scope', function(){
var scope = angular.scope();
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
mvc = angular.compile(template)(scope, true);
expect(template.text()).toEqual('');
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.scope.greeting).toEqual('hello world');
});
}); });
}); });

View file

@ -4,7 +4,6 @@ describe('Binder', function(){
var self = this; var self = this;
this.compile = function(html, parent) { this.compile = function(html, parent) {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
if (self.element) dealoc(self.element); if (self.element) dealoc(self.element);
var element; var element;
if (parent) { if (parent) {
@ -14,11 +13,10 @@ describe('Binder', function(){
element = jqLite(html); element = jqLite(html);
} }
self.element = element; self.element = element;
var scope = compiler.compile(element)(element); return angular.compile(element)();
return {node:element, scope:scope};
}; };
this.compileToHtml = function (content) { this.compileToHtml = function (content) {
return sortedHtml(this.compile(content).node); return sortedHtml(this.compile(content).view);
}; };
}); });
@ -92,7 +90,7 @@ describe('Binder', function(){
var form = this.compile('<div ng:bind="model.a">x</div>'); var form = this.compile('<div ng:bind="model.a">x</div>');
form.scope.$set('model', {a:123}); form.scope.$set('model', {a:123});
form.scope.$eval(); form.scope.$eval();
assertEquals('123', form.node.text()); assertEquals('123', form.view.text());
}); });
it('ReplaceBindingInTextWithSpan', function(){ it('ReplaceBindingInTextWithSpan', function(){
@ -115,7 +113,7 @@ describe('Binder', function(){
it('BindingOfAttributes', function(){ it('BindingOfAttributes', function(){
var c = this.compile("<a href='http://s/a{{b}}c' foo='x'></a>"); var c = this.compile("<a href='http://s/a{{b}}c' foo='x'></a>");
var attrbinding = c.node.attr("ng:bind-attr"); var attrbinding = c.view.attr("ng:bind-attr");
var bindings = fromJson(attrbinding); var bindings = fromJson(attrbinding);
assertEquals("http://s/a{{b}}c", decodeURI(bindings.href)); assertEquals("http://s/a{{b}}c", decodeURI(bindings.href));
assertTrue(!bindings.foo); assertTrue(!bindings.foo);
@ -123,7 +121,7 @@ describe('Binder', function(){
it('MarkMultipleAttributes', function(){ it('MarkMultipleAttributes', function(){
var c = this.compile('<a href="http://s/a{{b}}c" foo="{{d}}"></a>'); var c = this.compile('<a href="http://s/a{{b}}c" foo="{{d}}"></a>');
var attrbinding = c.node.attr("ng:bind-attr"); var attrbinding = c.view.attr("ng:bind-attr");
var bindings = fromJson(attrbinding); var bindings = fromJson(attrbinding);
assertEquals(bindings.foo, "{{d}}"); assertEquals(bindings.foo, "{{d}}");
assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c"); assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c");
@ -131,23 +129,23 @@ describe('Binder', function(){
it('AttributesNoneBound', function(){ it('AttributesNoneBound', function(){
var c = this.compile("<a href='abc' foo='def'></a>"); var c = this.compile("<a href='abc' foo='def'></a>");
var a = c.node; var a = c.view;
assertEquals(a[0].nodeName, "A"); assertEquals(a[0].nodeName, "A");
assertTrue(!a.attr("ng:bind-attr")); assertTrue(!a.attr("ng:bind-attr"));
}); });
it('ExistingAttrbindingIsAppended', function(){ it('ExistingAttrbindingIsAppended', function(){
var c = this.compile("<a href='http://s/{{abc}}' ng:bind-attr='{\"b\":\"{{def}}\"}'></a>"); var c = this.compile("<a href='http://s/{{abc}}' ng:bind-attr='{\"b\":\"{{def}}\"}'></a>");
var a = c.node; var a = c.view;
assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng:bind-attr')); assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng:bind-attr'));
}); });
it('AttributesAreEvaluated', function(){ it('AttributesAreEvaluated', function(){
var c = this.compile('<a ng:bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>'); var c = this.compile('<a ng:bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>');
var binder = c.binder, form = c.node; var binder = c.binder, form = c.view;
c.scope.$eval('a=1;b=2'); c.scope.$eval('a=1;b=2');
c.scope.$eval(); c.scope.$eval();
var a = c.node; var a = c.view;
assertEquals(a.attr('a'), 'a'); assertEquals(a.attr('a'), 'a');
assertEquals(a.attr('b'), 'a+b=3'); assertEquals(a.attr('b'), 'a+b=3');
}); });
@ -158,7 +156,7 @@ describe('Binder', function(){
c.scope.$set("person.save", function(){ c.scope.$set("person.save", function(){
savedCalled = true; savedCalled = true;
}); });
browserTrigger(c.node, 'click'); browserTrigger(c.view, 'click');
assertTrue(savedCalled); assertTrue(savedCalled);
}); });
@ -169,7 +167,7 @@ describe('Binder', function(){
log += 'click;'; log += 'click;';
}); });
expect(log).toEqual(''); expect(log).toEqual('');
browserTrigger(c.node, 'click'); browserTrigger(c.view, 'click');
expect(log).toEqual('click;'); expect(log).toEqual('click;');
}); });
@ -179,13 +177,13 @@ describe('Binder', function(){
c.scope.$set("person.save", function(){ c.scope.$set("person.save", function(){
savedCalled = true; savedCalled = true;
}); });
browserTrigger(c.node, 'click'); browserTrigger(c.view, 'click');
assertTrue(savedCalled); assertTrue(savedCalled);
}); });
it('RepeaterUpdateBindings', function(){ it('RepeaterUpdateBindings', function(){
var a = this.compile('<ul><LI ng:repeat="item in model.items" ng:bind="item.a"/></ul>'); var a = this.compile('<ul><LI ng:repeat="item in model.items" ng:bind="item.a"/></ul>');
var form = a.node; var form = a.view;
var items = [{a:"A"}, {a:"B"}]; var items = [{a:"A"}, {a:"B"}];
a.scope.$set('model', {items:items}); a.scope.$set('model', {items:items});
@ -225,7 +223,7 @@ describe('Binder', function(){
assertEquals('<ul>' + assertEquals('<ul>' +
'<#comment></#comment>' + '<#comment></#comment>' +
'<li ng:repeat-index="0"><span ng:bind="item.a">A</span></li>' + '<li ng:repeat-index="0"><span ng:bind="item.a">A</span></li>' +
'</ul>', sortedHtml(a.node)); '</ul>', sortedHtml(a.view));
}); });
it('ExpandEntityTag', function(){ it('ExpandEntityTag', function(){
@ -241,11 +239,11 @@ describe('Binder', function(){
it('RepeaterAdd', function(){ it('RepeaterAdd', function(){
var c = this.compile('<div><input type="text" name="item.x" ng:repeat="item in items"></div>'); var c = this.compile('<div><input type="text" name="item.x" ng:repeat="item in items"></div>');
var doc = c.node; var doc = c.view;
c.scope.$set('items', [{x:'a'}, {x:'b'}]); c.scope.$set('items', [{x:'a'}, {x:'b'}]);
c.scope.$eval(); c.scope.$eval();
var first = childNode(c.node, 1); var first = childNode(c.view, 1);
var second = childNode(c.node, 2); var second = childNode(c.view, 2);
assertEquals('a', first.val()); assertEquals('a', first.val());
assertEquals('b', second.val()); assertEquals('b', second.val());
@ -261,20 +259,20 @@ describe('Binder', function(){
c.scope.$set("items", items); c.scope.$set("items", items);
c.scope.$eval(); c.scope.$eval();
expect(c.node[0].childNodes.length - 1).toEqual(0); expect(c.view[0].childNodes.length - 1).toEqual(0);
items.name = "misko"; items.name = "misko";
c.scope.$eval(); c.scope.$eval();
expect(c.node[0].childNodes.length - 1).toEqual(1); expect(c.view[0].childNodes.length - 1).toEqual(1);
delete items.name; delete items.name;
c.scope.$eval(); c.scope.$eval();
expect(c.node[0].childNodes.length - 1).toEqual(0); expect(c.view[0].childNodes.length - 1).toEqual(0);
}); });
it('IfTextBindingThrowsErrorDecorateTheSpan', function(){ it('IfTextBindingThrowsErrorDecorateTheSpan', function(){
var a = this.compile('<div>{{error.throw()}}</div>'); var a = this.compile('<div>{{error.throw()}}</div>');
var doc = a.node; var doc = a.view;
var errorLogs = a.scope.$service('$log').error.logs; var errorLogs = a.scope.$service('$log').error.logs;
a.scope.$set('error.throw', function(){throw "ErrorMsg1";}); a.scope.$set('error.throw', function(){throw "ErrorMsg1";});
@ -303,7 +301,7 @@ describe('Binder', function(){
it('IfAttrBindingThrowsErrorDecorateTheAttribute', function(){ it('IfAttrBindingThrowsErrorDecorateTheAttribute', function(){
var a = this.compile('<div attr="before {{error.throw()}} after"></div>'); var a = this.compile('<div attr="before {{error.throw()}} after"></div>');
var doc = a.node; var doc = a.view;
var errorLogs = a.scope.$service('$log').error.logs; var errorLogs = a.scope.$service('$log').error.logs;
a.scope.$set('error.throw', function(){throw "ErrorMsg";}); a.scope.$set('error.throw', function(){throw "ErrorMsg";});
@ -340,7 +338,7 @@ describe('Binder', function(){
'<#comment></#comment>'+ '<#comment></#comment>'+
'<ul name="b1" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="0"></ul>'+ '<ul name="b1" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="0"></ul>'+
'<ul name="b2" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="1"></ul>'+ '<ul name="b2" ng:bind-attr="{"name":"{{i}}"}" ng:repeat-index="1"></ul>'+
'</div></div>', sortedHtml(a.node)); '</div></div>', sortedHtml(a.view));
}); });
it('HideBindingExpression', function(){ it('HideBindingExpression', function(){
@ -349,12 +347,12 @@ describe('Binder', function(){
a.scope.$set('hidden', 3); a.scope.$set('hidden', 3);
a.scope.$eval(); a.scope.$eval();
assertHidden(a.node); assertHidden(a.view);
a.scope.$set('hidden', 2); a.scope.$set('hidden', 2);
a.scope.$eval(); a.scope.$eval();
assertVisible(a.node); assertVisible(a.view);
}); });
it('HideBinding', function(){ it('HideBinding', function(){
@ -363,17 +361,17 @@ describe('Binder', function(){
c.scope.$set('hidden', 'true'); c.scope.$set('hidden', 'true');
c.scope.$eval(); c.scope.$eval();
assertHidden(c.node); assertHidden(c.view);
c.scope.$set('hidden', 'false'); c.scope.$set('hidden', 'false');
c.scope.$eval(); c.scope.$eval();
assertVisible(c.node); assertVisible(c.view);
c.scope.$set('hidden', ''); c.scope.$set('hidden', '');
c.scope.$eval(); c.scope.$eval();
assertVisible(c.node); assertVisible(c.view);
}); });
it('ShowBinding', function(){ it('ShowBinding', function(){
@ -382,17 +380,17 @@ describe('Binder', function(){
c.scope.$set('show', 'true'); c.scope.$set('show', 'true');
c.scope.$eval(); c.scope.$eval();
assertVisible(c.node); assertVisible(c.view);
c.scope.$set('show', 'false'); c.scope.$set('show', 'false');
c.scope.$eval(); c.scope.$eval();
assertHidden(c.node); assertHidden(c.view);
c.scope.$set('show', ''); c.scope.$set('show', '');
c.scope.$eval(); c.scope.$eval();
assertHidden(c.node); assertHidden(c.view);
}); });
it('BindClassUndefined', function(){ it('BindClassUndefined', function(){
@ -401,7 +399,7 @@ describe('Binder', function(){
assertEquals( assertEquals(
'<div class="undefined" ng:class="undefined"></div>', '<div class="undefined" ng:class="undefined"></div>',
sortedHtml(doc.node)); sortedHtml(doc.view));
}); });
it('BindClass', function(){ it('BindClass', function(){
@ -410,26 +408,26 @@ describe('Binder', function(){
c.scope.$set('class', 'testClass'); c.scope.$set('class', 'testClass');
c.scope.$eval(); c.scope.$eval();
assertEquals('<div class="testClass" ng:class="class"></div>', sortedHtml(c.node)); assertEquals('<div class="testClass" ng:class="class"></div>', sortedHtml(c.view));
c.scope.$set('class', ['a', 'b']); c.scope.$set('class', ['a', 'b']);
c.scope.$eval(); c.scope.$eval();
assertEquals('<div class="a b" ng:class="class"></div>', sortedHtml(c.node)); assertEquals('<div class="a b" ng:class="class"></div>', sortedHtml(c.view));
}); });
it('BindClassEvenOdd', function(){ it('BindClassEvenOdd', function(){
var x = this.compile('<div><div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"/></div>'); var x = this.compile('<div><div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"/></div>');
x.scope.$eval(); x.scope.$eval();
var d1 = jqLite(x.node[0].childNodes[1]); var d1 = jqLite(x.view[0].childNodes[1]);
var d2 = jqLite(x.node[0].childNodes[2]); var d2 = jqLite(x.view[0].childNodes[2]);
expect(d1.hasClass('o')).toBeTruthy(); expect(d1.hasClass('o')).toBeTruthy();
expect(d2.hasClass('e')).toBeTruthy(); expect(d2.hasClass('e')).toBeTruthy();
assertEquals( assertEquals(
'<div><#comment></#comment>' + '<div><#comment></#comment>' +
'<div class="o" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="0"></div>' + '<div class="o" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="0"></div>' +
'<div class="e" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="1"></div></div>', '<div class="e" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat-index="1"></div></div>',
sortedHtml(x.node)); sortedHtml(x.view));
}); });
it('BindStyle', function(){ it('BindStyle', function(){
@ -438,7 +436,7 @@ describe('Binder', function(){
c.scope.$eval('style={color:"red"}'); c.scope.$eval('style={color:"red"}');
c.scope.$eval(); c.scope.$eval();
assertEquals("red", c.node.css('color')); assertEquals("red", c.view.css('color'));
c.scope.$eval('style={}'); c.scope.$eval('style={}');
c.scope.$eval(); c.scope.$eval();
@ -449,7 +447,7 @@ describe('Binder', function(){
c.scope.action = function(){ c.scope.action = function(){
throw new Error('MyError'); throw new Error('MyError');
}; };
var input = c.node; var input = c.view;
browserTrigger(input, 'click'); browserTrigger(input, 'click');
var error = input.attr('ng-exception'); var error = input.attr('ng-exception');
assertTrue(!!error.match(/MyError/)); assertTrue(!!error.match(/MyError/));
@ -470,14 +468,14 @@ describe('Binder', function(){
"<div ng:non-bindable='true'>{{c}}</div></div>"); "<div ng:non-bindable='true'>{{c}}</div></div>");
c.scope.$set('a', 123); c.scope.$set('a', 123);
c.scope.$eval(); c.scope.$eval();
assertEquals('123{{a}}{{b}}{{c}}', c.node.text()); assertEquals('123{{a}}{{b}}{{c}}', c.view.text());
}); });
it('OptionShouldUpdateParentToGetProperBinding', function(){ it('OptionShouldUpdateParentToGetProperBinding', function(){
var c = this.compile('<select name="s"><option ng:repeat="i in [0,1]" value="{{i}}" ng:bind="i"></option></select>'); var c = this.compile('<select name="s"><option ng:repeat="i in [0,1]" value="{{i}}" ng:bind="i"></option></select>');
c.scope.$set('s', 1); c.scope.$set('s', 1);
c.scope.$eval(); c.scope.$eval();
assertEquals(1, c.node[0].selectedIndex); assertEquals(1, c.view[0].selectedIndex);
}); });
it('RepeaterShouldBindInputsDefaults', function () { it('RepeaterShouldBindInputsDefaults', function () {
@ -494,7 +492,7 @@ describe('Binder', function(){
c.scope.$set("name", "World"); c.scope.$set("name", "World");
c.scope.$eval(); c.scope.$eval();
assertEquals('<pre ng:bind-template="Hello {{name}}!">Hello World!</pre>', sortedHtml(c.node)); assertEquals('<pre ng:bind-template="Hello {{name}}!">Hello World!</pre>', sortedHtml(c.view));
}); });
it('FillInOptionValueWhenMissing', function(){ it('FillInOptionValueWhenMissing', function(){
@ -503,9 +501,9 @@ describe('Binder', function(){
c.scope.$set('a', 'A'); c.scope.$set('a', 'A');
c.scope.$set('b', 'B'); c.scope.$set('b', 'B');
c.scope.$eval(); c.scope.$eval();
var optionA = childNode(c.node, 0); var optionA = childNode(c.view, 0);
var optionB = childNode(c.node, 1); var optionB = childNode(c.view, 1);
var optionC = childNode(c.node, 2); var optionC = childNode(c.view, 2);
expect(optionA.attr('value')).toEqual('A'); expect(optionA.attr('value')).toEqual('A');
expect(optionA.text()).toEqual('A'); expect(optionA.text()).toEqual('A');
@ -565,7 +563,7 @@ describe('Binder', function(){
'<input name="c0" ng:bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng:bind-attr="{disabled:\'{{[]}}\'}"></div>'); '<input name="c0" ng:bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng:bind-attr="{disabled:\'{{[]}}\'}"></div>');
c.scope.$eval(); c.scope.$eval();
function assertChild(index, disabled) { function assertChild(index, disabled) {
var child = childNode(c.node, index); var child = childNode(c.view, index);
assertEquals(sortedHtml(child), disabled, !!child.attr('disabled')); assertEquals(sortedHtml(child), disabled, !!child.attr('disabled'));
} }
@ -581,8 +579,8 @@ describe('Binder', function(){
var c = this.compile('<div>' + var c = this.compile('<div>' +
'<input type="button" ng:click="greeting=\'ABC\'"/>' + '<input type="button" ng:click="greeting=\'ABC\'"/>' +
'<input type="button" ng:click=":garbage:"/></div>'); '<input type="button" ng:click=":garbage:"/></div>');
var first = jqLite(c.node[0].childNodes[0]); var first = jqLite(c.view[0].childNodes[0]);
var second = jqLite(c.node[0].childNodes[1]); var second = jqLite(c.view[0].childNodes[1]);
var errorLogs = c.scope.$service('$log').error.logs; var errorLogs = c.scope.$service('$log').error.logs;
browserTrigger(first, 'click'); browserTrigger(first, 'click');
@ -598,8 +596,8 @@ describe('Binder', function(){
var c = this.compile('<div>' + var c = this.compile('<div>' +
'<input type="radio" name="sex" value="female"/>' + '<input type="radio" name="sex" value="female"/>' +
'<input type="radio" name="sex" value="male"/></div>'); '<input type="radio" name="sex" value="male"/></div>');
var female = jqLite(c.node[0].childNodes[0]); var female = jqLite(c.view[0].childNodes[0]);
var male = jqLite(c.node[0].childNodes[1]); var male = jqLite(c.view[0].childNodes[1]);
browserTrigger(female); browserTrigger(female);
assertEquals("female", c.scope.sex); assertEquals("female", c.scope.sex);
@ -636,7 +634,7 @@ describe('Binder', function(){
'<li ng:bind=\"k + v\" ng:repeat-index="0">a0</li>' + '<li ng:bind=\"k + v\" ng:repeat-index="0">a0</li>' +
'<li ng:bind=\"k + v\" ng:repeat-index="1">b1</li>' + '<li ng:bind=\"k + v\" ng:repeat-index="1">b1</li>' +
'</ul>', '</ul>',
sortedHtml(x.node)); sortedHtml(x.view));
}); });
it('ItShouldFireChangeListenersBeforeUpdate', function(){ it('ItShouldFireChangeListenersBeforeUpdate', function(){
@ -648,13 +646,13 @@ describe('Binder', function(){
assertEquals(123, x.scope.$get("name")); assertEquals(123, x.scope.$get("name"));
assertEquals( assertEquals(
'<div ng:bind="name">123</div>', '<div ng:bind="name">123</div>',
sortedHtml(x.node)); sortedHtml(x.view));
}); });
it('ItShouldHandleMultilineBindings', function(){ it('ItShouldHandleMultilineBindings', function(){
var x = this.compile('<div>{{\n 1 \n + \n 2 \n}}</div>'); var x = this.compile('<div>{{\n 1 \n + \n 2 \n}}</div>');
x.scope.$eval(); x.scope.$eval();
assertEquals("3", x.node.text()); assertEquals("3", x.view.text());
}); });
it('ItBindHiddenInputFields', function(){ it('ItBindHiddenInputFields', function(){
@ -667,7 +665,7 @@ describe('Binder', function(){
var x = this.compile('<input name="a" ng:format="list" value="a,b">'); var x = this.compile('<input name="a" ng:format="list" value="a,b">');
x.scope.$eval(); x.scope.$eval();
assertEquals(['a','b'], x.scope.$get('a')); assertEquals(['a','b'], x.scope.$get('a'));
var input = x.node; var input = x.view;
input[0].value = ' x,,yz'; input[0].value = ' x,,yz';
browserTrigger(input, 'change'); browserTrigger(input, 'change');
assertEquals(['x','yz'], x.scope.$get('a')); assertEquals(['x','yz'], x.scope.$get('a'));

View file

@ -27,8 +27,7 @@ describe('compiler', function(){
compiler = new Compiler(markup, attrMarkup, directives, widgets); compiler = new Compiler(markup, attrMarkup, directives, widgets);
compile = function(html){ compile = function(html){
var e = jqLite("<div>" + html + "</div>"); var e = jqLite("<div>" + html + "</div>");
var scope = compiler.compile(e)(e); return scope = compiler.compile(e)().scope;
return scope;
}; };
}); });
@ -48,7 +47,7 @@ describe('compiler', function(){
}; };
var template = compiler.compile(e); var template = compiler.compile(e);
expect(log).toEqual("found"); expect(log).toEqual("found");
scope = template(e); scope = template(angular.scope(), e).scope;
expect(e.hasClass('ng-directive')).toEqual(true); expect(e.hasClass('ng-directive')).toEqual(true);
expect(log).toEqual("found:init"); expect(log).toEqual("found:init");
}); });
@ -85,7 +84,7 @@ describe('compiler', function(){
var template = this.compile(element); var template = this.compile(element);
return function(marker) { return function(marker) {
this.$onEval(function() { this.$onEval(function() {
marker.after(template(element.clone()).$element); marker.after(template(angular.scope(), true).view);
}); });
}; };
}; };

View file

@ -169,7 +169,7 @@ describe("resource", function() {
}); });
it('should excersize full stack', function(){ it('should excersize full stack', function(){
var scope = angular.compile('<div></div>'); var scope = angular.compile('<div></div>')().scope;
var $browser = scope.$service('$browser'); var $browser = scope.$service('$browser');
var $resource = scope.$service('$resource'); var $resource = scope.$service('$resource');
var Person = $resource('/Person/:id'); var Person = $resource('/Person/:id');
@ -181,7 +181,7 @@ describe("resource", function() {
}); });
it('should return the same object when verifying the cache', function(){ it('should return the same object when verifying the cache', function(){
var scope = angular.compile('<div></div>'); var scope = angular.compile('<div></div>')().scope;
var $browser = scope.$service('$browser'); var $browser = scope.$service('$browser');
var $resource = scope.$service('$resource'); var $resource = scope.$service('$resource');
var Person = $resource('/Person/:id', null, {query: {method:'GET', isArray: true, verifyCache: true}}); var Person = $resource('/Person/:id', null, {query: {method:'GET', isArray: true, verifyCache: true}});

View file

@ -12,25 +12,25 @@ describe("ScenarioSpec: Compilation", function(){
describe('compilation', function(){ describe('compilation', function(){
it("should compile dom node and return scope", function(){ it("should compile dom node and return scope", function(){
var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0]; var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[0];
scope = compile(node); scope = angular.compile(node)().scope;
expect(scope.a).toEqual(1); expect(scope.a).toEqual(1);
expect(scope.b).toEqual(2); expect(scope.b).toEqual(2);
}); });
it("should compile jQuery node and return scope", function(){ it("should compile jQuery node and return scope", function(){
scope = compile(jqLite('<div>{{a=123}}</div>')); scope = compile(jqLite('<div>{{a=123}}</div>'))().scope;
expect(jqLite(scope.$element).text()).toEqual('123'); expect(jqLite(scope.$element).text()).toEqual('123');
}); });
it("should compile text node and return scope", function(){ it("should compile text node and return scope", function(){
scope = compile('<div>{{a=123}}</div>'); scope = angular.compile('<div>{{a=123}}</div>')().scope;
expect(jqLite(scope.$element).text()).toEqual('123'); expect(jqLite(scope.$element).text()).toEqual('123');
}); });
}); });
describe('scope', function(){ describe('scope', function(){
it("should have $set, $get, $eval, $updateView methods", function(){ it("should have $set, $get, $eval, $updateView methods", function(){
scope = compile('<div>{{a}}</div>'); scope = angular.compile('<div>{{a}}</div>')().scope;
scope.$eval("$invalidWidgets.push({})"); scope.$eval("$invalidWidgets.push({})");
expect(scope.$set("a", 2)).toEqual(2); expect(scope.$set("a", 2)).toEqual(2);
expect(scope.$get("a")).toEqual(2); expect(scope.$get("a")).toEqual(2);
@ -40,7 +40,7 @@ describe("ScenarioSpec: Compilation", function(){
}); });
it("should have $ objects", function(){ it("should have $ objects", function(){
scope = compile('<div></div>', {$config: {a:"b"}}); scope = angular.compile('<div></div>')(angular.scope({$config: {a:"b"}})).scope;
expect(scope.$service('$location')).toBeDefined(); expect(scope.$service('$location')).toBeDefined();
expect(scope.$get('$eval')).toBeDefined(); expect(scope.$get('$eval')).toBeDefined();
expect(scope.$get('$config')).toBeDefined(); expect(scope.$get('$config')).toBeDefined();
@ -51,7 +51,7 @@ describe("ScenarioSpec: Compilation", function(){
describe("configuration", function(){ describe("configuration", function(){
it("should take location object", function(){ it("should take location object", function(){
var url = "http://server/#?book=moby"; var url = "http://server/#?book=moby";
scope = compile("<div>{{$location}}</div>"); scope = angular.compile("<div>{{$location}}</div>")().scope;
var $location = scope.$service('$location'); var $location = scope.$service('$location');
var $browser = scope.$service('$browser'); var $browser = scope.$service('$browser');
expect($location.hashSearch.book).toBeUndefined(); expect($location.hashSearch.book).toBeUndefined();

View file

@ -7,7 +7,7 @@ describe('ValidatorTest', function(){
validator.last = last; validator.last = last;
validator._this = this; validator._this = this;
}; };
var scope = compile('<input name="name" ng:validate="myValidator:\'hevery\'"/>'); var scope = compile('<input name="name" ng:validate="myValidator:\'hevery\'"/>')().scope;
scope.name = 'misko'; scope.name = 'misko';
scope.$eval(); scope.$eval();
assertEquals('misko', validator.first); assertEquals('misko', validator.first);
@ -95,7 +95,7 @@ describe('ValidatorTest', function(){
beforeEach(function(){ beforeEach(function(){
value = null; value = null;
fn = null; fn = null;
self = compile('<input />'); self = angular.compile('<input />')().scope;
jqLite(document.body).append(self.$element); jqLite(document.body).append(self.$element);
self.$element.data('$validate', noop); self.$element.data('$validate', noop);
self.$root = self; self.$root = self;
@ -107,7 +107,8 @@ describe('ValidatorTest', function(){
it('should make a request and show spinner', function(){ it('should make a request and show spinner', function(){
var value, fn; var value, fn;
var scope = compile('<input type="text" name="name" ng:validate="asynchronous:asyncFn"/>'); var scope = angular.compile(
'<input type="text" name="name" ng:validate="asynchronous:asyncFn"/>')().scope;
jqLite(document.body).append(scope.$element); jqLite(document.body).append(scope.$element);
var input = scope.$element; var input = scope.$element;
scope.asyncFn = function(v,f){ scope.asyncFn = function(v,f){
@ -149,7 +150,8 @@ describe('ValidatorTest', function(){
}); });
it("should handle update function", function(){ it("should handle update function", function(){
var scope = angular.compile('<input name="name" ng:validate="asynchronous:asyncFn:updateFn"/>'); var scope = angular.compile(
'<input name="name" ng:validate="asynchronous:asyncFn:updateFn"/>')().scope;
scope.asyncFn = jasmine.createSpy(); scope.asyncFn = jasmine.createSpy();
scope.updateFn = jasmine.createSpy(); scope.updateFn = jasmine.createSpy();
scope.name = 'misko'; scope.name = 'misko';

View file

@ -3,11 +3,9 @@ describe("directive", function(){
var compile, model, element; var compile, model, element;
beforeEach(function() { beforeEach(function() {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
compile = function(html) { compile = function(html) {
element = jqLite(html); element = jqLite(html);
model = compiler.compile(element)(element); return model = angular.compile(element)().scope;
return model;
}; };
}); });

View file

@ -5,10 +5,9 @@ describe("markups", function(){
beforeEach(function() { beforeEach(function() {
scope = null; scope = null;
element = null; element = null;
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
compile = function(html) { compile = function(html) {
element = jqLite(html); element = jqLite(html);
scope = compiler.compile(element)(element); scope = angular.compile(element)().scope;
}; };
}); });

View file

@ -12,9 +12,9 @@ describe('$invalidWidgets', function() {
it("should count number of invalid widgets", function(){ it("should count number of invalid widgets", function(){
scope = compile('<input name="price" ng:required ng:validate="number"></input>'); var element = jqLite('<input name="price" ng:required ng:validate="number"></input>')
jqLite(document.body).append(scope.$element); jqLite(document.body).append(element);
scope.$init(); scope = compile(element)().scope;
var $invalidWidgets = scope.$service('$invalidWidgets'); var $invalidWidgets = scope.$service('$invalidWidgets');
expect($invalidWidgets.length).toEqual(1); expect($invalidWidgets.length).toEqual(1);

View file

@ -18,7 +18,7 @@ describe('$route', function() {
function BookChapter() { function BookChapter() {
this.log = '<init>'; this.log = '<init>';
} }
scope = compile('<div></div>').$init(); scope = compile('<div></div>')().scope;
$location = scope.$service('$location'); $location = scope.$service('$location');
$route = scope.$service('$route'); $route = scope.$service('$route');
$route.when('/Book/:book/Chapter/:chapter', {controller: BookChapter, template:'Chapter.html'}); $route.when('/Book/:book/Chapter/:chapter', {controller: BookChapter, template:'Chapter.html'});
@ -87,7 +87,7 @@ describe('$route', function() {
$route = scope.$service('$route'), $route = scope.$service('$route'),
onChangeSpy = jasmine.createSpy('onChange'); onChangeSpy = jasmine.createSpy('onChange');
function NotFoundCtrl() {this.notFoundProp = 'not found!'} function NotFoundCtrl() {this.notFoundProp = 'not found!';}
$route.when('/foo', {template: 'foo.html'}); $route.when('/foo', {template: 'foo.html'});
$route.otherwise({template: '404.html', controller: NotFoundCtrl}); $route.otherwise({template: '404.html', controller: NotFoundCtrl});

View file

@ -4,7 +4,6 @@ describe("widget", function(){
beforeEach(function() { beforeEach(function() {
scope = null; scope = null;
element = null; element = null;
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
compile = function(html, parent) { compile = function(html, parent) {
if (parent) { if (parent) {
parent.html(html); parent.html(html);
@ -12,8 +11,7 @@ describe("widget", function(){
} else { } else {
element = jqLite(html); element = jqLite(html);
} }
scope = compiler.compile(element)(element); return scope = angular.compile(element)().scope;
return scope;
}; };
}); });
@ -594,7 +592,7 @@ describe("widget", function(){
}); });
it('should call change on switch', function(){ it('should call change on switch', function(){
var scope = angular.compile('<ng:switch on="url" change="name=\'works\'"><div ng:switch-when="a">{{name}}</div></ng:switch>'); var scope = angular.compile('<ng:switch on="url" change="name=\'works\'"><div ng:switch-when="a">{{name}}</div></ng:switch>')().scope;
scope.url = 'a'; scope.url = 'a';
scope.$eval(); scope.$eval();
expect(scope.name).toEqual(undefined); expect(scope.name).toEqual(undefined);
@ -606,7 +604,7 @@ describe("widget", function(){
describe('ng:include', function(){ describe('ng:include', function(){
it('should include on external file', function() { it('should include on external file', function() {
var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>'); var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
var scope = angular.compile(element); var scope = angular.compile(element)().scope;
scope.childScope = createScope(); scope.childScope = createScope();
scope.childScope.name = 'misko'; scope.childScope.name = 'misko';
scope.url = 'myUrl'; scope.url = 'myUrl';
@ -619,7 +617,7 @@ describe("widget", function(){
it('should remove previously included text if a falsy value is bound to src', function() { it('should remove previously included text if a falsy value is bound to src', function() {
var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>'); var element = jqLite('<ng:include src="url" scope="childScope"></ng:include>');
var scope = angular.compile(element); var scope = angular.compile(element)().scope;
scope.childScope = createScope(); scope.childScope = createScope();
scope.childScope.name = 'igor'; scope.childScope.name = 'igor';
scope.url = 'myUrl'; scope.url = 'myUrl';
@ -638,7 +636,7 @@ describe("widget", function(){
it('should allow this for scope', function(){ it('should allow this for scope', function(){
var element = jqLite('<ng:include src="url" scope="this"></ng:include>'); var element = jqLite('<ng:include src="url" scope="this"></ng:include>');
var scope = angular.compile(element); var scope = angular.compile(element)().scope;
scope.url = 'myUrl'; scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{c=c+1}}'}; scope.$service('$xhr.cache').data.myUrl = {value:'{{c=c+1}}'};
scope.$eval(); scope.$eval();
@ -652,7 +650,7 @@ describe("widget", function(){
it('should evaluate onload expression when a partial is loaded', function() { it('should evaluate onload expression when a partial is loaded', function() {
var element = jqLite('<ng:include src="url" onload="loaded = true"></ng:include>'); var element = jqLite('<ng:include src="url" onload="loaded = true"></ng:include>');
var scope = angular.compile(element); var scope = angular.compile(element)().scope;
expect(scope.loaded).not.toBeDefined(); expect(scope.loaded).not.toBeDefined();
@ -791,7 +789,7 @@ describe("widget", function(){
var rootScope, rootScope, $route, $location, $browser; var rootScope, rootScope, $route, $location, $browser;
beforeEach(function() { beforeEach(function() {
rootScope = angular.compile('<ng:view></ng:view>'); rootScope = angular.compile('<ng:view></ng:view>')().scope;
$route = rootScope.$service('$route'); $route = rootScope.$service('$route');
$location = rootScope.$service('$location'); $location = rootScope.$service('$location');
$browser = rootScope.$service('$browser'); $browser = rootScope.$service('$browser');
@ -870,7 +868,7 @@ describe("widget", function(){
rootScope = angular.compile( rootScope = angular.compile(
'<div>' + '<div>' +
'include: <ng:include src="\'includePartial.html\'">' + 'include: <ng:include src="\'includePartial.html\'">' +
'</ng:include></div>', myApp); '</ng:include></div>')(myApp).scope;
$browser.xhr.expectGET('viewPartial.html').respond('content'); $browser.xhr.expectGET('viewPartial.html').respond('content');
$browser.xhr.flush(); $browser.xhr.flush();