diff options
Diffstat (limited to 'src/Compiler.js')
| -rw-r--r-- | src/Compiler.js | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/Compiler.js b/src/Compiler.js new file mode 100644 index 00000000..c8910c27 --- /dev/null +++ b/src/Compiler.js @@ -0,0 +1,212 @@ +/** + * Template provides directions an how to bind to a given element. + * It contains a list of init functions which need to be called to + * bind to a new instance of elements. It also provides a list + * of child paths which contain child templates + */ +function Template(priority) { + this.paths = []; + this.children = []; + this.inits = []; + this.priority = priority || 0; +} + +Template.prototype = { + init: function(element, scope) { + var inits = {}; + this.collectInits(element, inits); + foreachSorted(inits, function(queue){ + foreach(queue, function(fn){ + fn(scope); + }); + }); + }, + + collectInits: function(element, inits) { + var queue = inits[this.priority]; + if (!queue) { + inits[this.priority] = queue = []; + } + element = jqLite(element); + foreach(this.inits, function(fn) { + queue.push(function(scope) { + scope.$tryEval(fn, element, element); + }); + }); + + var i, + childNodes = element[0].childNodes, + children = this.children, + paths = this.paths, + length = paths.length; + for (i = 0; i < length; i++) { + children[i].collectInits(childNodes[paths[i]], inits); + } + }, + + + addInit:function(init) { + if (init) { + this.inits.push(init); + } + }, + + + addChild: function(index, template) { + if (template) { + this.paths.push(index); + this.children.push(template); + } + }, + + empty: function() { + return this.inits.length === 0 && this.paths.length === 0; + } +}; + +/////////////////////////////////// +//Compiler +////////////////////////////////// +function Compiler(textMarkup, attrMarkup, directives, widgets){ + this.textMarkup = textMarkup; + this.attrMarkup = attrMarkup; + this.directives = directives; + this.widgets = widgets; +} + +Compiler.prototype = { + compile: function(rawElement) { + rawElement = jqLite(rawElement); + var index = 0, + template, + parent = rawElement.parent(); + if (parent && parent[0]) { + parent = parent[0]; + for(var i = 0; i < parent.childNodes.length; i++) { + if (parent.childNodes[i] == rawElement[0]) { + index = i; + } + } + } + template = this.templatize(rawElement, index, 0) || new Template(); + return function(element, parentScope){ + element = jqLite(element); + var scope = parentScope && parentScope.$eval ? + parentScope : + createScope(parentScope || {}, angularService); + return extend(scope, { + $element:element, + $init: function() { + template.init(element, scope); + scope.$eval(); + delete scope.$init; + return scope; + } + }); + }; + }, + + templatize: function(element, elementIndex, priority){ + var self = this, + widget, + directiveFns = self.directives, + descend = true, + directives = true, + template, + selfApi = { + compile: bind(self, self.compile), + 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;}, + directives: function(value){ if(isDefined(value)) directives = value; return directives;} + }; + priority = element.attr('ng-eval-order') || priority || 0; + if (isString(priority)) { + priority = PRIORITY[uppercase(priority)] || 0; + } + template = new Template(priority); + eachAttribute(element, function(value, name){ + if (!widget) { + if (widget = self.widgets['@' + name]) { + widget = bind(selfApi, widget, value, element); + } + } + }); + if (!widget) { + if (widget = self.widgets[nodeName(element)]) { + widget = bind(selfApi, widget, element); + } + } + if (widget) { + descend = false; + directives = false; + var parent = element.parent(); + template.addInit(widget.call(selfApi, element)); + if (parent && parent[0]) { + element = jqLite(parent[0].childNodes[elementIndex]); + } + } + if (descend){ + // process markup for text nodes only + eachTextNode(element, function(textNode){ + var text = textNode.text(); + foreach(self.textMarkup, function(markup){ + markup.call(selfApi, text, textNode, element); + }); + }); + } + + if (directives) { + // Process attributes/directives + eachAttribute(element, function(value, name){ + foreach(self.attrMarkup, function(markup){ + markup.call(selfApi, value, name, element); + }); + }); + eachAttribute(element, function(value, name){ + template.addInit((directiveFns[name]||noop).call(selfApi, value, element)); + }); + } + // Process non text child nodes + if (descend) { + eachNode(element, function(child, i){ + template.addChild(i, self.templatize(child, i, priority)); + }); + } + return template.empty() ? null : template; + } +}; + +function eachTextNode(element, fn){ + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { + if(isTextNode(chld = chldNodes[i])) { + fn(jqLite(chld), i); + } + } +} + +function eachNode(element, fn){ + var i, chldNodes = element[0].childNodes || [], chld; + for (i = 0; i < chldNodes.length; i++) { + if(!isTextNode(chld = chldNodes[i])) { + fn(jqLite(chld), i); + } + } +} + +function eachAttribute(element, fn){ + var i, attrs = element[0].attributes || [], chld, attr, name, value, attrValue = {}; + for (i = 0; i < attrs.length; i++) { + attr = attrs[i]; + name = attr.name.replace(':', '-'); + value = attr.value; + if (msie && name == 'href') { + value = decodeURIComponent(element[0].getAttribute(name, 2)); + } + attrValue[name] = value; + } + foreachSorted(attrValue, fn); +} + |
