mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
markup now wroks, some refactorings
This commit is contained in:
parent
6ff550cfa9
commit
bb98ae14f2
11 changed files with 509 additions and 295 deletions
|
|
@ -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){
|
||||
|
|
|
|||
271
src/Compiler.js
271
src/Compiler.js
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
185
src/jqLite.js
Normal 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
64
src/markup.js
Normal 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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
49
test/markupSpec.js
Normal 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");
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Reference in a new issue