aboutsummaryrefslogtreecommitdiffstats
path: root/src/Compiler.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compiler.js')
-rw-r--r--src/Compiler.js188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/Compiler.js b/src/Compiler.js
new file mode 100644
index 00000000..f4d901fb
--- /dev/null
+++ b/src/Compiler.js
@@ -0,0 +1,188 @@
+/**
+ * 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() {
+ this.paths = [];
+ this.children = [];
+ this.inits = [];
+}
+
+Template.prototype = {
+ init: function(element, scope) {
+ foreach(this.inits, function(fn) {
+ scope.apply(fn, nodeLite(element));
+ });
+
+ var i,
+ childNodes = element.childNodes,
+ children = this.children,
+ paths = this.paths,
+ length = paths.length;
+ for (i = 0; i < length; i++) {
+ children[i].init(childNodes[paths[i]], scope);
+ }
+ },
+
+ addInit:function(init) {
+ if (init) {
+ this.inits.push(init);
+ }
+ },
+
+ setExclusiveInit: function(init) {
+ this.inits = [init];
+ this.addInit = noop;
+ },
+
+
+ addChild: function(index, template) {
+ this.paths.push(index);
+ this.children.push(template);
+ }
+};
+
+///////////////////////////////////
+//NodeLite
+//////////////////////////////////
+
+function NodeLite(element) {
+ this.element = element;
+}
+
+function nodeLite(element) {
+ return element instanceof NodeLite ? element : new NodeLite(element);
+}
+
+NodeLite.prototype = {
+ eachTextNode: function(fn){
+ var i, chldNodes = this.element.childNodes || [], size = chldNodes.length, chld;
+ for (i = 0; i < size; i++) {
+ if((chld = new NodeLite(chldNodes[i])).isText()) {
+ fn(chld, i);
+ }
+ }
+ },
+
+ eachNode: function(fn){
+ var i, chldNodes = this.element.childNodes || [], size = chldNodes.length, chld;
+ for (i = 0; i < size; i++) {
+ if(!(chld = new NodeLite(chldNodes[i])).isText()) {
+ fn(chld, i);
+ }
+ }
+ },
+
+ eachAttribute: function(fn){
+ var i, attrs = this.element.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.element.parentNode.replaceChild(nodeLite(replaceNode).element, this.element);
+ },
+
+ removeAttribute: function(name) {
+ this.element.removeAttribute(name);
+ },
+
+ after: function(element) {
+ this.element.parentNode.insertBefore(element, this.element.nextSibling);
+ },
+
+ attr: function(name, value){
+ if (typeof value == 'undefined') {
+ return this.element.getAttribute(name);
+ } else {
+ this.element.setAttribute(name);
+ }
+ },
+
+ isText: function() { return this.element.nodeType == Node.TEXT_NODE; },
+ text: function() { return this.element.nodeValue; },
+ clone: function() { return nodeLite(this.element.cloneNode(true)); }
+};
+
+///////////////////////////////////
+//Compiler
+//////////////////////////////////
+
+function Compiler(markup, directives, widgets){
+ this.markup = markup;
+ this.directives = directives;
+ this.widgets = widgets;
+}
+
+DIRECTIVE = /^ng-(.*)$/;
+
+Compiler.prototype = {
+ compile: function(element) {
+ var template = this.templetize(nodeLite(element)) || new Template();
+ return function(element){
+ var scope = new Scope();
+ scope.element = element;
+ return {
+ scope: scope,
+ element:element,
+ init: bind(template, template.init, element, scope)
+ };
+ };
+ },
+
+ templetize: function(element){
+ var self = this,
+ markup = self.markup,
+ markupSize = markup.length,
+ directives = self.directives,
+ widgets = self.widgets,
+ recurse = true,
+ exclusive = false,
+ template;
+
+ // process markup for text nodes only
+ element.eachTextNode(function(textNode){
+ for (var i = 0, text = textNode.text(); i < markupSize; i++) {
+ markup[i].call(self, text, textNode, element);
+ }
+ });
+
+ // Process attributes/directives
+ element.eachAttribute(function(name, value){
+ var match = name.match(DIRECTIVE),
+ directive, init;
+ if (!exclusive && match) {
+ directive = directives[match[1]];
+ if (directive) {
+ init = directive.call(self, value, element);
+ template = template || new Template();
+ if (directive.exclusive) {
+ template.setExclusiveInit(init);
+ exclusive = true;
+ } else {
+ template.addInit(init);
+ }
+ recurse = recurse && init;
+ } else {
+ error("Directive '" + match[0] + "' is not recognized.");
+ }
+ }
+ });
+
+ // Process non text child nodes
+ if (recurse) {
+ element.eachNode(function(child, i){
+ var childTemplate = self.templetize(child);
+ if(childTemplate) {
+ template = template || new Template();
+ template.addChild(i, childTemplate);
+ }
+ });
+ }
+ return template;
+ }
+};