﻿/**Javascript Repeater http://jsrepeater.devprog.com/ Version 1.1**/

(function($) {
    $.fn.fillTemplate = function(obj) {

        if ($.fn.fillTemplate.rptInstance == null) {
            $.fn.fillTemplate.rptInstance = new $.fn.fillTemplate.jsRepeater();
        }

        return this.each(function() {

            if ((this.id == null) || (this.id == undefined)) {
                this.innerHTML = "Error: id attribute required";
            } else {
                if (($.fn.fillTemplate.rptInstance.templaters[this.id] == null) || ($.fn.fillTemplate.rptInstance.templaters[this.id] == undefined)) {
                    $.fn.fillTemplate.rptInstance.templaters[this.id] = new $.fn.fillTemplate.templater();
                    var node = this.cloneNode(true);
                    $.fn.fillTemplate.rptInstance.templaters[this.id].initialise(this.cloneNode(true));
                }
                this.innerHTML = $.fn.fillTemplate.rptInstance.templaters[this.id].parse(obj);
            }

        });
    };

    $.fn.fillTemplate.rptInstance = null;

    $.fn.fillTemplate.jsRepeater = function() {
        this.templaters = {};
    };

    $.fn.fillTemplate.templater = function() {
        this.templaters = {}; this.Context = null; this.template = "";
        this.isRoot = true; this.mirrorID = null;
    };

    var templater = $.fn.fillTemplate.templater;

    templater.prototype.initialise = function(rootNode) {
        if ((rootNode.getAttribute) && (rootNode.getAttribute("context"))) {
            this.Context = rootNode.getAttributeNode("context").nodeValue;
        }
        for (var i = 0; i < rootNode.childNodes.length; i++) {
            this.extractSubTemplates(rootNode, rootNode.childNodes[i]);
        }
        if (!this.isRoot) {
            var tempNode = document.createElement("div");
            tempNode.appendChild(rootNode.cloneNode(true));
            this.template = tempNode.innerHTML;
        } else { this.template = rootNode.innerHTML; }
        this.template = this.template.replace(/%7B/g, "{");
        this.template = this.template.replace(/%7D/g, "}");
    };
    templater.prototype.initialiseMirror = function(rootNode) {
        var tempNode = document.createElement("div");
        tempNode.appendChild(rootNode.cloneNode(false));
        var Marker = document.createTextNode("STATIC");
        tempNode.childNodes[0].appendChild(Marker);
        this.template = tempNode.innerHTML;
    };
    templater.prototype.parseOrdering = function(template, ordinal, total) {
        template = template.replace(/%{([^}]*)}/g,
            function(match, group1) {
                var first = null;
                var alternates = null;
                var last = null;

                if (group1.indexOf("|") > -1) {
                    var ary = group1.split("|");
                    first = ary[0];
                    alternates = ary[1];
                    if (ary.length > 2) { last = ary[2]; }
                } else {
                    alternates = group1;
                }
                alternates = alternates.split(":");

                if ((ordinal == 0) && (first != null)) { return first; }
                if ((ordinal == total - 1) && (last != null)) { return last; }
                return alternates[ordinal % alternates.length];
            });
        return template;
    };
    templater.prototype.parseRecursionOrdering = function(template, data, recursionCount, ob) {
        template = template.replace(/!%{([^}]*)}/g,
            function(match, group1) {
                var first = null;
                var alternates = null;
                var last = null;

                if (group1.indexOf("|") > -1) {
                    var ary = group1.split("|");
                    first = ary[0];
                    alternates = ary[1];
                    if (ary.length > 2) { last = ary[2]; }
                } else {
                    alternates = group1;
                }
                alternates = alternates.split(":");

                if ((recursionCount == 0) && (first != null)) { return first; }
                //if ((ordinal == total - 1) && (last != null)) { return last; }
                return alternates[recursionCount % alternates.length];
            });
        return template;
    };
    templater.prototype.parseNumbering = function(template, ordinal, total) {
        template = template.replace(/#{([^}]*)}/g,
            function(match, group1) {
                return ordinal + 1;
            });
        return template;
    };
    templater.prototype.parseRecursive = function(template, data, recursionCount, ob) {

        template = template.replace(/!{([^}]*)}/g,
        function(match, group1) {
            if (group1 > recursionCount) {
                if (ob.Context == null) { return ""; }
                var contextData = data[ob.Context];
                if ((contextData == null) || (contextData == undefined)) { return ""; }
                return ob.parse(data, recursionCount + 1);
            } else { return ""; }
        });
        return template;
    };
    templater.prototype.parse = function(data, recursionCount) {
        var result = "";
        var self = this;
        if (this.mirrorID) {
            result += this.template.replace(/(STATIC)/g,
                    function(match, group1) {
                        return document.getElementById(self.mirrorID).innerHTML;

                    });
            return result;
        }
        if ((recursionCount == null) || (recursionCount == undefined)) { recursionCount = 0; }
        var contextData = null;
        if (this.Context) { contextData = data[this.Context]; } else { contextData = data; }
        if ((contextData == null) || (contextData == undefined)) { contextData = {}; }
        if (contextData instanceof Array) {
            for (var i = 0; i < contextData.length; i++) {
                var obj = contextData[i];
                result += this.template.replace(/\$\{([^}]*)\}/g,
                    function(match, group1) {
                        var outer = group1.split(":");
                        var val = outer[0];
                        var f = null;
                        if (outer.length > 1) { f = outer[1]; }
                        var ary = val.split(".");
                        var newObj = obj;

                        for (var j = 0; j < ary.length; j++) {
                            newObj = newObj[ary[j]];

                            if (newObj == undefined) {
                                if (f != null) { return eval(f + '(newObj);'); }
                                else { return newObj; }
                            }
                        }
                        if (f != null) { return eval(f + '(newObj);'); }
                        else { return newObj; }
                    });
                var self = this;
                result = this.parseNumbering(result, i, contextData.length);
                result = this.parseRecursionOrdering(result, contextData[i], recursionCount, this);
                result = this.parseOrdering(result, i, contextData.length);

                result = this.parseRecursive(result, contextData[i], recursionCount, this);


                result = result.replace(/\~\{([^}]*)\}/g,
                        function(match, group1) {
                            return self.templaters[group1].parse(contextData[i]);
                        });
            }
        } else {
            var obj = contextData;

            result += this.template.replace(/\$\{([^}]*)\}/g,
                    function(match, group1) {
                        var outer = group1.split(":");
                        var val = outer[0];
                        var f = null;
                        if (outer.length > 1) { f = outer[1]; }

                        ary = val.split(".");
                        var newObj = obj;

                        for (var j = 0; j < ary.length; j++) {
                            newObj = newObj[ary[j]];

                            if (newObj == undefined) {
                                if (f != null) { return eval(f + '(newObj);'); }
                                else { return newObj; }
                            }
                        }
                        if (f != null) { return eval(f + '(newObj);'); }
                        else { return newObj; }
                    });
            var self = this;
            result = this.parseNumbering(result, i, contextData.length);
            result = this.parseRecursionOrdering(result, contextData, recursionCount, this);
            result = this.parseOrdering(result, 0, 1);

            result = this.parseRecursive(result, contextData, recursionCount, this);


            result = result.replace(/\~\{([^}]*)\}/g,
            function(match, group1) {
                return self.templaters[group1].parse(contextData);
            });
        }

        return result;
    };
    templater.prototype.extractSubTemplates = function(sourceTree, node) {
        var plucked = null;
        var markerID = null;
        var markerNode = null;
        var Subtemplater = null;
        if ((node.getAttribute) && (node.getAttribute("template"))) {
            plucked = node;
            markerID = this.newGuid();
            markerNode = document.createTextNode("~{" + markerID + "}");
            sourceTree.replaceChild(markerNode, node);
            Subtemplater = new $.fn.fillTemplate.templater();
            Subtemplater.isRoot = false;
            Subtemplater.mirrorID = node.getAttributeNode("ID").nodeValue;
            this.templaters[markerID] = Subtemplater;
            Subtemplater.initialiseMirror(plucked);
            return;
        }
        if ((node.getAttribute) && (node.getAttribute("context"))) {

            plucked = node;
            markerID = this.newGuid();
            markerNode = document.createTextNode("~{" + markerID + "}");

            sourceTree.replaceChild(markerNode, node);

            Subtemplater = new $.fn.fillTemplate.templater();
            Subtemplater.isRoot = false;
            this.templaters[markerID] = Subtemplater;
            Subtemplater.initialise(plucked);
        }
        else {
            for (var i = 0; i < node.childNodes.length; i++) {
                this.extractSubTemplates(node, node.childNodes[i]);
            }
        }
    };
    templater.prototype.S4 = function() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    };
    templater.prototype.newGuid = function() {
        return (this.S4() + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4()).toUpperCase();
    };


})(jQuery);