markup now wroks, some refactorings

This commit is contained in:
Misko Hevery 2010-03-23 14:57:11 -07:00
parent 6ff550cfa9
commit bb98ae14f2
11 changed files with 509 additions and 295 deletions

View file

@ -1,6 +1,7 @@
if (typeof document.getAttribute == 'undefined')
document.getAttribute = function() {};
if (typeof Node == 'undefined') {
//TODO: can we get rid of this?
Node = {
ELEMENT_NODE : 1,
ATTRIBUTE_NODE : 2,
@ -21,7 +22,7 @@ function noop() {}
function identity($) {return $;}
if (!window['console']) window['console']={'log':noop, 'error':noop};
function extension(angular, name) {
function extensionMap(angular, name) {
var extPoint;
return angular[name] || (extPoint = angular[name] = function (name, fn, prop){
if (isDefined(fn)) {
@ -31,20 +32,30 @@ function extension(angular, name) {
});
}
function extensionList(angular, name) {
var extPoint, length = 0;
return angular[name] || (extPoint = angular[name] = function (fn, prop){
if (isDefined(fn)) {
extPoint[length] = extend(fn, prop || {});
length++;
}
return extPoint;
});
}
var consoleNode, msie,
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
foreach = _.each,
extend = _.extend,
slice = Array.prototype.slice,
angular = window['angular'] || (window['angular'] = {}),
angularDirective = extension(angular, 'directive'),
angularMarkup = extension(angular, 'markup'),
angularWidget = extension(angular, 'widget'),
angularValidator = extension(angular, 'validator'),
angularFilter = extension(angular, 'filter'),
angularFormatter = extension(angular, 'formatter'),
angularCallbacks = extension(angular, 'callbacks'),
angularAlert = angular['alert'] || (angular['alert'] = function(){
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
slice = Array.prototype.slice,
angular = window['angular'] || (window['angular'] = {}),
angularTextMarkup = extensionList(angular, 'textMarkup'),
angularAttrMarkup = extensionList(angular, 'attrMarkup'),
angularDirective = extensionMap(angular, 'directive'),
angularWidget = extensionMap(angular, 'widget'),
angularValidator = extensionMap(angular, 'validator'),
angularFilter = extensionMap(angular, 'filter'),
angularFormatter = extensionMap(angular, 'formatter'),
angularCallbacks = extensionMap(angular, 'callbacks'),
angularAlert = angular['alert'] || (angular['alert'] = function(){
log(arguments); window.alert.apply(window, arguments);
});
angular['copy'] = copy;
@ -53,6 +64,29 @@ var isVisible = isVisible || function (element) {
return jQuery(element).is(":visible");
};
function foreach(obj, iterator, context) {
var key;
if (obj) {
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj instanceof Array) {
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
for (key in obj)
iterator.call(context, obj[key], key);
}
}
return obj;
}
function extend(dst, obj) {
foreach(obj, function(value, key){
dst[key] = value;
});
return dst;
}
function isDefined(value){ return typeof value != 'undefined'; }
function isObject(value){ return typeof value == 'object';}
function isString(value){ return typeof value == 'string';}
@ -112,14 +146,15 @@ function isNode(inp) {
}
function isLeafNode (node) {
switch (node.nodeName) {
case "OPTION":
case "PRE":
case "TITLE":
return true;
default:
return false;
if (node) {
switch (node.nodeName) {
case "OPTION":
case "PRE":
case "TITLE":
return true;
}
}
return false;
}
function copy(source, destination){

View file

@ -47,225 +47,42 @@ Template.prototype = {
}
};
///////////////////////////////////
//JQLite
//////////////////////////////////
var jqCache = {};
var jqName = 'ng-' + new Date().getTime();
var jqId = 1;
function jqNextId() { return jqId++; }
var addEventListener = window.document.attachEvent ?
function(element, type, fn) {
element.attachEvent('on' + type, fn);
} : function(element, type, fn) {
element.addEventListener(type, fn, false);
};
var removeEventListener = window.document.detachEvent ?
function(element, type, fn) {
element.detachEvent('on' + type, fn);
} : function(element, type, fn) {
element.removeEventListener(type, fn, false);
};
function jqClearData(element) {
var cacheId = element[jqName],
cache = jqCache[cacheId];
if (cache) {
foreach(cache.bind || {}, function(fn, type){
removeEventListener(element, type, fn);
});
delete jqCache[cacheId];
delete element[jqName];
}
};
function JQLite(element) {
this[0] = element;
}
function jqLite(element) {
if (typeof element == 'string') {
var div = document.createElement('div');
div.innerHTML = element;
element = div.childNodes[0];
}
return element instanceof JQLite ? element : new JQLite(element);
}
JQLite.prototype = {
data: function(key, value) {
var element = this[0],
cacheId = element[jqName],
cache = jqCache[cacheId || -1];
if (isDefined(value)) {
if (!cache) {
element[jqName] = cacheId = jqNextId();
cache = jqCache[cacheId] = {};
}
cache[key] = value;
} else {
return cache ? cache[key] : null;
}
},
removeData: function(){
jqClearData(this[0]);
},
dealoc: function(){
(function dealoc(element){
jqClearData(element);
for ( var i = 0, children = element.childNodes; i < children.length; i++) {
dealoc(children[0]);
}
})(this[0]);
},
bind: function(type, fn){
var element = this[0],
bind = this.data('bind'),
eventHandler;
if (!bind) this.data('bind', bind = {});
eventHandler = bind[type];
if (!eventHandler) {
bind[type] = eventHandler = function() {
var self = this;
foreach(eventHandler.fns, function(fn){
fn.apply(self, arguments);
});
};
eventHandler.fns = [];
addEventListener(element, type, eventHandler);
}
eventHandler.fns.push(fn);
},
trigger: function(type) {
var cache = this.data('bind');
if (cache) {
(cache[type] || noop)();
}
},
click: function(fn) {
if (fn)
this.bind('click', fn);
else
this.trigger('click');
},
eachTextNode: function(fn){
var i, chldNodes = this[0].childNodes || [], size = chldNodes.length, chld;
for (i = 0; i < size; i++) {
if((chld = new JQLite(chldNodes[i])).isText()) {
fn(chld, i);
}
}
},
eachNode: function(fn){
var i, chldNodes = this[0].childNodes || [], size = chldNodes.length, chld;
for (i = 0; i < size; i++) {
if(!(chld = new JQLite(chldNodes[i])).isText()) {
fn(chld, i);
}
}
},
eachAttribute: function(fn){
var i, attrs = this[0].attributes || [], size = attrs.length, chld, attr;
for (i = 0; i < size; i++) {
var attr = attrs[i];
fn(attr.name, attr.value);
}
},
replaceWith: function(replaceNode) {
this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]);
},
remove: function() {
this.dealoc();
this[0].parentNode.removeChild(this[0]);
},
removeAttr: function(name) {
this[0].removeAttribute(name);
},
after: function(element) {
this[0].parentNode.insertBefore(jqLite(element)[0], this[0].nextSibling);
},
hasClass: function(selector) {
var className = " " + selector + " ";
if ( (" " + this[0].className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1 ) {
return true;
}
return false;
},
addClass: function( selector ) {
if (!this.hasClass(selector)) {
this[0].className += ' ' + selector;
}
},
css: function(name, value) {
var style = this[0].style;
if (isString(name)) {
if (isDefined(value)) {
style[name] = value;
} else {
return style[name];
}
} else {
extend(style, name);
}
},
attr: function(name, value){
var e = this[0];
if (isObject(name)) {
foreach(name, function(value, name){
e.setAttribute(name, value);
});
} else if (isDefined(value)) {
e.setAttribute(name, value);
} else {
return e.getAttribute(name);
}
},
text: function(value) {
if (isDefined(value)) {
this[0].textContent = value;
}
return this[0].textContent;
},
html: function(value) {
if (isDefined(value)) {
this[0].innerHTML = value;
}
return this[0].innerHTML;
},
parent: function() { return jqLite(this[0].parentNode);},
isText: function() { return this[0].nodeType == Node.TEXT_NODE; },
clone: function() { return jqLite(this[0].cloneNode(true)); }
};
///////////////////////////////////
//Compiler
//////////////////////////////////
function isTextNode(node) {
return node.nodeType == Node.TEXT_NODE;
}
function Compiler(markup, directives, widgets){
this.markup = markup;
function eachTextNode(element, fn){
var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld;
for (i = 0; i < size; i++) {
if(isTextNode(chld = chldNodes[i])) {
fn(jqLite(chld), i);
}
}
}
function eachNode(element, fn){
var i, chldNodes = element[0].childNodes || [], size = chldNodes.length, chld;
for (i = 0; i < size; i++) {
if(!isTextNode(chld = chldNodes[i])) {
fn(jqLite(chld), i);
}
}
}
function eachAttribute(element, fn){
var i, attrs = element[0].attributes || [], size = attrs.length, chld, attr;
for (i = 0; i < size; i++) {
var attr = attrs[i];
fn(attr.name, attr.value);
}
}
function Compiler(textMarkup, attrMarkup, directives, widgets){
this.textMarkup = textMarkup;
this.attrMarkup = attrMarkup;
this.directives = directives;
this.widgets = widgets;
}
@ -291,8 +108,6 @@ Compiler.prototype = {
elementName = element[0].nodeName,
widgets = self.widgets,
widget = widgets[elementName],
markup = self.markup,
markupSize = markup.length,
directives = self.directives,
descend = true,
exclusive = false,
@ -300,7 +115,9 @@ Compiler.prototype = {
template = new Template(),
selfApi = {
compile: bind(self, self.compile),
reference:function(name) {return jqLite(document.createComment(name));},
comment:function(text) {return jqLite(document.createComment(text));},
element:function(type) {return jqLite(document.createElement(type));},
text:function(text) {return jqLite(document.createTextNode(text));},
descend: function(value){ if(isDefined(value)) descend = value; return descend;}
};
@ -308,14 +125,20 @@ Compiler.prototype = {
template.addInit(widget.call(selfApi, element));
} else {
// process markup for text nodes only
element.eachTextNode(function(textNode){
for (var i = 0, text = textNode.text(); i < markupSize; i++) {
markup[i].call(selfApi, text, textNode, element);
}
eachTextNode(element, function(textNode){
var text = textNode.text();
foreach(self.textMarkup, function(markup){
markup.call(selfApi, text, textNode, element);
});
});
// Process attributes/directives
element.eachAttribute(function(name, value){
eachAttribute(element, function(name, value){
foreach(self.attrMarkup, function(markup){
markup.call(selfApi, value, name, element);
});
});
eachAttribute(element, function(name, value){
var directive = directives[name];
if (!exclusive && directive) {
if (directive.exclusive) {
@ -333,7 +156,7 @@ Compiler.prototype = {
// Process non text child nodes
if (descend) {
element.eachNode(function(child, i){
eachNode(element, function(child, i){
template.addChild(i, self.templatize(child));
});
}

View file

@ -27,7 +27,7 @@ foreach({
jQuery(this.$element).toggleClass('ng-format-negative', amount < 0);
return '$' + angularFilter['number'].apply(this, [amount, 2]);
},
'number': function(amount, fractionSize){
if (isNaN(amount) || !isFinite(amount)) {
return '';
@ -55,15 +55,15 @@ foreach({
}
return text;
},
'date': function(amount) {
},
'json': function(object) {
jQuery(this.$element).addClass("ng-monospace");
return toJson(object, true);
},
'trackPackage': (function(){
var MATCHERS = [
{ name: "UPS",
@ -89,7 +89,7 @@ foreach({
var returnValue;
foreach(MATCHERS, function(carrier){
foreach(carrier.regexp, function(regexp){
if (regexp.test(tNo)) {
if (!returnValue && regexp.test(tNo)) {
var text = carrier.name + ": " + trackingNo;
var url = carrier.url + trackingNo;
returnValue = new angularFilter.Meta({
@ -97,19 +97,17 @@ foreach({
url:url,
html: '<a href="' + escapeAttr(url) + '">' + text + '</a>',
trackingNo:trackingNo});
_.breakLoop();
}
});
if (returnValue) _.breakLoop();
});
if (returnValue)
if (returnValue)
return returnValue;
else if (trackingNo)
return noMatch || new angularFilter.Meta({text:trackingNo + " is not recognized"});
else
return null;
};})(),
'link': function(obj, title) {
var text = title || angularFilter.Meta.get(obj);
var url = angularFilter.Meta.get(obj, "url") || angularFilter.Meta.get(obj);
@ -122,13 +120,13 @@ foreach({
}
return obj;
},
'bytes': (function(){
var SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
return function(size) {
if(size === null) return "";
var suffix = 0;
while (size > 1000) {
size = size / 1024;
@ -142,7 +140,7 @@ foreach({
return txt + " " + SUFFIX[suffix];
};
})(),
'image': function(obj, width, height) {
if (obj && obj.url) {
var style = "";
@ -155,36 +153,36 @@ foreach({
}
return null;
},
'lowercase': function (obj) {
var text = angularFilter.Meta.get(obj);
return text ? ("" + text).toLowerCase() : text;
},
'uppercase': function (obj) {
var text = angularFilter.Meta.get(obj);
return text ? ("" + text).toUpperCase() : text;
},
'linecount': function (obj) {
var text = angularFilter.Meta.get(obj);
if (text==='' || !text) return 1;
return text.split(/\n|\f/).length;
},
'if': function (result, expression) {
return expression ? result : undefined;
},
'unless': function (result, expression) {
return expression ? undefined : result;
},
'googleChartApi': extend(
function(type, data, width, height) {
data = data || {};
var chart = {
'cht':type,
'cht':type,
'chco':angularFilterGoogleChartApi['collect'](data, 'color'),
'chtt':angularFilterGoogleChartApi['title'](data),
'chdl':angularFilterGoogleChartApi['collect'](data, 'label'),
@ -210,7 +208,7 @@ foreach({
var values = seriesValues.join('|');
return values === "" ? null : "t:" + values;
},
'title': function(data){
var titles = [];
var title = data['title'] || [];
@ -219,7 +217,7 @@ foreach({
});
return titles.join('|');
},
'collect': function(data, key){
var outterValues = [];
var count = 0;
@ -234,7 +232,7 @@ foreach({
});
return count?outterValues.join(','):null;
},
'encode': function(params, width, height) {
width = width || 200;
height = height || width;
@ -253,8 +251,8 @@ foreach({
}
}
),
'qrcode': function(value, width, height) {
return angularFilterGoogleChartApi['encode']({
'cht':'qr', 'chl':encodeURIComponent(value)}, width, height);
@ -291,11 +289,11 @@ foreach({
return angularFilterGoogleChartApi('s', data, width, height);
}
},
'html': function(html){
return new angularFilter.Meta({html:html});
},
'linky': function(text){
if (!text) return text;
function regExpEscape(text) {

View file

@ -116,7 +116,7 @@ Scope.prototype = {
},
compile: function(exp) {
if (isFunction(exp)) return exp;
if (isFunction(exp)) return bind(this.state, exp);
var expFn = Scope.expressionCache[exp], self = this;
if (!expFn) {
var parser = new Parser(exp);

View file

@ -18,9 +18,50 @@ angularDirective("ng-bind", function(expression){
};
});
var bindTemplateCache = {};
function compileBindTemplate(template){
var fn = bindTemplateCache[template];
if (!fn) {
var bindings = [];
foreach(parseBindings(template), function(text){
var exp = binding(text);
bindings.push(exp ? function(){
return this.$eval(exp);
} : function(){
return text;
});
});
bindTemplateCache[template] = fn = function(){
var parts = [], self = this;
foreach(bindings, function(fn){
parts.push(fn.call(self));
});
return parts.join('');
};
}
return fn;
};
angularDirective("ng-bind-template", function(expression){
var templateFn = compileBindTemplate(expression);
return function(element) {
var lastValue;
this.$addEval(function() {
var value = templateFn.call(this);
if (value != lastValue) {
element.text(value);
lastValue = value;
}
});
};
});
angularDirective("ng-bind-attr", function(expression){
return function(element){
this.$watch(expression, bind(element, element.attr));
this.$addEval(function(){
foreach(this.$eval(expression), function(value, key){
element.attr(key, compileBindTemplate(value).call(this));
}, this);
});
};
});
@ -29,7 +70,7 @@ angularDirective("ng-non-bindable", function(){
});
angularDirective("ng-repeat", function(expression, element){
var reference = this.reference("ng-repeat: " + expression),
var reference = this.comment("ng-repeat: " + expression),
r = element.removeAttr('ng-repeat'),
template = this.compile(element),
match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
@ -135,18 +176,12 @@ angularDirective("ng-hide", function(expression, element){
});
};
});
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
angularDirective("ng-style", function(expression, element){
return function(element){
this.$addEval(expression, function(value){
element.css(value);
});
};
});
//widget related
//ng-validate, ng-required, ng-formatter
//ng-error
//ng-scope ng-controller????

185
src/jqLite.js Normal file
View file

@ -0,0 +1,185 @@
///////////////////////////////////
//JQLite
//////////////////////////////////
var jqCache = {};
var jqName = 'ng-' + new Date().getTime();
var jqId = 1;
function jqNextId() { return jqId++; }
var addEventListener = window.document.attachEvent ?
function(element, type, fn) {
element.attachEvent('on' + type, fn);
} : function(element, type, fn) {
element.addEventListener(type, fn, false);
};
var removeEventListener = window.document.detachEvent ?
function(element, type, fn) {
element.detachEvent('on' + type, fn);
} : function(element, type, fn) {
element.removeEventListener(type, fn, false);
};
function jqClearData(element) {
var cacheId = element[jqName],
cache = jqCache[cacheId];
if (cache) {
foreach(cache.bind || {}, function(fn, type){
removeEventListener(element, type, fn);
});
delete jqCache[cacheId];
delete element[jqName];
}
};
function JQLite(element) {
this[0] = element;
}
function jqLite(element) {
if (typeof element == 'string') {
var div = document.createElement('div');
div.innerHTML = element;
element = div.childNodes[0];
}
return element instanceof JQLite ? element : new JQLite(element);
}
JQLite.prototype = {
data: function(key, value) {
var element = this[0],
cacheId = element[jqName],
cache = jqCache[cacheId || -1];
if (isDefined(value)) {
if (!cache) {
element[jqName] = cacheId = jqNextId();
cache = jqCache[cacheId] = {};
}
cache[key] = value;
} else {
return cache ? cache[key] : null;
}
},
removeData: function(){
jqClearData(this[0]);
},
dealoc: function(){
(function dealoc(element){
jqClearData(element);
for ( var i = 0, children = element.childNodes; i < children.length; i++) {
dealoc(children[0]);
}
})(this[0]);
},
bind: function(type, fn){
var element = this[0],
bind = this.data('bind'),
eventHandler;
if (!bind) this.data('bind', bind = {});
eventHandler = bind[type];
if (!eventHandler) {
bind[type] = eventHandler = function() {
var self = this;
foreach(eventHandler.fns, function(fn){
fn.apply(self, arguments);
});
};
eventHandler.fns = [];
addEventListener(element, type, eventHandler);
}
eventHandler.fns.push(fn);
},
trigger: function(type) {
var cache = this.data('bind');
if (cache) {
(cache[type] || noop)();
}
},
click: function(fn) {
if (fn)
this.bind('click', fn);
else
this.trigger('click');
},
replaceWith: function(replaceNode) {
this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]);
},
remove: function() {
this.dealoc();
this[0].parentNode.removeChild(this[0]);
},
removeAttr: function(name) {
this[0].removeAttribute(name);
},
after: function(element) {
this[0].parentNode.insertBefore(jqLite(element)[0], this[0].nextSibling);
},
hasClass: function(selector) {
var className = " " + selector + " ";
if ( (" " + this[0].className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1 ) {
return true;
}
return false;
},
addClass: function( selector ) {
if (!this.hasClass(selector)) {
this[0].className += ' ' + selector;
}
},
css: function(name, value) {
var style = this[0].style;
if (isString(name)) {
if (isDefined(value)) {
style[name] = value;
} else {
return style[name];
}
} else {
extend(style, name);
}
},
attr: function(name, value){
var e = this[0];
if (isObject(name)) {
foreach(name, function(value, name){
e.setAttribute(name, value);
});
} else if (isDefined(value)) {
e.setAttribute(name, value);
} else {
return e.getAttribute(name);
}
},
text: function(value) {
if (isDefined(value)) {
this[0].textContent = value;
}
return this[0].textContent;
},
html: function(value) {
if (isDefined(value)) {
this[0].innerHTML = value;
}
return this[0].innerHTML;
},
parent: function() { return jqLite(this[0].parentNode);},
clone: function() { return jqLite(this[0].cloneNode(true)); }
};

64
src/markup.js Normal file
View file

@ -0,0 +1,64 @@
function parseBindings(string) {
var results = [];
var lastIndex = 0;
var index;
while((index = string.indexOf('{{', lastIndex)) > -1) {
if (lastIndex < index)
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
index = string.indexOf('}}', index);
index = index < 0 ? string.length : index + 2;
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
}
if (lastIndex != string.length)
results.push(string.substr(lastIndex, string.length - lastIndex));
return results.length === 0 ? [ string ] : results;
};
function binding(string) {
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
return binding ? binding[1] : null;
};
function hasBindings(bindings) {
return bindings.length > 1 || Binder.binding(bindings[0]) !== null;
};
angularTextMarkup(function(text, textNode, parentElement) {
var bindings = parseBindings(text),
self = this;
if (isLeafNode(parentElement[0])) {
parentElement.attr('ng-bind-template', text);
} else {
var cursor = textNode, newElement;
foreach(parseBindings(text), function(text){
var exp = binding(text);
if (exp) {
newElement = self.element('span');
newElement.attr('ng-bind', exp);
} else {
newElement = self.text(text);
}
cursor.after(newElement);
cursor = newElement;
});
}
textNode.remove();
});
var NG_BIND_ATTR = 'ng-bind-attr';
angularAttrMarkup(function(value, name, element){
if (name.substr(0, 3) != 'ng-') {
var bindings = parseBindings(value),
bindAttr;
if (hasBindings(bindings)) {
element.removeAttr(name);
bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}");
bindAttr[name] = value;
element.attr(NG_BIND_ATTR, toJson(bindAttr));
}
}
});

View file

@ -1,3 +1,19 @@
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
//widget related
//ng-validate, ng-required, ng-formatter
//ng-error
//ng-scope ng-controller????
// <input type="text" name="bla" ng-action=""> -> <ng:textinput name="" ng-action=""/>
angular.widget("inputtext", function(element) {
var expression = element.attr('name');
@ -49,7 +65,7 @@ angular.widget("colorpicker", function(element) {
this.$watch(expression, function(cmyk){
element.setColor(formatter.format(cmyk));
});
}
};
});
angular.widget("template", function(element) {

View file

@ -3,7 +3,7 @@ describe('compiler', function(){
return jQuery(html)[0];
}
var compiler, markup, directives, widgets, compile, log;
var compiler, textMarkup, directives, widgets, compile, log;
beforeEach(function(){
log = "";
@ -24,9 +24,10 @@ describe('compiler', function(){
}
};
markup = [];
textMarkup = [];
attrMarkup = [];
widgets = {};
compiler = new Compiler(markup, directives, widgets);
compiler = new Compiler(textMarkup, attrMarkup, directives, widgets);
compile = function(html){
var e = element("<div>" + html + "</div>");
var view = compiler.compile(e)(e);
@ -108,7 +109,7 @@ describe('compiler', function(){
});
it('should process markup before directives', function(){
markup.push(function(text, textNode, parentNode) {
textMarkup.push(function(text, textNode, parentNode) {
if (text == 'middle') {
expect(textNode.text()).toEqual(text);
parentNode.attr('hello', text);

View file

@ -3,7 +3,7 @@ describe("directives", function(){
var compile, element;
beforeEach(function() {
var compiler = new Compiler(angularMarkup, angularDirective, angularWidget);
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
compile = function(html) {
element = jqLite(html);
var view = compiler.compile(element)(element);
@ -39,6 +39,14 @@ describe("directives", function(){
expect(element.text()).toEqual('misko');
});
it('should ng-bind-template', function() {
var scope = compile('<div ng-bind-template="Hello {{name}}!"></div>');
expect(element.text()).toEqual('');
scope.set('name', 'Misko');
scope.updateView();
expect(element.text()).toEqual('Hello Misko!');
});
it('should ng-bind-attr', function(){
var scope = compile('<img ng-bind-attr="{src:\'mysrc\', alt:\'myalt\'}"/>');
expect(element.attr('src')).toEqual(null);
@ -81,7 +89,7 @@ describe("directives", function(){
it('should error on wrong parsing of ng-repeat', function(){
var scope = compile('<ul><li ng-repeat="i dont parse"></li></ul>');
var log = "";
element.eachNode(function(li){
eachNode(element, function(li){
log += li.attr('ng-error') + ';';
log += li.hasClass('ng-exception') + ';';
});

49
test/markupSpec.js Normal file
View file

@ -0,0 +1,49 @@
describe("markups", function(){
var compile, element, scope;
beforeEach(function() {
scope = null;
element = null;
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
compile = function(html) {
element = jqLite(html);
var view = compiler.compile(element)(element);
view.init();
scope = view.scope;
};
});
afterEach(function(){
if (element) {
element.remove();
}
expect(_(jqCache).size()).toEqual(0);
});
it('should translate {{}} in text', function(){
compile('<div>hello {{name}}!</div>');
expect(element.html()).toEqual('hello <span ng-bind="name"></span>!');
scope.set('name', 'Misko');
scope.updateView();
expect(element.html()).toEqual('hello <span ng-bind="name">Misko</span>!');
});
it('should translate {{}} in terminal nodes', function(){
compile('<select><option>Greet {{name}}!</option></select>');
expect(element.html()).toEqual('<option ng-bind-template="Greet {{name}}!"></option>');
scope.set('name', 'Misko');
scope.updateView();
expect(element.html()).toEqual('<option ng-bind-template="Greet {{name}}!">Greet Misko!</option>');
});
it('should translate {{}} in attributes', function(){
compile('<img src="http://server/{{path}}.png"/>');
expect(element.attr('src')).toEqual();
expect(element.attr('ng-bind-attr')).toEqual('{"src":"http://server/{{path}}.png"}');
scope.set('path', 'a/b');
scope.updateView();
expect(element.attr('src')).toEqual("http://server/a/b.png");
});
});