add($compile): add compiler v2.0 - not connected

This commit is contained in:
Misko Hevery 2011-11-29 12:11:32 -08:00
parent 5001c1a121
commit 8af4fde182
10 changed files with 2019 additions and 572 deletions

View file

@ -626,6 +626,22 @@ function copy(source, destination){
return destination;
}
/**
* Create a shallow copy of an object
* @param src
*/
function shallowCopy(src) {
var dst = {},
key;
for(key in src) {
if (src.hasOwnProperty(key)) {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc function
* @name angular.equals
@ -750,6 +766,19 @@ function toBoolean(value) {
return value;
}
/**
* @returns {string} Returns the string representation of the element.
*/
function startingTag(element) {
element = jqLite(element).clone();
try {
// turns out IE does not let you set .html() on elements which
// are not allowed to have children. So we just ignore it.
element.html('');
} catch(e) {};
return jqLite('<div>').append(element).html().replace(/\<\/[\w\:\-]+\>$/, '');
}
/////////////////////////////////////////////////
@ -918,6 +947,14 @@ function bootstrap(element, modules) {
return injector;
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
function snake_case(name, separator){
separator = separator || '_';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
function bindJQuery() {
// bind to jQuery if present;
jQuery = window.jQuery;
@ -951,6 +988,7 @@ function assertArg(arg, name, reason) {
}
function assertArgFn(arg, name) {
assertArg(arg, name);
assertArg(isFunction(arg), name, 'not a function, got ' +
(typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
return arg;

12
src/angular-mocks.js vendored
View file

@ -684,19 +684,17 @@ angular.mock.dump = function(object) {
*
* <pre>
// controller
function MyController($http) {
var scope = this;
function MyController($scope, $http) {
$http.get('/auth.py').success(function(data) {
scope.user = data;
$scope.user = data;
});
this.saveMessage = function(message) {
scope.status = 'Saving...';
$scope.status = 'Saving...';
$http.post('/add-msg.py', message).success(function(response) {
scope.status = '';
$scope.status = '';
}).error(function() {
scope.status = 'ERROR!';
$scope.status = 'ERROR!';
});
};
}

View file

@ -100,13 +100,27 @@ function getStyle(element) {
}
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
/**
* Converts dash-separated names to camelCase. Useful for dealing with css properties.
* Converts all accepted directives format into proper directive name.
* All of these will become 'myDirective':
* my:DiRective
* my-directive
* x-my-directive
* data-my:directive
*
* Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize
*/
function camelCase(name) {
return name.replace(/\-(\w)/g, function(all, letter, offset){
return (offset == 0 && letter == 'w') ? 'w' : letter.toUpperCase();
});
return name.
replace(PREFIX_REGEXP, '').
replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
}).
replace(MOZ_HACK_REGEXP, 'Moz$1');
}
/////////////////////////////////////////////

File diff suppressed because it is too large Load diff

View file

@ -56,41 +56,43 @@
});
}
angular.directive('ng:contenteditable', function() {
return ['$formFactory', '$element', function ($formFactory, element) {
var exp = element.attr('ng:contenteditable'),
form = $formFactory.forElement(element),
widget;
element.attr('contentEditable', true);
widget = form.$createWidget({
scope: this,
model: exp,
controller: HTMLEditorWidget,
controllerArgs: {$element: element}});
// if the element is destroyed, then we need to notify the form.
element.bind('$destroy', function() {
widget.$destroy();
});
}];
});
</script>
<form name='editorForm' ng:controller="EditorCntl">
<div ng:contenteditable="html"></div>
<hr/>
HTML: <br/>
<textarea ng:model="html" cols=80></textarea>
<hr/>
<pre>editorForm = {{editorForm}}</pre>
</form>
</doc:source>
<doc:scenario>
it('should enter invalid HTML', function() {
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-valid/);
input('html').enter('<');
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-invalid/);
});
</doc:scenario>
</doc:example>
angular.module('formModule', [], function($compileProvider){
$compileProvider.directive('ngHtmlEditorModel', function ($formFactory) {
return function(scope, element, attr) {
var form = $formFactory.forElement(element),
widget;
element.attr('contentEditable', true);
widget = form.$createWidget({
scope: scope,
model: attr.ngHtmlEditorModel,
controller: HTMLEditorWidget,
controllerArgs: {$element: element}});
// if the element is destroyed, then we need to
// notify the form.
element.bind('$destroy', function() {
widget.$destroy();
});
};
});
});
</script>
<form name='editorForm' ng:controller="EditorCntl">
<div ng:html-editor-model="htmlContent"></div>
<hr/>
HTML: <br/>
<textarea ng:model="htmlContent" cols="80"></textarea>
<hr/>
<pre>editorForm = {{editorForm|json}}</pre>
</form>
</doc:source>
<doc:scenario>
it('should enter invalid HTML', function() {
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-valid/);
input('htmlContent').enter('<');
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-invalid/);
});
</doc:scenario>
</doc:example>
*/
/**

View file

@ -75,6 +75,22 @@ describe('angular', function() {
});
});
describe('shallow copy', function() {
it('should make a copy', function() {
var original = {key:{}};
var copy = shallowCopy(original);
expect(copy).toEqual(original);
expect(copy.key).toBe(original.key);
});
});
describe('elementHTML', function() {
it('should dump element', function() {
expect(lowercase(startingTag('<div attr="123">something<span></span></div>'))).
toEqual('<div attr="123">');
});
});
describe('equals', function() {
it('should return true if same object', function() {
var o = {};
@ -501,4 +517,11 @@ describe('angular', function() {
dealoc(element);
});
});
describe('startingElementHtml', function(){
it('should show starting element tag only', function(){
expect(startingTag('<ng:abc x="2"><div>text</div></ng:abc>')).toEqual('<ng:abc x="2">');
});
});
});

View file

@ -2,14 +2,18 @@
describe("directive", function() {
var $filterProvider;
var $filterProvider, element;
beforeEach(module(['$filterProvider', function(provider){
$filterProvider = provider;
}]));
afterEach(function() {
dealoc(element);
});
it("should ng:init", inject(function($rootScope, $compile) {
var element = $compile('<div ng:init="a=123"></div>')($rootScope);
element = $compile('<div ng:init="a=123"></div>')($rootScope);
expect($rootScope.a).toEqual(123);
}));

View file

@ -1,4 +1,3 @@
'use strict';
describe('jqLite', function() {
var scope, a, b, c;
@ -880,6 +879,7 @@ describe('jqLite', function() {
it('should covert dash-separated strings to camelCase', function() {
expect(camelCase('foo-bar')).toBe('fooBar');
expect(camelCase('foo-bar-baz')).toBe('fooBarBaz');
expect(camelCase('foo:bar_baz')).toBe('fooBarBaz');
});

File diff suppressed because it is too large Load diff

View file

@ -67,7 +67,10 @@ function dealoc(obj) {
}
}
/**
* @param {DOMElement} element
* @param {boolean=} showNgClass
*/
function sortedHtml(element, showNgClass) {
var html = "";
forEach(jqLite(element), function toString(node) {