(function($)
{
    var rgxp = /\{([^{}]*)}/g;
    // match formatting functions seperately (not used now)
    var fmrgxp = /\{([^{}$]*):([^{}$]*)}/g; 
    var that = this;
    var all_data = null;
    var current_values = null;

    $.fn.tplFromMarkup = function(all_data)
    {
    	that.all_data = all_data;      	
    	var result = fill(unescape(this.html()), all_data);
    	$(this).html(result).show();	
    	
    	return $(this);
    };
    
    $.fn.tplFromData = function(all_data)
    {
    	that.all_data = all_data;
    	
    	var parent_clone = this.parent().clone();
    	var template = parent_clone.empty().append(this.clone()).html();
		
    	var result = '';
    	
    	$.each(all_data, function()
    	{
    		result += fill(unescape(template), this);
    	});
    	
    	this.parent().empty().append(result).show();
    	this.parent().prepend(this);
    	return this;
    }; 
    
    $.fn.tplFromDataAppend = function(all_data)
    {
    	var template = this.show().clone().html();
    	
    	$.each(all_data, function(index, value) {
    		var currentValOfResult = template;
    		template = fill(unescape(template), all_data[index]);
    	
    	});
    	
    	this.hide();
    	this.parent().append(template);
    	    	
    	return $(this);
    }   

    $.fn.tplFromDataReplace = function(all_data)
    {
    	var template = this.show().clone().html();
    	
    	$.each(all_data, function(index, value) {
    		var currentValOfResult = template;
    		template = fill(unescape(template), all_data[index]);
    	
    	});
    	
    	this.html(template);
    	    	
    	return $(this);
    } 

    function fill(template, values)
    {
    	// set the values so they can be accessed by the matcher function
    	this.current_values = values;
    	// run the matcher
    	var result = template.replace(rgxp, matcher);
    	// clear these values out so they don't get confused next time we run this
    	this.current_values = null;
    	
    	return result;	
    }
    
    function matcher(tpl_string, match, pos, tpl_fragment)
    {		
		//debug
		var allVals = this.current_values;
		var matchedVal = this.current_values[match];
		//debug
		
		var fm_fnc_marker = tpl_string.indexOf(':');
		if(fm_fnc_marker >= 0)
		{
			var fm_fnc_string = tpl_string.substring((fm_fnc_marker + 1), (tpl_string.length -1));
			var match = tpl_string.substring(1, fm_fnc_marker);
						
			// execute the function, passing in all the relevant values
			tpl_string = executeFunctionByName(fm_fnc_string, window, tpl_string, match, this.current_values, this.current_values[match]);
		}
		else if(this.current_values[match] || this.current_values[match] === 0)
		{
			tpl_string = this.current_values[match];
		}
		else if(typeof this.current_values === 'undefined' || this.current_values[match] === null || this.current_values[match] === "")
		{
			tpl_string = '';
		}

		return tpl_string;
   	}
   	

	function executeFunctionByName(functionName, context /*, args */) 
	{
		var args = jQuery.makeArray(arguments).slice(2);
		
		var namespaces = functionName.split(".");
		var func = namespaces.pop();
		for(var i = 0; i < namespaces.length; i++) 
		{
		    context = context[namespaces[i]];
		}
		
		return context[func].apply(this, args);
	}
   	     
})(jQuery);