/**
 * jTemplates 0.7.8 (http://jtemplates.tpython.com)
 * Copyright (c) 2007-2009 Tomasz Gloc (http://www.tpython.com)
 * 
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and/or GPL (GPL-LICENSE.txt) licenses.
 *
 * Id: $Id: jquery-jtemplates_uncompressed.js 177 2009-04-02 17:36:36Z tom $
 */
 
 /**
 * @fileOverview Template engine in JavaScript.
 * @name jTemplates
 * @author Tomasz Gloc
 * @date $Date: 2009-04-02 19:36:36 +0200 (Cz, 02 kwi 2009) $
 */


if(window.jQuery && !window.jQuery.createTemplate) {(function (jQuery) {

    /**
    * [abstract]
    * @name BaseNode
    * @class Abstract node. [abstract]
    */

    /**
    * Process node and get the html string. [abstract]
    * @name get
    * @function
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    * @memberOf BaseNode
    */

    /**
    * [abstract]
    * @name BaseArray
    * @augments BaseNode
    * @class Abstract array/collection. [abstract]
    */

    /**
    * Add node 'e' to array.
    * @name push
    * @function
    * @param {BaseNode} e a node
    * @memberOf BaseArray
    */

    /**
    * See (http://jquery.com/).
    * @name jQuery
    * @class jQuery Library (http://jquery.com/)
    */

    /**
    * See (http://jquery.com/)
    * @name fn
    * @class jQuery Library (http://jquery.com/)
    * @memberOf jQuery
    */


    /**
    * Create new template from string s.
    * @name Template
    * @class A template or multitemplate.
    * @param {string} s A template string (like: "Text: {$T.txt}.").
    * @param {array} [includes] Array of included templates.
    * @param {object} [settings] Settings.
    * @config {boolean} [disallow_functions] Do not allow use function in data (default: true).
    * @config {boolean} [filter_data] Enable filter data using escapeHTML (default: true).
    * @config {boolean} [filter_params] Enable filter parameters using escapeHTML (default: false).
    * @config {boolean} [runnable_functions] Automatically run function (from data) inside {} [default: false].
    * @config {boolean} [clone_data] Clone input data [default: true]
    * @config {boolean} [clone_params] Clone input parameters [default: true]
    * @config {Function} [f_cloneData] Function using to data cloning
    * @config {Function} [f_escapeString] Function using to escape strings
    * @augments BaseNode
    */
    var Template = function (s, includes, settings) {
        this._tree = [];
        this._param = {};
        this._includes = null;
        this._templates = {};
        this._templates_code = {};

        this.settings = jQuery.extend({
            disallow_functions: false,
            filter_data: true,
            filter_params: false,
            runnable_functions: false,
            clone_data: true,
            clone_params: true
        }, settings);

        this.f_cloneData = (this.settings.f_cloneData !== undefined) ? (this.settings.f_cloneData) : (TemplateUtils.cloneData);
        this.f_escapeString = (this.settings.f_escapeString !== undefined) ? (this.settings.f_escapeString) : (TemplateUtils.escapeHTML);

        this.splitTemplates(s, includes);

        if (s) {
            this.setTemplate(this._templates_code['MAIN'], includes, this.settings);
        }

        this._templates_code = null;
    };

    /**
    * jTemplates version
    * @type string
    */
    Template.prototype.version = '0.7.8';

    /**
    * Debug mode (all errors are on), default: on
    * @type Boolean
    */
    Template.DEBUG_MODE = true;

    /**
    * Split multitemplate into multiple templates.
    * @param {string} s A template string (like: "Text: {$T.txt}.").
    * @param {array} includes Array of included templates.
    */
    Template.prototype.splitTemplates = function (s, includes) {
        var reg = /\{#template *(\w*?)( .*)*\}/g;
        var iter, tname, se;
        var lastIndex = null;

        var _template_settings = [];

        while ((iter = reg.exec(s)) != null) {
            lastIndex = reg.lastIndex;
            tname = iter[1];
            se = s.indexOf('{#/template ' + tname + '}', lastIndex);
            if (se == -1) {
                throw new Error('jTemplates: Template "' + tname + '" is not closed.');
            }
            this._templates_code[tname] = s.substring(lastIndex, se);
            _template_settings[tname] = TemplateUtils.optionToObject(iter[2]);
        }
        if (lastIndex === null) {
            this._templates_code['MAIN'] = s;
            return;
        }

        for (var i in this._templates_code) {
            if (i != 'MAIN') {
                this._templates[i] = new Template();
            }
        }
        for (var i in this._templates_code) {
            if (i != 'MAIN') {
                this._templates[i].setTemplate(this._templates_code[i], jQuery.extend({}, includes || {}, this._templates || {}), jQuery.extend({}, this.settings, _template_settings[i]));
                this._templates_code[i] = null;
            }
        }
    };

    /**
    * Parse template. (should be template, not multitemplate).
    * @param {string} s A template string (like: "Text: {$T.txt}.").
    * @param {array} includes Array of included templates.
    */
    Template.prototype.setTemplate = function (s, includes, settings) {
        if (s == undefined) {
            this._tree.push(new TextNode('', 1, this));
            return;
        }
        s = s.replace(/[\n\r]/g, '');
        s = s.replace(/\{\*.*?\*\}/g, '');
        this._includes = jQuery.extend({}, this._templates || {}, includes || {});
        this.settings = new Object(settings);
        var node = this._tree;
        var op = s.match(/\{#.*?\}/g);
        var ss = 0, se = 0;
        var e;
        var literalMode = 0;
        var elseif_level = 0;

        for (var i = 0, l = (op) ? (op.length) : (0); i < l; ++i) {
            var this_op = op[i];

            if (literalMode) {
                se = s.indexOf('{#/literal}');
                if (se == -1) {
                    throw new Error("jTemplates: No end of literal.");
                }
                if (se > ss) {
                    node.push(new TextNode(s.substring(ss, se), 1, this));
                }
                ss = se + 11;
                literalMode = 0;
                i = jQuery.inArray('{#/literal}', op);
                continue;
            }
            se = s.indexOf(this_op, ss);
            if (se > ss) {
                node.push(new TextNode(s.substring(ss, se), literalMode, this));
            }
            var ppp = this_op.match(/\{#([\w\/]+).*?\}/);
            var op_ = RegExp.$1;
            switch (op_) {
                case 'elseif':
                    ++elseif_level;
                    node.switchToElse();
                    //no break
                case 'if':
                    e = new opIF(this_op, node);
                    node.push(e);
                    node = e;
                    break;
                case 'else':
                    node.switchToElse();
                    break;
                case '/if':
                    while (elseif_level) {
                        node = node.getParent();
                        --elseif_level;
                    }
                    //no break
                case '/for':
                case '/foreach':
                    node = node.getParent();
                    break;
                case 'foreach':
                    e = new opFOREACH(this_op, node, this);
                    node.push(e);
                    node = e;
                    break;
                case 'for':
                    e = opFORFactory(this_op, node, this);
                    node.push(e);
                    node = e;
                    break;
                case 'continue':
                case 'break':
                    node.push(new JTException(op_));
                    break;
                case 'include':
                    node.push(new Include(this_op, this._includes));
                    break;
                case 'param':
                    node.push(new UserParam(this_op));
                    break;
                case 'cycle':
                    node.push(new Cycle(this_op));
                    break;
                case 'ldelim':
                    node.push(new TextNode('{', 1, this));
                    break;
                case 'rdelim':
                    node.push(new TextNode('}', 1, this));
                    break;
                case 'literal':
                    literalMode = 1;
                    break;
                case '/literal':
                    if (Template.DEBUG_MODE) {
                        throw new Error("jTemplates: Missing begin of literal.");
                    }
                    break;
                default:
                    if (Template.DEBUG_MODE) {
                        throw new Error('jTemplates: unknown tag: ' + op_ + '.');
                    }
            }

            ss = se + this_op.length;
        }

        if (s.length > ss) {
            node.push(new TextNode(s.substr(ss), literalMode, this));
        }
    };

    /**
    * Process template and get the html string.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    Template.prototype.get = function (d, param, element, deep) {
        ++deep;

        var $T = d, _param1, _param2;

        if (this.settings.clone_data) {
            $T = this.f_cloneData(d, { escapeData: (this.settings.filter_data && deep == 1), noFunc: this.settings.disallow_functions }, this.f_escapeString);
        }

        if (!this.settings.clone_params) {
            _param1 = this._param;
            _param2 = param;
        } else {
            _param1 = this.f_cloneData(this._param, { escapeData: (this.settings.filter_params), noFunc: false }, this.f_escapeString);
            _param2 = this.f_cloneData(param, { escapeData: (this.settings.filter_params && deep == 1), noFunc: false }, this.f_escapeString);
        }
        var $P = jQuery.extend({}, _param1, _param2);

        var $Q = (element != undefined) ? (element) : ({});
        $Q.version = this.version;

        var ret = '';
        for (var i = 0, l = this._tree.length; i < l; ++i) {
            ret += this._tree[i].get($T, $P, $Q, deep);
        }

        --deep;
        return ret;
    };

    /**
    * Set to parameter 'name' value 'value'.
    * @param {string} name
    * @param {object} value
    */
    Template.prototype.setParam = function (name, value) {
        this._param[name] = value;
    };


    /**
    * Template utilities.
    * @namespace Template utilities.
    */
    TemplateUtils = function () {
    };

    /**
    * Replace chars &, >, <, ", ' with html entities.
    * To disable function set settings: filter_data=false, filter_params=false
    * @param {string} string
    * @return {string}
    * @static
    * @memberOf TemplateUtils
    */
    TemplateUtils.escapeHTML = function (txt) {
        return txt.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
    };

    /**
    * Make a copy od data 'd'. It also filters data (depend on 'filter').
    * @param {object} d input data
    * @param {object} filter a filters
    * @config {boolean} [escapeData] Use escapeHTML on every string.
    * @config {boolean} [noFunc] Do not allow to use function (throws exception).
    * @param {Function} f_escapeString function using to filter string (usually: TemplateUtils.escapeHTML)
    * @return {object} output data (filtered)
    * @static
    * @memberOf TemplateUtils
    */
    TemplateUtils.cloneData = function (d, filter, f_escapeString) {
        if (d == null) {
            return d;
        }
        switch (d.constructor) {
            case Object:
                var o = {};
                for (var i in d) {
                    o[i] = TemplateUtils.cloneData(d[i], filter, f_escapeString);
                }
                if (!filter.noFunc) {
                    if (d.hasOwnProperty("toString"))
                        o.toString = d.toString;
                }
                return o;
            case Array:
                var o = [];
                for (var i = 0, l = d.length; i < l; ++i) {
                    o[i] = TemplateUtils.cloneData(d[i], filter, f_escapeString);
                }
                return o;
            case String:
                return (filter.escapeData) ? (f_escapeString(d)) : (d);
            case Function:
                if (filter.noFunc) {
                    if (Template.DEBUG_MODE)
                        throw new Error("jTemplates: Functions are not allowed.");
                    else
                        return undefined;
                }
                //no break
            default:
                return d;
        }
    };

    /**
    * Convert text-based option string to Object
    * @param {string} optionText text-based option string
    * @return {Object}
    * @static
    * @memberOf TemplateUtils
    */
    TemplateUtils.optionToObject = function (optionText) {
        if (optionText === null || optionText === undefined) {
            return {};
        }

        var o = optionText.split(/[= ]/);
        if (o[0] === '') {
            o.shift();
        }

        var obj = {};
        for (var i = 0, l = o.length; i < l; i += 2) {
            obj[o[i]] = o[i + 1];
        }

        return obj;
    };


    /**
    * Create a new text node.
    * @name TextNode
    * @class All text (block {..}) between control's block "{#..}".
    * @param {string} val text string
    * @param {boolean} literalMode When enable (true) template does not process blocks {..}.
    * @param {Template} Template object
    * @augments BaseNode
    */
    var TextNode = function (val, literalMode, template) {
        this._value = val;
        this._literalMode = literalMode;
        this._template = template;
    };

    /**
    * Get the html string for a text node.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    TextNode.prototype.get = function (d, param, element, deep) {
        var __t = this._value;
        if (!this._literalMode) {
            var __template = this._template;
            var $T = d;
            var $P = param;
            var $Q = element;
            __t = __t.replace(/\{(.*?)\}/g, function (__0, __1) {
                try {
                    var __tmp = eval(__1);
                    if (typeof __tmp == 'function') {
                        if (__template.settings.disallow_functions || !__template.settings.runnable_functions) {
                            return '';
                        } else {
                            __tmp = __tmp($T, $P, $Q);
                        }
                    }
                    return (__tmp === undefined) ? ("") : (String(__tmp));
                } catch (e) {
                    if (Template.DEBUG_MODE) {
                        if (e instanceof JTException)
                            e.type = "subtemplate";
                        throw e;
                    }
                    return "";
                }
            });
        }
        return __t;
    };

    /**
    * Create a new conditional node.
    * @name opIF
    * @class A class represent: {#if}.
    * @param {string} oper content of operator {#..}
    * @param {object} par parent node
    * @augments BaseArray
    */
    var opIF = function (oper, par) {
        this._parent = par;
        oper.match(/\{#(?:else)*if (.*?)\}/);
        this._cond = RegExp.$1;
        this._onTrue = [];
        this._onFalse = [];
        this._currentState = this._onTrue;
    };

    /**
    * Add node 'e' to array.
    * @param {BaseNode} e a node
    */
    opIF.prototype.push = function (e) {
        this._currentState.push(e);
    };

    /**
    * Get a parent node.
    * @return {BaseNode}
    */
    opIF.prototype.getParent = function () {
        return this._parent;
    };

    /**
    * Switch from collection onTrue to onFalse.
    */
    opIF.prototype.switchToElse = function () {
        this._currentState = this._onFalse;
    };

    /**
    * Process node depend on conditional and get the html string.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    opIF.prototype.get = function (d, param, element, deep) {
        var $T = d;
        var $P = param;
        var $Q = element;
        var ret = '';

        try {
            var tab = (eval(this._cond)) ? (this._onTrue) : (this._onFalse);
            for (var i = 0, l = tab.length; i < l; ++i) {
                ret += tab[i].get(d, param, element, deep);
            }
        } catch (e) {
            if (Template.DEBUG_MODE || (e instanceof JTException)) {
                //                throw e;
                if ($.browser.msie && !SF.Tools.IsNull(this.ExThrowed)) {
                    // alert(e);
                }
                else {
                    this.ExThrowed = true;
                    throw e;
                }
            }
        }
        return ret;
    };

    /**
    * Handler for a tag 'FOR'. Create new and return relative opFOREACH object.
    * @name opFORFactory
    * @class Handler for a tag 'FOR'. Create new and return relative opFOREACH object.
    * @param {string} oper content of operator {#..}
    * @param {object} par parent node
    * @param {Template} template a pointer to Template object
    * @return {opFOREACH}
    */
    opFORFactory = function (oper, par, template) {
        if (oper.match(/\{#for (\w+?) *= *(\S+?) +to +(\S+?) *(?:step=(\S+?))*\}/)) {
            oper = '{#foreach opFORFactory.funcIterator as ' + RegExp.$1 + ' begin=' + (RegExp.$2 || 0) + ' end=' + (RegExp.$3 || -1) + ' step=' + (RegExp.$4 || 1) + ' extData=$T}';
            return new opFOREACH(oper, par, template);
        } else {
            throw new Error('jTemplates: Operator failed "find": ' + oper);
        }
    };

    /**
    * Function returns inputs data (using internal with opFORFactory)
    * @param {object} i any data
    * @return {object} any data (equal parameter 'i')
    * @private
    * @static
    */
    opFORFactory.funcIterator = function (i) {
        return i;
    };

    /**
    * Create a new loop node.
    * @name opFOREACH
    * @class A class represent: {#foreach}.
    * @param {string} oper content of operator {#..}
    * @param {object} par parent node
    * @param {Template} template a pointer to Template object
    * @augments BaseArray
    */
    var opFOREACH = function (oper, par, template) {
        this._parent = par;
        this._template = template;
        oper.match(/\{#foreach (.+?) as (\w+?)( .+)*\}/);
        this._arg = RegExp.$1;
        this._name = RegExp.$2;
        this._option = RegExp.$3 || null;
        this._option = TemplateUtils.optionToObject(this._option);

        this._onTrue = [];
        this._onFalse = [];
        this._currentState = this._onTrue;
    };

    /**
    * Add node 'e' to array.
    * @param {BaseNode} e
    */
    opFOREACH.prototype.push = function (e) {
        this._currentState.push(e);
    };

    /**
    * Get a parent node.
    * @return {BaseNode}
    */
    opFOREACH.prototype.getParent = function () {
        return this._parent;
    };

    /**
    * Switch from collection onTrue to onFalse.
    */
    opFOREACH.prototype.switchToElse = function () {
        this._currentState = this._onFalse;
    };

    /**
    * Process loop and get the html string.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    opFOREACH.prototype.get = function (d, param, element, deep) {
        try {
            var $T = d;
            var $P = param;
            var $Q = element;
            var fcount = eval(this._arg); //array of elements in foreach
            var key = []; //only for objects
            var mode = typeof fcount;
            if (mode == 'object') {
                var arr = [];
                jQuery.each(fcount, function (k, v) {
                    key.push(k);
                    arr.push(v);
                });
                fcount = arr;
            }
            var extData = (this._option.extData !== undefined) ? (eval(this._option.extData)) : (($T != null) ? ($T) : ({}));
            var s = Number(eval(this._option.begin) || 0), e; //start, end
            var step = Number(eval(this._option.step) || 1);
            if (mode != 'function') {
                e = fcount.length;
            } else {
                if (this._option.end === undefined || this._option.end === null) {
                    e = Number.MAX_VALUE;
                } else {
                    e = Number(eval(this._option.end)) + ((step > 0) ? (1) : (-1));
                }
            }
            var ret = ''; //returned string
            var i, l; //iterators

            if (this._option.count) {
                var tmp = s + Number(eval(this._option.count));
                e = (tmp > e) ? (e) : (tmp);
            }
            if ((e > s && step > 0) || (e < s && step < 0)) {
                var iteration = 0;
                var _total = (mode != 'function') ? (Math.ceil((e - s) / step)) : undefined;
                var ckey, cval; //current key, current value
                for (; ((step > 0) ? (s < e) : (s > e)); s += step, ++iteration) {
                    ckey = key[s];
                    if (mode != 'function') {
                        cval = fcount[s];
                    } else {
                        cval = fcount(s);
                        if (cval === undefined || cval === null) {
                            break;
                        }
                    }
                    if ((typeof cval == 'function') && (this._template.settings.disallow_functions || !this._template.settings.runnable_functions)) {
                        continue;
                    }
                    if ((mode == 'object') && (ckey in Object)) {
                        continue;
                    }
                    var prevValue = extData[this._name];
                    extData[this._name] = cval;
                    extData[this._name + '$index'] = s;
                    extData[this._name + '$iteration'] = iteration;
                    extData[this._name + '$first'] = (iteration == 0);
                    extData[this._name + '$last'] = (s + step >= e);
                    extData[this._name + '$total'] = _total;
                    extData[this._name + '$key'] = (ckey !== undefined && ckey.constructor == String) ? (this._template.f_escapeString(ckey)) : (ckey);
                    extData[this._name + '$typeof'] = typeof cval;
                    for (i = 0, l = this._onTrue.length; i < l; ++i) {
                        try {
                            ret += this._onTrue[i].get(extData, param, element, deep);
                        } catch (ex) {
                            if (ex instanceof JTException) {
                                switch (ex.type) {
                                    case 'continue':
                                        i = l;
                                        break;
                                    case 'break':
                                        i = l;
                                        s = e;
                                        break;
                                    default:
                                        throw e;
                                }
                            } else {
                                throw e;
                            }
                        }
                    }
                    delete extData[this._name + '$index'];
                    delete extData[this._name + '$iteration'];
                    delete extData[this._name + '$first'];
                    delete extData[this._name + '$last'];
                    delete extData[this._name + '$total'];
                    delete extData[this._name + '$key'];
                    delete extData[this._name + '$typeof'];
                    delete extData[this._name];
                    extData[this._name] = prevValue;
                }
            } else {
                for (i = 0, l = this._onFalse.length; i < l; ++i) {
                    ret += this._onFalse[i].get($T, param, element, deep);
                }
            }
            return ret;
        } catch (e) {
            if (Template.DEBUG_MODE || (e instanceof JTException))
                throw e;
            return "";
        }
    };

    /**
    * Template-control exceptions
    * @name JTException
    * @class A class used internals for a template-control exceptions
    * @param type {string} Type of exception
    * @augments Error
    * @augments BaseNode
    */
    var JTException = function (type) {
        this.type = type;
    };
    JTException.prototype = Error;

    /**
    * Throw a template-control exception
    * @throws It throws itself
    */
    JTException.prototype.get = function (d) {
        throw this;
    };

    /**
    * Create a new entry for included template.
    * @name Include
    * @class A class represent: {#include}.
    * @param {string} oper content of operator {#..}
    * @param {array} includes
    * @augments BaseNode
    */
    var Include = function (oper, includes) {
        oper.match(/\{#include (.*?)(?: root=(.*?))?\}/);
        this._template = includes[RegExp.$1];
        if (this._template == undefined) {
            if (Template.DEBUG_MODE)
                throw new Error('jTemplates: Cannot find include: ' + RegExp.$1);
        }
        this._root = RegExp.$2;
    };

    /**
    * Run method get on included template.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    Include.prototype.get = function (d, param, element, deep) {
        var $T = d;
        var $P = param;
        try {
            return this._template.get(eval(this._root), param, element, deep);
        } catch (e) {
            if (Template.DEBUG_MODE || (e instanceof JTException))
                throw e;
        }
        return '';
    };

    /**
    * Create new node for {#param}.
    * @name UserParam
    * @class A class represent: {#param}.
    * @param {string} oper content of operator {#..}
    * @augments BaseNode
    */
    var UserParam = function (oper) {
        oper.match(/\{#param name=(\w*?) value=(.*?)\}/);
        this._name = RegExp.$1;
        this._value = RegExp.$2;
    };

    /**
    * Return value of selected parameter.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String} empty string
    */
    UserParam.prototype.get = function (d, param, element, deep) {
        var $T = d;
        var $P = param;
        var $Q = element;

        try {
            param[this._name] = eval(this._value);
        } catch (e) {
            if (Template.DEBUG_MODE || (e instanceof JTException))
                throw e;
            param[this._name] = undefined;
        }
        return '';
    };

    /**
    * Create a new cycle node.
    * @name Cycle
    * @class A class represent: {#cycle}.
    * @param {string} oper content of operator {#..}
    * @augments BaseNode
    */
    var Cycle = function (oper) {
        oper.match(/\{#cycle values=(.*?)\}/);
        this._values = eval(RegExp.$1);
        this._length = this._values.length;
        if (this._length <= 0) {
            throw new Error('jTemplates: cycle has no elements');
        }
        this._index = 0;
        this._lastSessionID = -1;
    };

    /**
    * Do a step on cycle and return value.
    * @param {object} d data
    * @param {object} param parameters
    * @param {Element} element a HTML element
    * @param {Number} deep
    * @return {String}
    */
    Cycle.prototype.get = function (d, param, element, deep) {
        var sid = jQuery.data(element, 'jTemplateSID');
        if (sid != this._lastSessionID) {
            this._lastSessionID = sid;
            this._index = 0;
        }
        var i = this._index++ % this._length;
        return this._values[i];
    };


    /**
    * Add a Template to HTML Elements.
    * @param {Template/string} s a Template or a template string
    * @param {array} [includes] Array of included templates.
    * @param {object} [settings] Settings (see Template)
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.setTemplate = function (s, includes, settings) {
        if (s.constructor === Template) {
            return jQuery(this).each(function () {
                jQuery.data(this, 'jTemplate', s);
                jQuery.data(this, 'jTemplateSID', 0);
            });
        } else {
            return jQuery(this).each(function () {
                jQuery.data(this, 'jTemplate', new Template(s, includes, settings));
                jQuery.data(this, 'jTemplateSID', 0);
            });
        }
    };

    /**
    * Add a Template (from URL) to HTML Elements.
    * @param {string} url_ URL to template
    * @param {array} [includes] Array of included templates.
    * @param {object} [settings] Settings (see Template)
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.setTemplateURL = function (url_, includes, settings) {
        var s = jQuery.ajax({
            url: url_,
            async: false
        }).responseText;

        return jQuery(this).setTemplate(s, includes, settings);
    };

    /**
    * Create a Template from element's content.
    * @param {string} elementName an ID of element
    * @param {array} [includes] Array of included templates.
    * @param {object} [settings] Settings (see Template)
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.setTemplateElement = function (elementName, includes, settings) {
        var s = jQuery('#' + elementName).val();
        if (s == null) {
            s = jQuery('#' + elementName).html();
            s = s.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
        }

        s = jQuery.trim(s);
        s = s.replace(/^<\!\[CDATA\[([\s\S]*)\]\]>$/im, '$1');
        s = s.replace(/^<\!--([\s\S]*)-->$/im, '$1');

        return jQuery(this).setTemplate(s, includes, settings);
    };

    /**
    * Check it HTML Elements have a template. Return count of templates.
    * @return {number} Number of templates.
    * @memberOf jQuery.fn
    */
    jQuery.fn.hasTemplate = function () {
        var count = 0;
        jQuery(this).each(function () {
            if (jQuery.getTemplate(this)) {
                ++count;
            }
        });
        return count;
    };

    /**
    * Remote Template from HTML Element(s)
    * @return {jQuery} chainable jQuery class
    */
    jQuery.fn.removeTemplate = function () {
        jQuery(this).processTemplateStop();
        return jQuery(this).each(function () {
            jQuery.removeData(this, 'jTemplate');
        });
    };

    /**
    * Set to parameter 'name' value 'value'.
    * @param {string} name
    * @param {object} value
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.setParam = function (name, value) {
        return jQuery(this).each(function () {
            var t = jQuery.getTemplate(this);
            if (t === undefined) {
                if (Template.DEBUG_MODE)
                    throw new Error('jTemplates: Template is not defined.');
                else
                    return;
            }
            t.setParam(name, value);
        });
    };

    /**
    * Process template using data 'd' and parameters 'param'. Update HTML code.
    * @param {object} d data 
    * @param {object} [param] parameters
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.processTemplate = function (d, param, callback) {
        return jQuery(this).each(function () {
            var t = jQuery.getTemplate(this);
            if (t === undefined) {
                if (Template.DEBUG_MODE)
                    throw new Error('jTemplates: Template is not defined.');
                else
                    return;
            }
            jQuery.data(this, 'jTemplateSID', jQuery.data(this, 'jTemplateSID') + 1);
            jQuery(this).html(t.get(d, param, this, 0));
            if (callback != undefined && callback != null && typeof callback == "function") {
                callback();
            }


        });
    };

    /**
    * Process template using data from URL 'url_' (only format JSON) and parameters 'param'. Update HTML code.
    * @param {string} url_ URL to data (in JSON)
    * @param {object} [param] parameters
    * @param {object} options options and callbacks
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.processTemplateURL = function (url_, param, options) {
        var that = this;

        options = jQuery.extend({
            type: 'GET',
            async: true,
            cache: false
        }, options);

        jQuery.ajax({
            url: url_,
            type: options.type,
            data: options.data,
            dataFilter: options.dataFilter,
            async: options.async,
            cache: options.cache,
            timeout: options.timeout,
            dataType: 'json',
            success: function (d) {
                var r = jQuery(that).processTemplate(d, param);
                if (options.on_success) {
                    options.on_success(r);
                }
            },
            error: options.on_error,
            complete: options.on_complete
        });
        return this;
    };

    //#####>UPDATER
    /**
    * Create new Updater.
    * @name Updater
    * @class This class is used for 'Live Refresh!'.
    * @param {string} url A destination URL
    * @param {object} param Parameters (for template)
    * @param {number} interval Time refresh interval
    * @param {object} args Additional URL parameters (in URL alter ?) as assoc array.
    * @param {array} objs An array of HTMLElement which will be modified by Updater.
    * @param {object} options options and callbacks
    */
    var Updater = function (url, param, interval, args, objs, options) {
        this._url = url;
        this._param = param;
        this._interval = interval;
        this._args = args;
        this.objs = objs;
        this.timer = null;
        this._options = options || {};

        var that = this;
        jQuery(objs).each(function () {
            jQuery.data(this, 'jTemplateUpdater', that);
        });
        this.run();
    };

    /**
    * Create new HTTP request to server, get data (as JSON) and send it to templates. Also check does HTMLElements still exists in Document.
    */
    Updater.prototype.run = function () {
        this.detectDeletedNodes();
        if (this.objs.length == 0) {
            return;
        }
        var that = this;
        jQuery.getJSON(this._url, this._args, function (d) {
            var r = jQuery(that.objs).processTemplate(d, that._param);
            if (that._options.on_success) {
                that._options.on_success(r);
            }
        });
        this.timer = setTimeout(function () { that.run(); }, this._interval);
    };

    /**
    * Check does HTMLElements still exists in HTML Document.
    * If not exist, delete it from property 'objs'.
    */
    Updater.prototype.detectDeletedNodes = function () {
        this.objs = jQuery.grep(this.objs, function (o) {
            if (jQuery.browser.msie) {
                var n = o.parentNode;
                while (n && n != document) {
                    n = n.parentNode;
                }
                return n != null;
            } else {
                return o.parentNode != null;
            }
        });
    };

    /**
    * Start 'Live Refresh!'.
    * @param {string} url A destination URL
    * @param {object} param Parameters (for template)
    * @param {number} interval Time refresh interval
    * @param {object} args Additional URL parameters (in URL alter ?) as assoc array.
    * @param {object} options options and callbacks
    * @return {Updater} an Updater object
    * @memberOf jQuery.fn
    */
    jQuery.fn.processTemplateStart = function (url, param, interval, args, options) {
        return new Updater(url, param, interval, args, this, options);
    };

    /**
    * Stop 'Live Refresh!'.
    * @return {jQuery} chainable jQuery class
    * @memberOf jQuery.fn
    */
    jQuery.fn.processTemplateStop = function () {
        return jQuery(this).each(function () {
            var updater = jQuery.data(this, 'jTemplateUpdater');
            if (updater == null) {
                return;
            }
            var that = this;
            updater.objs = jQuery.grep(updater.objs, function (o) {
                return o != that;
            });
            jQuery.removeData(this, 'jTemplateUpdater');
        });
    };
    //#####<UPDATER

    jQuery.extend(/** @scope jQuery.prototype */{
    /**
    * Create new Template.
    * @param {string} s A template string (like: "Text: {$T.txt}.").
    * @param {array} includes Array of included templates.
    * @param {object} settings Settings. (see Template)
    * @return {Template}
    */
    createTemplate: function (s, includes, settings) {
        return new Template(s, includes, settings);
    },

    /**
    * Create new Template from URL.
    * @param {string} url_ URL to template
    * @param {array} includes Array of included templates.
    * @param {object} settings Settings. (see Template)
    * @return {Template}
    */
    createTemplateURL: function (url_, includes, settings) {
        var s = jQuery.ajax({
            url: url_,
            async: false
        }).responseText;

        return new Template(s, includes, settings);
    },

    /**
    * Get a Template for HTML node
    * @param {Element} HTML node
    * @return {Template} a Template or "undefined"
    */
    getTemplate: function (element) {
        return jQuery.data(element, 'jTemplate');
    },

    /**
    * Process template and return text content.
    * @param {Template} template A Template
    * @param {object} data data
    * @param {object} param parameters
    * @return {string} Content of template
    */
    processTemplateToText: function (template, data, parameter) {
        return template.get(data, parameter, undefined, 0);
    },

    /**
    * Set Debug Mode
    * @param {Boolean} value
    */
    jTemplatesDebugMode: function (value) {
        Template.DEBUG_MODE = value;
    }
});

})(jQuery);}

