aboutsummaryrefslogtreecommitdiffstats
path: root/src/Compiler.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compiler.js')
-rw-r--r--src/Compiler.js212
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);
+}
+