/* 
 * jTemplates 0.6.6 (http://jtemplates.tpython.com)
 * Copyright (c) 2007-2008 Tomasz Gloc (http://www.tpython.com)
 * Please do not remove or modify above line. Thanks.
 * 
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Id: $Id: jquery-jtemplates_uncompressed.js 151 2008-01-06 12:36:19Z tom $
 */
 
 /**
 * @fileOverview Template engine in JavaScript.
 * @name jTemplates
 * @author Tomasz Gloc
 * @date $Date: 2008-01-06 13:36:19 +0100 (N, 06 sty 2008) $
 */


/** @ignore */
function log(arg) {
	if(window.console) {
		console.log.apply(document, arguments);
	}
};


if(window.jQuery && !window.jQuery.createTemplate) {(function() {
	
	/**
	 * [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
	 * @return {String}
	 * @memberOf BaseNode
	 */
	
	/**
	 * [abstract]
	 * @name BaseArray
	 * @inherits 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.
	 * @constructor
	 * @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]. 
	 * @inherits BaseNode
	 * @class A template or multitemplate.
	 */
	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
		}, settings);
		
		this.splitTemplates(s, includes);
		
		if(s) {
			this.setTemplate(this._templates_code['MAIN'], includes);
		}
		
		this._templates_code = null;
	};
	
	/**
	 * jTemplates version
	 * @type string
	 */
	Template.prototype.version = '0.6.6';
	
	/**
	 * 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;
		
		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);
		}
		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 || {}));
				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) {
		if(s == undefined) {
			this._tree.push(new TextNode('', 1));
			return;
		}
		s = s.replace(/[\n\r]/g, '');
		s = s.replace(/\{\*.*?\*\}/g, '');
		this._includes = jQuery.extend({}, this._templates || {}, includes || {});
		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) {
			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));
				}
				ss = se + 11;
				literalMode = 0;
				i = jQuery.inArray('{#/literal}', op);
				continue;
			}
			se = s.indexOf(op[i], ss);
			if(se > ss) {
				node.push(new TextNode(s.substring(ss, se), literalMode));
			}
			var ppp = op[i].match(/\{#([\w\/]+).*?\}/);
			var op_ = RegExp.$1;
			switch(op_) {
				case 'elseif':
					++elseif_level;
					node.switchToElse();
					//no break
				case 'if':
					e = new opIF(op[i], 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(op[i], node);
					node.push(e);
					node = e;
					break;
				case 'include':
					node.push(new Include(op[i], this._includes));
					break;
				case 'param':
					node.push(new UserParam(op[i]));
					break;
				case 'cycle':
					node.push(new Cycle(op[i]));
					break;
				case 'ldelim':
					node.push(new TextNode('{'));
					break;
				case 'rdelim':
					node.push(new TextNode('}'));
					break;
				case 'literal':
					literalMode = 1;
					break;
				case '/literal':
					throw new Error("jTemplates: No begin of literal.");
				default:
					throw new Error('jTemplates: unknown tag ' + op_ + '.');
			}
	
			ss = se + op[i].length;
		}
	
		if(s.length > ss) {
			node.push(new TextNode(s.substr(ss), literalMode));
		}
	};
	
	/**
	 * Process template and get the html string.
	 * @param {object} d data
	 * @param {object} param parameters
	 * @param {Element} element a HTML element
	 * @return {String}
	 */
	Template.prototype.get = function(d, param, element) {
		var $T = this.cloneData(d, {escapeData: this.settings.filter_data, noFunc: this.settings.disallow_functions});
	
		var $P = jQuery.extend(this._param, param);
		if(this.settings.filter_params) {
			$P = this.cloneData($P, {escapeData: this.settings.filter_params, noFunc: false});
		}
		
		var $Q = 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);
		}
		return ret;
	};
	
	/**
	 * Set to parameter 'name' value 'value'.
	 * @param {string} name
	 * @param {object} value
	 */
	Template.prototype.setParam = function(name, value) {
		this._param[name] = value;
	};
	
	/**
	 * Replace chars &, >, <, ", ' with html entities.
	 * To disable function set settings: filter_data=false, filter_params=false
	 * @param {string} string
	 * @return {string}
	 */
	Template.prototype.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).
	 * @return {object} output data (filtered)
	 */
	Template.prototype.cloneData = function(d, filter) {
		if(d == null) {
			return d;
		}
		switch(d.constructor) {
			case Object:
				var o = {};
				for(var i in d) {
					o[i] = this.cloneData(d[i], filter);
				}
				return o;
			case Array:
				var o = [];
				for(var i=0,l=d.length; i<l; ++i) {
					o[i] = this.cloneData(d[i], filter);
				}
				return o;
			case String:
				return (filter.escapeData) ? (this.escapeHTML(d)) : (d);
			case Function:
				if(filter.noFunc) {
					throw new Error("jTemplates: Functions are not allowed.");
				}
				//no break
			default:
				return d;
		}
	};
	
	/**
	 * Create a new text node.
	 * @constructor
	 * @param {string} val text string
	 * @param {boolean} literalMode When enable (true) template does not interpret blocks {..}.
	 * @inherits BaseNode
	 * @class All text (block {..}) between controls block {#..}.
	 */
	var TextNode = function(val, literalMode) {
		this._value = val;
		this._literalMode = literalMode;
	};
	
	/**
	 * Get the html string for a text node.
	 * @param {object} d data
	 * @param {object} param parameters
	 * @param {Element} element a HTML element
	 * @return {String}
	 */
	TextNode.prototype.get = function(d, param, element) {
		var t = this._value;
		if(!this._literalMode) {
			var $T = d;
			var $P = param;
			var $Q = element;
			t = t.replace(/\{(.*?)\}/g, function(__a0, __a1) {
				var tmp = eval(__a1);
				if(typeof tmp == 'function') {
					var settings = jQuery.data(element, 'jTemplate').settings;
					if(settings.disallow_functions || !settings.runnable_functions) {
						return '';
					} else {
						tmp = tmp($T, $P, $Q);
					}
				}
				return (tmp === undefined) ? ("") : (String(tmp));
			});
		}
		return t;
	};
	
	/**
	 * Create a new conditional node.
	 * @constructor
	 * @param {string} oper content of operator {#..}
	 * @param {object} par parent node
	 * @inherits BaseArray
	 * @class A class represent: {#if}.
	 */
	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
	 * @return {String}
	 */
	opIF.prototype.get = function(d, param, element) {
		var $T = d;
		var $P = param;
		var $Q = element;
		var tab = (eval(this._cond)) ? (this._onTrue) : (this._onFalse);
		var ret = '';
		for(var i=0, l=tab.length; i<l; ++i) {
			ret += tab[i].get(d, param, element);
		}
		return ret;
	};
	
	/**
	 * Create a new loop node.
	 * @constructor
	 * @param {string} oper content of operator {#..}
	 * @param {object} par parent node
	 * @inherits BaseArray
	 * @class A class represent: {#foreach}.
	 */
	var opFOREACH = function(oper, par) {
		this._parent = par;
		oper.match(/\{#foreach (.+?) as (\w+?)( .+)*\}/);
		this._arg = RegExp.$1;
		this._name = RegExp.$2;
		this._option = RegExp.$3 || null;
		if(this._option !== null) {
			var o = this._option.split(/[= ]/);
			if(o[0] === '') {
				o.shift();
			}
			this._option = {};
			for(var i=0, l=o.length; i<l; i+=2) {
				this._option[o[i]] = o[i+1];
			}
		} else {
			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
	 * @return {String}
	 */
	opFOREACH.prototype.get = function(d, param, element) {
		var $T = d;
		var $P = param;
		var $Q = element;
		var count = eval(this._arg);	//array of elements in foreach
		var key = [];	//only for objects
		var mode = typeof count;
		if(mode == 'object') {
			var arr = [];
			jQuery.each(count, function(k, v) {
				key.push(k);
				arr.push(v);
			});
			count = arr;
		}
		var s = Number(eval(this._option.begin) || 0);	//start
		var step = Number(eval(this._option.step) || 1);
		var e = count.length;	//end
		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) {
			var iteration = 0;
			var _total = Math.ceil((e-s)/step);
			var ckey, cval;	//current key, current value
			for(; s<e; s+=step, ++iteration) {
				ckey = key[s];
				cval = count[s];
				if((mode == 'object') && (ckey in Object) && (Object[ckey] === $T[ckey])) {
					continue;
				}
				var p = $T[this._name] = cval;
				//backwards compatibility
				p.$index = s;
				p.$iteration = iteration;
				p.$first = (iteration==0);
				p.$last = (s+step>=e);
				p.$total = _total;
				//end backwards compatibility
				$T[this._name + '$index'] = s;
				$T[this._name + '$iteration'] = iteration;
				$T[this._name + '$first'] = (iteration==0);
				$T[this._name + '$last'] = (s+step>=e);
				$T[this._name + '$total'] = _total;
				$T[this._name + '$key'] = ckey;
				$T[this._name + '$typeof'] = typeof cval;
				for(i=0, l=this._onTrue.length; i<l; ++i) {
					ret += this._onTrue[i].get($T, param, element);
				}
				//backwards compatibility
				delete p.$index;
				delete p.$iteration;
				delete p.$first;
				delete p.$last;
				delete p.$total;
				//end backwards compatibility
				delete $T[this._name + '$index'];
				delete $T[this._name + '$iteration'];
				delete $T[this._name + '$first'];
				delete $T[this._name + '$last'];
				delete $T[this._name + '$total'];
				delete $T[this._name + '$key'];
				delete $T[this._name + '$typeof'];
				delete $T[this._name];
			}
		} else {
			for(i=0, l=this._onFalse.length; i<l; ++i) {
				ret += this._onFalse[i].get($T, param, element);
			}
		}
		return ret;
	};
	
	/**
	 * Create a new entry for included template.
	 * @constructor
	 * @param {string} oper content of operator {#..}
	 * @param {array} includes
	 * @inherits BaseNode
	 * @class A class represent: {#include}.
	 */
	var Include = function(oper, includes) {
		oper.match(/\{#include (.*?)(?: root=(.*?))?\}/);
		this._template = includes[RegExp.$1];
		if(this._template == undefined) {
			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
	 * @return {String}
	 */
	Include.prototype.get = function(d, param, element) {
		var $T = d;
		return this._template.get(eval(this._root), param, element);
	};
	
	/**
	 * Create new node for {#param}.
	 * @constructor
	 * @param {string} oper content of operator {#..}
	 * @inherits BaseNode
	 * @class A class represent: {#param}.
	 */
	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
	 * @return {String}
	 */
	UserParam.prototype.get = function(d, param, element) {
		var $T = d;
		var $P = param;
		var $Q = element;
		
		param[this._name] = eval(this._value);
		return '';
	};
	
	/**
	 * Create a new cycle node.
	 * @constructor
	 * @param {string} oper content of operator {#..}
	 * @inherits BaseNode
	 * @class A class represent: {#cycle}.
	 */
	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
	 * @return {String}
	 */
	Cycle.prototype.get = function(d, param, element) {
		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)
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.setTemplate = function(s, includes, settings) {
		if(s.constructor === Template) {
			jQuery(this).each(function() {
				jQuery.data(this, 'jTemplate', s);
				jQuery.data(this, 'jTemplateSID', 0);
			});
		} else {
			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)
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.setTemplateURL = function(url_, includes, settings) {
		var s = jQuery.ajax({
			url: url_,
			async: false
		}).responseText;
		
		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)
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.setTemplateElement = function(elementName, includes, settings) {
		var s = $('#' + elementName).val();
		if(s == null) {
			s = $('#' + elementName).html();
			s = s.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
		}
		
		s = jQuery.trim(s);
		s = s.replace(/^<\!\[CDATA\[([\s\S]*)\]\]>$/im, '$1');
		
		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.data(this, 'jTemplate')) {
				++count;
			}
		});
		return count;
	};
	
	/**
	 * Remote Template from HTML Element(s)
	 */
	jQuery.fn.removeTemplate = function() {
		jQuery(this).processTemplateStop();
		jQuery(this).each(function() {
			jQuery.removeData(this, 'jTemplate');
		});
	};
	
	/**
	 * Set to parameter 'name' value 'value'.
	 * @param {string} name
	 * @param {object} value
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.setParam = function(name, value) {
		jQuery(this).each(function() {
			var t = jQuery.data(this, 'jTemplate');
			if(t === undefined) {
				throw new Error('jTemplates: Template is not defined.');
			}
			t.setParam(name, value); 
		});
	};
	
	/**
	 * Process template using data 'd' and parameters 'param'. Update HTML code.
	 * @param {object} d data 
	 * @param {object} [param] parameters
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.processTemplate = function(d, param) {
		jQuery(this).each(function() {
			var t = jQuery.data(this, 'jTemplate');
			if(t === undefined) {
				throw new Error('jTemplates: Template is not defined.');
			}
			jQuery.data(this, 'jTemplateSID', jQuery.data(this, 'jTemplateSID') + 1);
			jQuery(this).html(t.get(d, param, this));
		});
	};
	
	/**
	 * 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
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.processTemplateURL = function(url_, param) {
		var that = this;
		var s = jQuery.ajax({
			url: url_,
			async: false,
			cache: false,
			dataType: 'json',
			success: function(d) {
				jQuery(that).processTemplate(d, param);
			}
		});
	};

//#####>UPDATER
	/**
	 * Create new Updater.
	 * @constructor
	 * @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.
	 * @class This class is used for 'Live Refresh!'.
	 */
	var Updater = function(url, param, interval, args, objs) {
		this._url = url;
		this._param = param;
		this._interval = interval;
		this._args = args;
		this.objs = objs;
		this.timer = null;
		
		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) {
		  jQuery(that.objs).processTemplate(d, that._param);
		});
		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.
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.processTemplateStart = function(url, param, interval, args) {
		var u = new Updater(url, param, interval, args, this);
		return u.timer;
	};
	
	/**
	 * Stop 'Live Refresh!'.
	 * @memberOf jQuery.fn
	 */
	jQuery.fn.processTemplateStop = function() {
		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}
		 * @obsolete
		 */
		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}
		 * @obsolete
		 */
		createTemplateURL: function(url_, includes, settings) {
			var s = jQuery.ajax({
				url: url_,
				async: false
			}).responseText;
			
			return new Template(s, includes, settings);
		}
	});
	
})(jQuery);}

