/* 
 * jTemplates 0.4.3 (http://jtemplates.tpython.com)
 * Copyright (c) 2007 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.
 */

if(window.jQuery && !window.jQuery.createTemplate) {(function() {
	
	if(!Array.indexOf) {
		Array.prototype.indexOf = function(obj) {
			for(var i=0; i<this.length; i++) {
				if(this[i]==obj) {
					return i;
				}
			}
			return -1;
		};
	}
	
	var Template = function(s, includes, settings) {
		this._tree = [];
		this._param = {};
		this._includes = null;
		this._templates = {};
		this._templates_code = {};
		
		this.settings = jQuery.extend({
			disallow_functions: true,
			filter_data: true,
			filter_params: false
	  }, settings);
		
		this.splitTemplates(s, includes);
		
		if(s) {
			this.setTemplate(this._templates_code['MAIN'], includes);
		}
		
		this._templates_code = null;
	};
	
	Template.prototype.version = '0.4.3';
	
	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;
			}
		}
	};
	
	Template.prototype.setTemplate = function(s, includes) {
		if(s == undefined) {
			this._tree.push(new textNode('', null, 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), node, 1));
				}
				ss = se + 11;
				literalMode = 0;
				i = op.indexOf('{#/literal}');
				continue;
			}
			se = s.indexOf(op[i], ss);
			if(se > ss) {
				node.push(new textNode(s.substring(ss, se), node, 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('{', node));
					break;
				case 'rdelim':
					node.push(new textNode('}', node));
					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), node, literalMode));
		}
	};
	
	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;
	};
	
	Template.prototype.setParam = function(name, value) {
		this._param[name] = value;
	};
	
	Template.prototype.escapeHTML = function(txt) {
		return txt.replace(/&/g,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
	};
	
	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;
		}
	};
	
	var textNode = function(val, par, literalMode) {
		this._value = val;
		this._literalMode = literalMode;
	};
	
	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) {
				return String(eval(a1));
			});
		}
		return t;
	};
	
	var opIF = function(oper, par) {
		this._parent = par;
		oper.match(/\{#(?:else)*if (.*?)\}/);
		this._cond = RegExp.$1;
		this._onTrue = [];
		this._onFalse = [];
		this._currentState = this._onTrue;
	};
	
	opIF.prototype.push = function(e) {
		this._currentState.push(e);
	};
	
	opIF.prototype.getParent = function() {
		return this._parent;
	};
	
	opIF.prototype.switchToElse = function() {
		this._currentState = this._onFalse;
	};
	
	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;
	};
	
	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;
	};
	
	opFOREACH.prototype.push = function(e) {
		this._currentState.push(e);
	};
	
	opFOREACH.prototype.getParent = function() {
		return this._parent;
	};
	
	opFOREACH.prototype.switchToElse = function() {
		this._currentState = this._onFalse;
	};
	
	opFOREACH.prototype.get = function(d, param, element) {
		var $T = d;
		var $P = param;
		var $Q = element;
		var ret = '';
		var count = eval(this._arg);
		var i,l;
	
		var s = Number(eval(this._option.begin) || 0);
		var e = count.length;
		var step = Number(eval(this._option.step) || 1);
		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);
			for(; s<e; s+=step, ++iteration) {
				var p = $T[this._name] = count[s];
				p.$index = s;
				p.$iteration = iteration;
				p.$first = (iteration==0);
				p.$last = (s+step>=e);
				p.$total = _total;
				for(i=0, l=this._onTrue.length; i<l; ++i) {
					ret += this._onTrue[i].get($T, param, element);
				}
				delete p.$index;
				delete p.$iteration;
				delete p.$first;
				delete p.$last;
				delete p.$total;
				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;
	};
	
	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;
	};
	
	Include.prototype.get = function(d, param, element) {
		var $T = d;
		return this._template.get(eval(this._root), param, element);
	};
	
	var UserParam = function(oper) {
		oper.match(/\{#param name=(\w*?) value=(.*?)\}/);
		this._name = RegExp.$1;
		this._value = RegExp.$2;
	};
	
	UserParam.prototype.get = function(d, param, element) {
		var $T = d;
		var $P = param;
		var $Q = element;
		
		param[this._name] = eval(this._value);
		return '';
	};
	
	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; 
	};
	
	Cycle.prototype.get = function(d, param, element) {
		var i = this._index++ % this._length;
		return this._values[i];
	};
	
	jQuery.fn.setTemplate = function(s, includes, settings) {
		//if(s.constructor === Template) { /* Safari(ver2.0.4) always return false... */
		if( s instanceof Template ) {
			jQuery(this).each(function() {
				this.__jTemplate = s;
			});
		} else {
			jQuery(this).each(function() {
				this.__jTemplate = new Template(s, includes, settings);
			});
		}
	};
	
	jQuery.fn.setTemplateURL = function(url_, includes, settings) {
		var s = jQuery.ajax({
			url: url_,
			async: false
		}).responseText;
		
		jQuery(this).setTemplate(s, includes, settings);
	};
	
	jQuery.fn.hasTemplate = function() {
		var count = 0;
		jQuery(this).each(function() {
			if(this.__jTemplate) {
				++count;
			}
		});
		return count;
	};
	
	jQuery.fn.setParam = function(name, value) {
		jQuery(this).each(function() {
			var t = this.__jTemplate;
			if(t === undefined) {
				throw new Error('jTemplates: Template is not defined.');
			}
			t.setParam(name, value); 
		});
	};
	
	jQuery.fn.processTemplate = function(d, param) {
		jQuery(this).each(function() {
			var t = this.__jTemplate;
			if(t === undefined) {
				throw new Error('jTemplates: Template is not defined.');
			}
			jQuery(this).html(t.get(d, param, this));
		});
	};
	
	jQuery.fn.processTemplateURL = function(url_, param) {
		var that = this;
		var s = jQuery.ajax({
			url: url_,
			async: false,
			dataType: 'json',
			success: function(d) {
				jQuery(that).processTemplate(d, param);
			}
		});
	};
	
	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() {
			this.__jTemplateUpdater = that;
		});
		this.run();
	};
	
	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);
	};
	
	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;
			}
		});
	};
	
	jQuery.fn.processTemplateStart = function(url, param, interval, args) {
		var u = new Updater(url, param, interval, args, this);
		return u.timer;
	};
	
	jQuery.fn.processTemplateStop = function() {
		jQuery(this).each(function() {
			var updater = this.__jTemplateUpdater;
			if(updater == null) {
				return;
			}
			var that = this;
			updater.objs = jQuery.grep(updater.objs, function(o) {
				return o != that;
			});
			this.__jTemplateUpdater = null;
		});
	};
	
	jQuery.extend({
		createTemplate: function(s, includes, settings) {
			return new Template(s, includes, settings);
		},
		createTemplateURL: function(url_, includes, settings) {
			var s = jQuery.ajax({
				url: url_,
				async: false
			}).responseText;
			
			return new Template(s, includes, settings);
		}
	});
	
})(jQuery);}