﻿(function($)
{
    $.fn.bindTo = function(data, options)
    {
        var defaults = {
            appendTo: null,
            fillTo: null,
            root: 'data',
            onBind: null,
            onBound: null,
            fill: false
        }
        var options = $.extend(defaults, options);

        if ($.isFunction(options.onBind))
            options.onBind();
        var template = '<!--bind.template-->' + ((options.fill) ? this.html() : this.parent().html()).replace(/%7B/g, '{').replace(/%7D/g, '}') + '<!--bind.template-->';
        var repeaters = this.bindTo.findRepeaters(template);
        var fixedData = {};
        fixedData[options.root] = data;
        var content = this.bindTo.traverse(
									  'bind.template',
									  fixedData,
									  repeaters['<!--bind.template-->'].template,
									  repeaters,
									  'bind.template'
									 );
        if (options.fill)
            this.html(content);
        if (options.appendTo != null)
            content = $(content).appendTo($(options.appendTo));
        if (options.fillTo != null)
            $(options.fillTo).html(content);
        if ($.isFunction(options.onBound))
            options.onBound(content, data);
        return content;
    }
    $.extend($.fn.bindTo,
	{
	    templates: {},
	    traverse: function(key, data, template, repeaters, parent)
	    {
	        if (typeof data == 'string' || typeof data == 'number' || typeof data == 'boolean')
	        {
	            return template.replace(new RegExp("\{" + key + "\}", "ig"), data);
	        } else if (typeof data == 'object')
	        {
	            if (typeof data.length == 'undefined')
	            {
	                if (repeaters['<!--' + parent + '-->'].action)
	                    template = $.fn.bindTo[repeaters['<!--' + parent + '-->'].action](template, data) || template;
	                for (var item in data)
	                {
	                    if (typeof data[item] == 'object')
	                    {
	                        if (typeof repeaters['<!--' + item + '-->'] != 'undefined')
	                        {//Skip not defined templates for child in aggregate object
	                            var temp = $.fn.bindTo.traverse(item, data[item], repeaters['<!--' + item + '-->'].template, repeaters, item);
	                            template = template.replace('<!--' + item + '-->', temp);
	                        }
	                    } else
	                    {
	                        var temp = $.fn.bindTo.traverse(item, data[item], template, repeaters);
	                        template = temp;
	                    }
	                }
	                return template;
	            } else
	            {
	                var listTemplate = '';
	                for (var item in data)
	                {
	                    listTemplate += $.fn.bindTo.traverse(item, data[item], repeaters['<!--' + key + '-->'].template, repeaters, key);
	                }
	                return listTemplate;
	            }
	        }
	        return ''; //(handle extjs) this will return '' for function in case you extned array or object
	    },
	    findRepeaters: function(template)
	    {
	        $this = this;
	        var templates = {};
	        var reg = new RegExp('<!--[.a-zA-Z1-9]*-->', 'g');
	        var regAction = new RegExp('<!--action:[$.a-zA-Z1-9]*-->', 'g');
	        var matches = (template.match(reg));
	        $.each(matches,
				   function()
				   {
				       if (templates[this] != undefined)/*template is already added because end tag and start tags are the same*/
				           return true;
				       templates[this] = {};
				       var temp = template.substring(template.indexOf(this) + this.length, template.lastIndexOf(this));
				       var innerMatches = (temp.match(reg)) || [];
				       $.each(innerMatches,
							   function()
							   {
							       if (temp.indexOf(this) > -1)
							       {
							           var innerRepeater = temp.substring(temp.indexOf(this), temp.lastIndexOf(this) + this.length);
							           temp = temp.replace(innerRepeater, this);
							       }
							   }
						)
				       var actions = (temp.match(regAction)) || [];
				       var key = this;
				       $.each(actions,
							   function()
							   {
							       var action = this.substring(11, this.length - 3);
							       templates[key].action = action;
							       temp = temp.replace(actions, '');
							   }
						)
				       templates[this].template = temp;
				   }
			)
	        return templates;
	    }
	}
);
    //
})(jQuery);
