
var Template;
(function($){

function textNodes(el) {
  var ret = [];
  el.contents().each( function() {
    var fn = arguments.callee;
	
      if ( this.nodeType == 3 ) 
        ret.push( this );
      else if (!$.nodeName(this, 'iframe')) $(this).contents().each(fn);
  });
  return $(ret);
}

function findTemplateFields(node) {
	if (node.nodeType == 3) {
		var matches;
		if (matches = node.nodeValue.match(/%{[^}]*}/g)) {
			var names = [];
			$.each(matches, function(i, match) {
				var m = /^%{([A-z\-]+)(?::([A-z\-]+))?}$/.exec(match);
				names.push({name:m[1], param:m[2], placeholder:m[0]});
			});

			return [{ value: node.nodeValue, names: names}];
		}
		else {
			return [];
		}
	}
	else {
		var attributes = node.attributes;
		var fields = [];
		
		for (var i = 0; i < attributes.length; ++ i) {
			var attribute = attributes[i], matches;
			
			if (attribute.name == 'altHtml') continue;

			if (matches = attribute.value.match(/%{[^}]*}/g)) {
				var names = [];
				$.each(matches, function(i, match) {
					var m = /^%{([A-z\-]+)(?::([A-z\-]+))?}$/.exec(match);
					names.push({name:m[1], param:m[2], placeholder:m[0]});
				});
				
				fields.push({ attribute: attribute.name, value: attribute.value, names: names});
			}
		}
		
		return fields;
	}
}

function collectTemplateFields(node) {
	var fields = findTemplateFields(node);
	var nodes = [];
	if (fields.length) {
		nodes = nodes.concat({
			node: node,
			fields: fields
		});
	}

	return nodes;
}

Template = function(query, recreates) {
	this.query = query;
	this.process();
	if (recreates) {
		this.recreates = true;
		this.initRecreate();
	}
}

Template.prototype = {
	process: function() {
		var query = this.query;
		var nodes = [];
		
		function processNode(node) {
			var fields = findTemplateFields(node);
			if (fields.length) {
				nodes = nodes.concat({
					node: node,
					fields: fields
				});
			}
		}
		
		processNode(query.get(0));
		query.find('*').each(function(i, node) {
			processNode(node);
		});
		textNodes(query).each(function(i, node) {
			processNode(node);
		});
		
		this.nodes = nodes;
	},

	initRecreate: function() {
		var query = this.query;
		this.templateEl = query.get(0).cloneNode(true);
	},

	substitute: function(data) {
		var prev;
		if (this.recreates) {
			prev = this.query.prev();
			this.query.detach();
			this.query = $(this.templateEl.cloneNode(true));
			this.process();
		}
		
		$.each(this.nodes, function(i, node) {
			$.each(node.fields, function(i, field) {
				var newValue = field.value;
				
				$.each(field.names, function(i, name) {
					var v;
					if (name.param == 'escaped') {
						v = escape(data[name.name]);
					}
					else {
						v = data[name.name];
					}
					
					newValue = newValue.replace(name.placeholder, v);
				});
				
				var attrName = field.attribute;
				if (attrName) {
					if (attrName.substr(0, 2) == 'on') {
						node.node[attrName] = function() {
							return eval(newValue);
						}
					}
					else if (attrName == 'class') {
						node.node.className = newValue;
					}
					else {
						node.node.setAttribute(attrName, newValue);
					}
				}
				else {
					node.node.nodeValue = newValue;
				}
			});
		});
				
		if (this.recreates) {
			prev.after(this.query);
		}
	}
}
	
$.fn.template = function() {
	return new Template(this);
}

})(jQuery);

