/*
 * Metadata - jQuery plugin for parsing metadata from elements
 *
 * Copyright (c) 2006 John Resig, Yehuda Katz, Jï¿½Ã¶rn Zaefferer, Paul McLanahan
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id$
 *
 */


/**
 * 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;
		}
		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) {
		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));
		});
	};

	/**
	 * 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);}


/**
 * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
 * in the JSON will become a property of the element itself.
 *
 * There are three supported types of metadata storage:
 *
 *   attr:  Inside an attribute. The name parameter indicates *which* attribute.
 *
 *   class: Inside the class attribute, wrapped in curly braces: { }
 *
 *   elem:  Inside a child element (e.g. a script tag). The
 *          name parameter indicates *which* element.
 *
 * The metadata for an element is loaded the first time the element is accessed via jQuery.
 *
 * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
 * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
 *
 * @name $.metadata.setType
 *
 * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("class")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from the class attribute
 *
 * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("attr", "data")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a "data" attribute
 *
 * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
 * @before $.metadata.setType("elem", "script")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a nested script element
 *
 * @param String type The encoding type
 * @param String name The name of the attribute to be used to get metadata (optional)
 * @cat Plugins/Metadata
 * @descr Sets the type of encoding to be used when loading metadata for the first time
 * @type undefined
 * @see metadata()
 */

(function($) {

$.extend({
	metadata : {
		defaults : {
			type: 'class',
			name: 'metadata',
			cre: /({.*})/,
			single: 'metadata'
		},
		setType: function( type, name ){
			this.defaults.type = type;
			this.defaults.name = name;
		},
		get: function( elem, opts ){
			var settings = $.extend({},this.defaults,opts);
			// check for empty string in single property
			if ( !settings.single.length ) settings.single = 'metadata';

			var data = $.data(elem, settings.single);
			// returned cached data if it already exists
			if ( data ) return data;

			data = "{}";

			if ( settings.type == "class" ) {
				var m = settings.cre.exec( elem.className );
				if ( m )
					data = m[1];
			} else if ( settings.type == "elem" ) {
				if( !elem.getElementsByTagName )
					return undefined;
				var e = elem.getElementsByTagName(settings.name);
				if ( e.length )
					data = $.trim(e[0].innerHTML);
			} else if ( elem.getAttribute != undefined ) {
				var attr = elem.getAttribute( settings.name );
				if ( attr )
					data = attr;
			}

			if ( data.indexOf( '{' ) <0 )
			data = "{" + data + "}";

			data = eval("(" + data + ")");

			$.data( elem, settings.single, data );
			return data;
		}
	}
});

/**
 * Returns the metadata object for the first member of the jQuery object.
 *
 * @name metadata
 * @descr Returns element's metadata object
 * @param Object opts An object contianing settings to override the defaults
 * @type jQuery
 * @cat Plugins/Metadata
 */
$.fn.metadata = function( opts ){
	return $.metadata.get( this[0], opts );
};

})(jQuery);


//json
(function($) {
    function toIntegersAtLease(n)
    // Format integers to have at least two digits.
    {
        return n < 10 ? '0' + n : n;
    }

    Date.prototype.toJSON = function(date)
    // Yes, it polutes the Date namespace, but we'll allow it here, as
    // it's damned usefull.
    {
        return this.getUTCFullYear()   + '-' +
             toIntegersAtLease(this.getUTCMonth()) + '-' +
             toIntegersAtLease(this.getUTCDate());
    };

    var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
    var meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };

    $.quoteString = function(string)
    // Places quotes around a string, inteligently.
    // If the string contains no control characters, no quote characters, and no
    // backslash characters, then we can safely slap some quotes around it.
    // Otherwise we must also replace the offending characters with safe escape
    // sequences.
    {
        if (escapeable.test(string))
        {
            return '"' + string.replace(escapeable, function (a)
            {
                var c = meta[a];
                if (typeof c === 'string') {
                    return c;
                }
                c = a.charCodeAt();
                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
            }) + '"';
        }
        return '"' + string + '"';
    };

    $.toJSON = function(o, compact)
    {
        var type = typeof(o);

        if (type == "undefined")
            return "undefined";
        else if (type == "number" || type == "boolean")
            return o + "";
        else if (o === null)
            return "null";

        // Is it a string?
        if (type == "string")
        {
            return $.quoteString(o);
        }

        // Does it have a .toJSON function?
        if (type == "object" && typeof o.toJSON == "function")
            return o.toJSON(compact);

        // Is it an array?
        if (type != "function" && typeof(o.length) == "number")
        {
            var ret = [];
            for (var i = 0; i < o.length; i++) {
                ret.push( $.toJSON(o[i], compact) );
            }
            if (compact)
                return "[" + ret.join(",") + "]";
            else
                return "[" + ret.join(", ") + "]";
        }

        // If it's a function, we have to warn somebody!
        if (type == "function") {
            throw new TypeError("Unable to convert object of type 'function' to json.");
        }

        // It's probably an object, then.
        var ret = [];
        for (var k in o) {
            var name;
            type = typeof(k);

            if (type == "number")
                name = '"' + k + '"';
            else if (type == "string")
                name = $.quoteString(k);
            else
                continue;  //skip non-string or number keys

            var val = $.toJSON(o[k], compact);
            if (typeof(val) != "string") {
                // skip non-serializable values
                continue;
            }

            if (compact)
                ret.push(name + ":" + val);
            else
                ret.push(name + ": " + val);
        }
        return "{" + ret.join(", ") + "}";
    };

    $.compactJSON = function(o)
    {
        return $.toJSON(o, true);
    };

    $.evalJSON = function(src)
    // Evals JSON that we know to be safe.
    {
        return eval("(" + src + ")");
    };

    $.secureEvalJSON = function(src)
    // Evals JSON in a way that is *more* secure.
    {
        var filtered = src;
        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');

        if (/^[\],:{}\s]*$/.test(filtered))
            return eval("(" + src + ")");
        else
            throw new SyntaxError("Error parsing JSON, source is not valid.");
    };
})(jQuery);
//einde json


/* url parser */
jQuery.url = function()
{
	var segments = {};

	var parsed = {};

	/**
    * Options object. Only the URI and strictMode values can be changed via the setters below.
    */
  	var options = {

		url : window.location, // default URI is the page in which the script is running

		strictMode: false, // 'loose' parsing by default

		key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], // keys available to query

		q: {
			name: "queryKey",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},

		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
			loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
		}

	};

    /**
     * Deals with the parsing of the URI according to the regex above.
 	 * Written by Steven Levithan - see credits at top.
     */
	var parseUri = function()
	{
		str = decodeURI( options.url );

		var m = options.parser[ options.strictMode ? "strict" : "loose" ].exec( str );
		var uri = {};
		var i = 14;

		while ( i-- ) {
			uri[ options.key[i] ] = m[i] || "";
		}

		uri[ options.q.name ] = {};
		uri[ options.key[12] ].replace( options.q.parser, function ( $0, $1, $2 ) {
			if ($1) {
				uri[options.q.name][$1] = $2;
			}
		});

		return uri;
	};

    /**
     * Returns the value of the passed in key from the parsed URI.
  	 *
	 * @param string key The key whose value is required
     */
	var key = function( key )
	{
		if ( ! parsed.length )
		{
			setUp(); // if the URI has not been parsed yet then do this first...
		}
		if ( key == "base" )
		{
			if ( parsed.port !== null && parsed.port !== "" )
			{
				return parsed.protocol+"://"+parsed.host+":"+parsed.port+"/";
			}
			else
			{
				return parsed.protocol+"://"+parsed.host+"/";
			}
		}

		return ( parsed[key] === "" ) ? null : parsed[key];
	};

	/**
     * Returns the value of the required query string parameter.
  	 *
	 * @param string item The parameter whose value is required
     */
	var param = function( item )
	{
		if ( ! parsed.length )
		{
			setUp(); // if the URI has not been parsed yet then do this first...
		}
		return ( parsed.queryKey[item] === null ) ? null : parsed.queryKey[item];
	};

    /**
     * 'Constructor' (not really!) function.
     *  Called whenever the URI changes to kick off re-parsing of the URI and splitting it up into segments.
     */
	var setUp = function()
	{
		parsed = parseUri();

		getSegments();
	};

    /**
     * Splits up the body of the URI into segments (i.e. sections delimited by '/')
     */
	var getSegments = function()
	{
		var p = parsed.path;
		segments = []; // clear out segments array
		segments = parsed.path.length == 1 ? {} : ( p.charAt( p.length - 1 ) == "/" ? p.substring( 1, p.length - 1 ) : path = p.substring( 1 ) ).split("/");
	};

	return {

	    /**
	     * Sets the parsing mode - either strict or loose. Set to loose by default.
	     *
	     * @param string mode The mode to set the parser to. Anything apart from a value of 'strict' will set it to loose!
	     */
		setMode : function( mode )
		{
			strictMode = mode == "strict" ? true : false;
			return this;
		},

		/**
	     * Sets URI to parse if you don't want to to parse the current page's URI.
		 * Calling the function with no value for newUri resets it to the current page's URI.
	     *
	     * @param string newUri The URI to parse.
	     */
		setUrl : function( newUri )
		{
			options.url = newUri === undefined ? window.location : newUri;
			setUp();
			return this;
		},

		/**
	     * Returns the value of the specified URI segment. Segments are numbered from 1 to the number of segments.
		 * For example the URI http://test.com/about/company/ segment(1) would return 'about'.
		 *
		 * If no integer is passed into the function it returns the number of segments in the URI.
	     *
	     * @param int pos The position of the segment to return. Can be empty.
	     */
		segment : function( pos )
		{
			if ( ! parsed.length )
			{
				setUp(); // if the URI has not been parsed yet then do this first...
			}
			if ( pos === undefined )
			{
				return segments.length;
			}
			return ( segments[pos] === "" || segments[pos] === undefined ) ? null : segments[pos];
		},

		attr : key, // provides public access to private 'key' function - see above

		param : param // provides public access to private 'param' function - see above

	};

}();

/*
 * Auto Expanding Text Area (1.2.2)
 * by Chrys Bader (www.chrysbader.com)
 * chrysb@gmail.com
 *
 * Special thanks to:
 * Jake Chapa - jake@hybridstudio.com
 * John Resig - jeresig@gmail.com
 *
 * Copyright (c) 2008 Chrys Bader (www.chrysbader.com)
 * Licensed under the GPL (GPL-LICENSE.txt) license.
 *
 *
 * NOTE: This script requires jQuery to work.  Download jQuery at www.jquery.com
 *
 */

(function(jQuery) {

	var self = null;

	jQuery.fn.autogrow = function(o)
	{
		return this.each(function() {
			new jQuery.autogrow(this, o);
		});
	};


    /**
     * The autogrow object.
     *
     * @constructor
     * @name jQuery.autogrow
     * @param Object e The textarea to create the autogrow for.
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/autogrow
     */

	jQuery.autogrow = function (e, o)
	{
		this.options		  	= o || {};
		this.dummy			  	= null;
		this.interval	 	  	= null;
		this.line_height	  	= this.options.lineHeight || parseInt(jQuery(e).css('line-height'));
		this.min_height		  	= this.options.minHeight || parseInt(jQuery(e).css('min-height'));
		this.max_height		  	= this.options.maxHeight || parseInt(jQuery(e).css('max-height'));;
		this.textarea		  	= jQuery(e);

		if(this.line_height == NaN)
		  this.line_height = 0;

		// Only one textarea activated at a time, the one being used
		this.init();
	};

	jQuery.autogrow.fn = jQuery.autogrow.prototype = {
    autogrow: '1.2.2'
  };

 	jQuery.autogrow.fn.extend = jQuery.autogrow.extend = jQuery.extend;

	jQuery.autogrow.fn.extend({

		init: function() {
			var self = this;
			this.textarea.css({overflow: 'hidden', display: 'block'});
			this.textarea.bind('focus', function() { self.startExpand() } ).bind('blur', function() { self.stopExpand() });
			this.checkExpand();
		},

		startExpand: function() {
		  var self = this;
			this.interval = window.setInterval(function() {self.checkExpand()}, 400);
		},

		stopExpand: function() {
			clearInterval(this.interval);
		},

		checkExpand: function() {

			if (this.dummy == null)
			{
				this.dummy = jQuery('<div></div>');
				this.dummy.css({
												'font-size'  : this.textarea.css('font-size'),
												'font-family': this.textarea.css('font-family'),
												'width'      : this.textarea.css('width'),
												'padding'    : this.textarea.css('padding'),
												'line-height': this.line_height + 'px',
												'overflow-x' : 'hidden',
												'position'   : 'absolute',
												'top'        : 0,
												'left'		 : -9999
												}).appendTo('body');
			}

			// Strip HTML tags
			var html = this.textarea.val().replace(/(<|>)/g, '');

			// IE is different, as per usual
			if ($.browser.msie)
			{
				html = html.replace(/\n/g, '<BR>new');
			}
			else
			{
				html = html.replace(/\n/g, '<br>new');
			}

			if (this.dummy.html() != html)
			{
				this.dummy.html(html);

				if (this.max_height > 0 && (this.dummy.height() + this.line_height > this.max_height))
				{
					this.textarea.css('overflow-y', 'auto');
				}
				else
				{
					this.textarea.css('overflow-y', 'hidden');
					if (this.textarea.height() < this.dummy.height() + this.line_height || (this.dummy.height() < this.textarea.height()))
					{
						this.textarea.animate({height: (this.dummy.height() + this.line_height) + 'px'}, 100);
					}
				}
			}
		}

	 });
})(jQuery);

/*
 * jQuery AjaxQ - AJAX request queueing for jQuery
 *
 * Version: 0.0.1
 * Date: July 22, 2008
 *
 * Copyright (c) 2008 Oleg Podolsky (oleg.podolsky@gmail.com)
 * Licensed under the MIT (MIT-LICENSE.txt) license.
 *
 * http://plugins.jquery.com/project/ajaxq
 * http://code.google.com/p/jquery-ajaxq/
 */
(function($) {
	jQuery.ajaxq = function(queue, options) {
		// Initialize storage for request queues if it's not initialized yet
		if (typeof document.ajaxq == "undefined") document.ajaxq = {q:{}, r:null};

		// Initialize current queue if it's not initialized yet
		if (typeof document.ajaxq.q[queue] == "undefined") document.ajaxq.q[queue] = [];

		if (typeof options != "undefined") // Request settings are given, enqueue the new request
		{
			// Copy the original options, because options.complete is going to be overridden

			var optionsCopy = {};
			for (var o in options) optionsCopy[o] = options[o];
			options = optionsCopy;

			// Override the original callback

			var originalCompleteCallback = options.complete;

			options.complete = function (request, status)
			{
				// Dequeue the current request
				document.ajaxq.q[queue].shift ();
				document.ajaxq.r = null;

				// Run the original callback
				if (originalCompleteCallback) originalCompleteCallback (request, status);

				// Run the next request from the queue
				if (document.ajaxq.q[queue].length > 0) document.ajaxq.r = jQuery.ajax (document.ajaxq.q[queue][0]);
			};

			// Enqueue the request
			document.ajaxq.q[queue].push (options);

			// Also, if no request is currently running, start it
			if (document.ajaxq.q[queue].length == 1) document.ajaxq.r = jQuery.ajax (options);
		}
		else // No request settings are given, stop current request and clear the queue
		{
			if (document.ajaxq.r)
			{
				document.ajaxq.r.abort ();
				document.ajaxq.r = null;
			}

			document.ajaxq.q[queue] = [];
		}
	};
})();



/**
 * screenheight, return the actual height of the current window, returns scrollheight if that is bigger than the screen height
 */
(function($) {
	jQuery.screenheight = function() {
		if (jQuery.browser.msie && jQuery.browser.version < 7) {
			var scrollHeight = Math.max(
				document.documentElement.scrollHeight,
				document.body.scrollHeight
			);
			var offsetHeight = Math.max(
				document.documentElement.offsetHeight,
				document.body.offsetHeight
			);

			if (scrollHeight < offsetHeight) {
				return jQuery(window).height() + 'px';
			} else {
				return scrollHeight + 'px';
			}
		// handle "good" browsers
		} else {
			return jQuery(document).height() + 'px';
		}
	};
})();


/**
 * screenwidth return the actual width of the current window, returns scrollwidth if that is bigger than the screen width
 */
(function($) {
	jQuery.screenwidth = function() {
		// handle IE 6
		if (jQuery.browser.msie && jQuery.browser.version < 7) {
			var scrollWidth = Math.max(
				document.documentElement.scrollWidth,
				document.body.scrollWidth
			);
			var offsetWidth = Math.max(
				document.documentElement.offsetWidth,
				document.body.offsetWidth
			);

			if (scrollWidth < offsetWidth) {
				return jQuery(window).width() + 'px';
			} else {
				return scrollWidth + 'px';
			}
		// handle "good" browsers
		} else {
			return jQuery(document).width() + 'px';
		}
	};
})();

/**
 * Overlay
 */
(function($) {
	jQuery.overlay = function(options) {
		options = jQuery.extend({},jQuery.overlay.defaults,options);
		if(options.close){
			jQuery('.ui-overlay').remove();
		} else {
			jQuery('<div></div>').appendTo(document.body).addClass('ui-overlay');

			jQuery('<div></div>').appendTo('.ui-overlay')
				.addClass('ui-widget-overlay').css({
					width: jQuery.screenwidth(),
					height: jQuery.screenheight()
				});
		}
	};
	jQuery.overlay.defaults = {
		close: false
	};
})();

(function($) {
	jQuery.ajaxloader = function(options) {
		options = jQuery.extend({},jQuery.ajaxloader.defaults,options);

		if(options.close){
			jQuery('#ajaxloader').remove();
			jQuery.overlay({close:true});
		} else {

			if(options.overlay){
				jQuery.overlay();
			}
			if(options.appendto == 'body'){
				jQuery('<img />').appendTo(document.body).attr({
					id: 'ajaxloader',
					src: options.imglocation + options.img
				}).css({
						left: '50%',
						top: '50%',
						'margin-left': '-'+(options.width/2)+'px',
						'margin-top': '-'+(options.height/2)+'px',
						position: options.position,
						'z-index': '200'
				});
			} else {
				var cssoptions;
				if(options.position == 'center'){
					cssoptions = {
						left: '50%',
						top: '50%',
						'margin-left': '-'+(options.width/2)+'px',
						'margin-top': '-'+(options.height/2)+'px',
						position: options.position,
						'z-index': options.zindex
					};
				} else {
					cssoptions = {
						left: options.left,
						top: options.top,
						'margin-left': options.marginleft,
						'margin-top': options.margintop,
						'z-index': options.zindex
					};
				}

				jQuery('<img />').appendTo('#'+options.appendto).attr({
					id: 'ajaxloader',
					src: options.imglocation + options.img
				}).css(cssoptions);
			}
		}
	};

	jQuery.ajaxloader.defaults = {
		img: 'ajaxloader_loading.gif',
		imglocation: 'templates/backoffice/images/',
		appendto: 'body',
		width: 100,
		height: 100,
		position: 'fixed',
		overlay: true,
		close: false,
		zindex: 200,
		top: 0,
		left: 0,
		marginleft: 0,
		margintop: 0
	};
})();

/**
 * show error
 */
(function($) {
	jQuery.ShowError = function(message, options, f) {
		options = jQuery.extend({},jQuery.ShowError.defaults,options);
		jQuery.ajaxq('ajax');
		jQuery.ajaxloader({close:true});
		var alreadycreated = jQuery('#dialog').size();
		if(alreadycreated == 0){
			jQuery('<div></div>').appendTo(document.body).attr({
				id: 'dialog'
			});
			jQuery('#dialog').dialog({autoOpen: false});
		}

		jQuery('#dialog').dialog('option', 'buttons', {
			"Ok": function() {
				jQuery(this).dialog("close");
				if (typeof 1 == "function") f();
			}
		});
		jQuery('#dialog').dialog('option', 'title', options.title);
		jQuery('#dialog').dialog('option', 'height', options.height);
		jQuery('#dialog').dialog('option', 'width', options.width);
		jQuery('#dialog').dialog('option', 'position', options.position);
		jQuery('#dialog').dialog('option', 'modal', options.modal);

		jQuery('#dialog').html(message);
		jQuery('#dialog').dialog('open');
	};

	jQuery.ShowError.defaults = {
		height: 530,
		width: 1050,
		title: 'Error',
		position: [100, 100],
		modal: true
	};
})();


/**
 * show message
 */
(function($) {
	jQuery.message = function(message, options, f) {
		options = jQuery.extend({},jQuery.message.defaults,options);

		var alreadycreated = jQuery('#dialog').size();
		if(alreadycreated == 0){
			jQuery('<div></div>').appendTo(document.body).attr({
				id: 'dialog'
			});
			jQuery('#dialog').dialog({autoOpen: false});
		}

		if(options.buttons == false){
			jQuery('#dialog').dialog('option', 'buttons', {
				"Ok": function() {
					jQuery(this).dialog("close");
					if (typeof f == "function") f();
				}
			});
		} else {
			jQuery('#dialog').dialog('option', 'buttons', options.buttons);
		}
		var breedte;
		if(options.textarea){
			message = '<textarea rows="20" cols="140">' + message + '</textarea>';
			breedte = 800;
		} else {
			breedte = options.width;
		}


		if(options.template){

			var mydata = message;

			jQuery("#dialog").setTemplateElement(options.template);
			jQuery("#dialog").processTemplate(mydata);
		}

		jQuery('#dialog').dialog('option', 'title', options.title);
		jQuery('#dialog').dialog('option', 'height', options.height);
		jQuery('#dialog').dialog('option', 'width', breedte);
		jQuery('#dialog').dialog('option', 'position', options.position);
		jQuery('#dialog').dialog('option', 'modal', options.modal);
		if(!options.template)
			jQuery('#dialog').html(message);

		jQuery('#dialog').dialog('open');
	};

	jQuery.message.defaults = {
		height: 'auto',
		width: 400,
		title: 'Bericht',
		position: 'center',
		modal: true,
		buttons: false,
		textarea: false,
		template: false,
		ajax: false
	};
})();


/**
 * Confirm box
 */
(function($) {
	jQuery.confirm = function(message, options, j, n) {

		if (typeof options == "function"){
			n = j;
			j = options;
			options = {};
		}
		options = jQuery.extend({},jQuery.confirm.defaults,options);

		var alreadycreated = jQuery('#dialog').size();
		if(alreadycreated == 0){
			jQuery('<div></div>').appendTo(document.body).attr({
				id: 'dialog'
			});
			jQuery('#dialog').dialog({autoOpen: false});
		}

		jQuery('#dialog').dialog('option', 'buttons', {
			"Nee": function() {
				jQuery('#dialog').dialog("close");
				if (typeof n == "function") n();
			},
			"Ja": function() {
				jQuery('#dialog').dialog("close");
				if (typeof j == "function") j();
			}
		});

		jQuery('#dialog').dialog('option', 'title', options.title);
		jQuery('#dialog').dialog('option', 'height', options.height);
		jQuery('#dialog').dialog('option', 'width', options.width);
		jQuery('#dialog').dialog('option', 'position', options.position);
		jQuery('#dialog').dialog('option', 'modal', options.modal);

		jQuery('#dialog').html(message);
		jQuery('#dialog').dialog('open');
	};

	jQuery.confirm.defaults = {
		height: 'auto',
		width: 400,
		title: 'Bevestig',
		position: 'center',
		modal: true
	};
})();

/**
 * popover
 */
(function($) {
	jQuery.popover = function(text, options) {
		options = jQuery.extend({},jQuery.popover.defaults,options);

		if(options.close){
			jQuery('#overlaycontent').remove();
			jQuery('#popovershadow').remove();
			jQuery.overlay({close:true});
		} else {
			jQuery.overlay();

			if (options.shadow) {
				jQuery('<div></div>').appendTo('.ui-overlay').attr({
					id: 'popovershadow'
				}).addClass('ui-widget-shadow ui-corner-all').css({
					width: (options.width+22),
					height: (options.height+22),
					left: options.left,
					top: options.top,
					'margin-left': (options.marginleft-11)+'px',
					'margin-top': (options.margintop-11)+'px',
					position: options.position,
					'z-index': options.zindex
				});
			}


			jQuery('<div></div>').appendTo(document.body).attr({
				id: 'overlaycontent'
			}).addClass('ui-widget ui-widget-content ui-corner-all').css({
				width: options.width,
				height: options.height,
				left: options.left,
				top: options.top,
				padding: 7,
				'margin-left': options.marginleft+'px',
				'margin-top': options.margintop+'px',
				position: options.position,
				'z-index': (options.zindex+10)
			});

			if(options.closebutton){
				jQuery('<img />').appendTo('#overlaycontent').attr({
					src: 'templates/backoffice/images/icons/Exit.png',
					id: 'closeicon'
				}).addClass('editicon').css({
					'float': 'right'
				});
				jQuery('#closeicon').click(function(){
					jQuery.popover('', {close:true});
				});
			}

			jQuery('<div></div>').appendTo('#overlaycontent').attr({
				id: 'overlaytext'
			}).addClass('ui-dialog-content ui-widget-content').css({
				background: 'none',
				border: 0
			});



			jQuery('#overlaytext').append(text);
		}
	};

	jQuery.popover.defaults = {
		shadow: true,
		width: 600,
		height: 300,
		margintop: -150,
		marginleft: -300,
		top: '50%',
		left: '50%',
		position: 'fixed',
		zindex: 2000,
		closebutton: true
	};
})();

/**
 * Confirm box
 */
(function($) {
	jQuery.messagediv = function(text, options) {
		options = jQuery.extend({},jQuery.messagediv.defaults,options);

			var divclass = options.error ? 'ui-state-error ui-corner-all' : 'ui-state-highlight ui-corner-all';

			jQuery('<div></div>').prependTo(options.prepend).attr({
				id: 'createdmessagediv'
			}).addClass('ui-widget').css({
				'padding-bottom': '10px'
			});

			jQuery('<div></div>').appendTo('#createdmessagediv').attr({
				id: 'createdmessagediv1'
			}).addClass(divclass).css({
				padding: '0.7em'
			});

			jQuery('<p></p>').appendTo('#createdmessagediv1').attr({
				id: 'createdmessagedivp'
			});

			var iconclass = options.error ? 'ui-icon ui-icon-alert' : 'ui-icon ui-icon-info';

			jQuery('<span></span>').appendTo('#createdmessagedivp').addClass(iconclass).css({
				'float': 'left',
				'margin-right': '.3em'
			});

			var infotext = options.error ? 'Fout' : 'Info';

			jQuery('#createdmessagedivp').append('<strong>'+infotext+':</strong> ' + text);

			setTimeout('jQuery("#createdmessagediv").slideUp("slow", function(){jQuery("#createdmessagediv").remove();});', options.timeout);
		
	};

	jQuery.messagediv.defaults = {
		error: false,
		timeout: 3000,
		prepend: '#inhoudvak'
	};
})();

/**
 * Confirm box
 */
(function($) {
	jQuery.flashuploader = function(fileinput, options) {
		options = jQuery.extend({},jQuery.flashuploader.defaults,options);
		
		var uploads = jQuery(fileinput).size();
		var sessieid = jQuery('#sessieid').val();

		jQuery.each(jQuery(fileinput), function(){
			if(jQuery('#'+this.id+':visible').size()>0){
				jQuery(this).uploadify({
					uploader				: 'templates/backoffice/scripts/uploadify.swf',
					script					: options.script,
					scriptData			:	{
						'sessie_id': sessieid,
						'functie': options.functie,
						'extra':	options.extraScriptData,
						'login':	options.login,
						'fieldid': this.id
					},
					auto						: options.auto,
					multi						: options.multi,
					fileDesc			  : options.fileDesc,
					fileExt				  : options.fileExt,
					folder					: options.folder,
					height					: options.height,
					width						: options.width,
					buttonText			:	options.buttonText,
					cancelImg				: options.cancelImg,
					wmode						: options.wmode,
					scriptAccess		: options.scriptAccess,
					fileDataName		: options.fileDataName,
					method					: options.method,
					queueSizeLimit	: options.queueSizeLimit,
					simUploadLimit	: options.simUploadLimit,
					queueID					: options.queueID,
					displayData			: options.displayData,
					onInit					: options.onInit,
					onSelect				: options.onSelect,
					onQueueFull			: options.onQueueFull,
					onCheck					: options.onCheck,
					onCancel				: options.onCancel,
					onError					: options.onError,
					onProgress			: options.onProgress,
					onComplete			: options.onComplete,
					onAllComplete		: options.onAllComplete
				});
			}
		});
	};

	
	var baseurl = jQuery('#baseurl').attr('href');
	
	jQuery.flashuploader.defaults = {
		script         : baseurl + 'uploaden.html',
		folder         : '',
		functie				 : '',
		login					 : true,
		auto					 : true,
		multi					 : false,
		height         : 30,
		buttonText		 : 'Bladeren',
		width          : 110,
		extraScriptData: '',
		cancelImg      : 'templates/backoffice/images/cancel.png',
		wmode          : 'opaque',
		scriptAccess   : 'sameDomain',
		fileDataName   : 'Filedata',
		fileDesc			 : 'Alleen afbeeldingen',
		fileExt				 : '*.jpg;*.gif;*.png;*.jpeg',
		method         : 'POST',
		queueSizeLimit : 999,
		simUploadLimit : 1,
		queueID        : false,
		displayData    : 'percentage',
		onInit         : function() {},
		onSelect       : function() {},
		onQueueFull    : function() {},
		onCheck        : function() {},
		onCancel       : function() {},
		onError        : function() {},
		onProgress     : function() {},
		onComplete     : function() {},
		onAllComplete  : function() {}
	};
})();

/**
 * Load url
 */
(function($) {
	jQuery.LoadUrl = function(link) {
		if(link == 'current'){
			window.location = window.location;
			return;
		}

		/*
		var host = jQuery.url.attr("host");
		var elem1 = jQuery.url.segment(0);
		var elem2 = jQuery.url.segment(1);
		var elem3 = jQuery.url.segment(2);
		*/
		var prot = jQuery.url.setUrl(link).attr("protocol");

		if(prot == 'http'){
			window.location.href = link;
		} else {

			var baseurl = jQuery('#baseurl').attr('href');
			window.location.href = baseurl + '/' + link;

			/*
			if(elem1 == 'backoffice'){
				window.location.href = 'http://'+host+'/'+link;
			} else if (elem2 == 'backoffice'){
				window.location.href = 'http://'+host+'/'+elem1+'/'+link;
			} else if (elem3 == 'backoffice'){
				window.location.href = 'http://'+host+'/'+elem1+'/'+elem2+'/'+link;
			} else {
				window.location.href = 'http://'+host+'/'+link;
			}
			*/
		}
	};
})();


//TODO: getPassword jquery script maken
function getRandomNum(lbound, ubound) {
	return (Math.floor(Math.random() * (ubound - lbound)) + lbound);
}

function getRandomChar(number, lower, upper, other, extra) {
	var numberChars = "0123456789";
	var lowerChars = "abcdefghijklmnopqrstuvwxyz";
	var upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var otherChars = "!@#$%&*?";
	var charSet = extra;
	if (number == true)
		charSet += numberChars;
	if (lower == true)
		charSet += lowerChars;
	if (upper == true)
		charSet += upperChars;
	if (other == true)
		charSet += otherChars;
	return charSet.charAt(getRandomNum(0, charSet.length));
}

function getPassword(length, extraChars, firstNumber, firstLower, firstUpper, firstOther,
	latterNumber, latterLower, latterUpper, latterOther) {
	var rc = "";
	if (length > 0)
		rc = rc + getRandomChar(firstNumber, firstLower, firstUpper, firstOther, extraChars);
	for (var idx = 1; idx < length; ++idx) {
		rc = rc + getRandomChar(latterNumber, latterLower, latterUpper, latterOther, extraChars);
	}
	return rc;
}




$.validator.addMethod("captcha", function(value, element, param) {
	if ( this.optional(element) )
		return "dependency-mismatch";
	var previous = this.previousValue(element);
	if (!this.settings.messages[element.name] )
		this.settings.messages[element.name] = {};
	this.settings.messages[element.name].remote = typeof previous.message == "function" ? previous.message(value) : previous.message;
	param = typeof param == "string" && {url:param} || param;
	if ( previous.old !== value ) {
		previous.old = value;
		var validator = this;
		this.startRequest(element);
		var data = {};
		data[element.name] = value;
		$.ajax($.extend(true, {
			url: param,
			mode: "abort",
			port: "validate" + element.name,
			dataType: "json",
			data: data,
			success: function(response) {
				var valid = response === true;
				var rand = getPassword(2, true, true, true, true, false, true, true, true, true);
				if ( valid ) {
					var submitted = validator.formSubmitted;
					validator.prepareElement(element);
					validator.formSubmitted = submitted;
					validator.successList.push(element);
					validator.showErrors();
				} else {
					jQuery('body').find('img[alt="Captcha"]').attr('src', 'captcha/default.html?value='+rand);
					var errors = {};
					errors[element.name] = previous.message = response || validator.defaultMessage( element, "captcha" );
					validator.showErrors(errors);
				}
				previous.valid = valid;
				validator.stopRequest(element, valid);
			}
		}, param));
		return "pending";
	} else if( this.pending[element.name] ) {
		return "pending";
	}
	return previous.valid;
}, 'De ingevulde waarde is al in gebruik.');

$.validator.addMethod("decimal", function(value, element, param) {
	var reg = new RegExp($(element).attr('ref'));
	value = value.replace('.', '');
	var val = value.split(',');
	if(val[0].length > param[0]){
		return false;
	} else {
		return this.optional(element) || reg.test(value);
	}
}, $.validator.format('Max. {0} cijfers voor de komma en max. {1} na de komma'));

/**
 * sprintf and vsprintf for jQuery
 * somewhat based on http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/
 *
 * Copyright (c) 2008 Sabin Iacob (m0n5t3r) <iacobs@m0n5t3r.info>
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * @license http://www.gnu.org/licenses/gpl.html
 * @project jquery.sprintf
 */
(function($){
	var formats = {
		'%': function(val) {return '%';},
		'b': function(val) {return  parseInt(val, 10).toString(2);},
		'c': function(val) {return  String.fromCharCode(parseInt(val, 10));},
		'd': function(val) {return  parseInt(val, 10) ? parseInt(val, 10) : 0;},
		'u': function(val) {return  Math.abs(val);},
		'f': function(val, p) {return  (p > -1) ? Math.round(parseFloat(val) * Math.pow(10, p)) / Math.pow(10, p): parseFloat(val);},
		'o': function(val) {return  parseInt(val, 10).toString(8);},
		's': function(val) {return  val;},
		'x': function(val) {return  ('' + parseInt(val, 10).toString(16)).toLowerCase();},
		'X': function(val) {return  ('' + parseInt(val, 10).toString(16)).toUpperCase();}
	};

	var re = /%(?:(\d+)?(?:\.(\d+))?|\(([^)]+)\))([%bcdufosxX])/g;

	var dispatch = function(data){
		if(data.length == 1 && typeof data[0] == 'object') { //python-style printf
			data = data[0];
			return function(match, w, p, lbl, fmt, off, str) {
				return formats[fmt](data[lbl]);
			};
		} else { // regular, somewhat incomplete, printf
			var idx = 0; // oh, the beauty of closures :D
			return function(match, w, p, lbl, fmt, off, str) {
				return formats[fmt](data[idx++], p);
			};
		}
	};

	$.extend({
		sprintf: function(format) {
			var argv = Array.apply(null, arguments).slice(1);
			return format.replace(re, dispatch(argv));
		},
		vsprintf: function(format, data) {
			return format.replace(re, dispatch(data));
		}
	});
})(jQuery);

/*
 * Copyright (c) 2008 Greg Weber greg at gregweber.info
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * documentation at http://gregweber.info/projects/uitablefilter
 *
 * allows table rows to be filtered (made invisible)
 * <code>
 * t = $('table')
 * $.uiTableFilter( t, phrase )
 * </code>
 * arguments:
 *   jQuery object containing table rows
 *   phrase to search for
 *   optional arguments:
 *     column to limit search too (the column title in the table header)
 *     ifHidden - callback to execute if one or more elements was hidden
 */
jQuery.uiTableFilter = function(jq, phrase, column, ifHidden){
  var new_hidden = false;
  if( this.last_phrase === phrase ) return false;

  var phrase_length = phrase.length;
  var words = phrase.toLowerCase().split(" ");

  // these function pointers may change
	var matches = function(elem) { elem.show();  }
  var noMatch = function(elem) { elem.hide(); new_hidden = true }
  var getText = function(elem) { return elem.text(); }

  if( column ) {
    var index = null;
    jq.find("thead > tr:first > th").each( function(i){
      if( $(this).text() == column ){
        index = i; return false;
      }
    });
    if( index == null ) throw("given column: " + column + " not found")

    getText = function(elem){ return jQuery(elem.find(
      ("td:eq(" + index + ")")  )).text()
    }
  }

  // if added one letter to last time,
  // just check newest word and only need to hide
  if( (words.size > 1) && (phrase.substr(0, phrase_length - 1) ===
        this.last_phrase) ) {

    if( phrase[-1] === " " )
    { this.last_phrase = phrase; return false; }

    var words = words[-1]; // just search for the newest word

    // only hide visible rows
    matches = function(elem) {;}
    var elems = jq.find("tbody > tr:visible")
  }
  else {
    new_hidden = true;
    var elems = jq.find("tbody > tr")
  }

  elems.each(function(){
    var elem = jQuery(this);
    jQuery.uiTableFilter.has_words( getText(elem), words, false, elem ) ?
      matches(elem) : noMatch(elem);
  });

  last_phrase = phrase;
  if( ifHidden && new_hidden ) ifHidden();
	initPagination();
  return jq;
};

// caching for speedup
jQuery.uiTableFilter.last_phrase = ""

// not jQuery dependent
// "" [""] -> Boolean
// "" [""] Boolean -> Boolean
jQuery.uiTableFilter.has_words = function( str, words, caseSensitive, elem )
{
  var text = caseSensitive ? str : str.toLowerCase();
  for (var i=0; i < words.length; i++) {
    if (text.indexOf(words[i]) === -1){
			elem.removeClass('result');
			return false;
		}
  }
	elem.addClass('result');
  return true;
}

function GetBackofficeUrl(){
	var link;
	var host = $.url.attr("host");
	var element = '';
	for(var i = 0; i < 15;i++){
		element += $.url.segment(i)+'/';
		if($.url.segment(i) == 'backoffice'){
			i=20;
		}
	}
	return 'http://'+host+'/'+element;
}

function GetUploadScriptUrl(){
	var url = window.location.href;
	var url_segments = url.split('/');
	var tel = 0;
	var telling = false;
	$.each(url_segments, function(index, value){
		if (value == 'backoffice'){
			telling = true;
		}
		if (telling){
			tel++;
		}
	});
	var url_prefix = '';
	for(var i = 1; i<tel;i++){
		url_prefix += '../';
	}
	return url_prefix;
}

function GetModuleImageUrl(){
	var link;
	var host = $.url.attr("host");
	var element = '';
	for(var i = 0; i < 15;i++){
		if($.url.segment(i) == 'backoffice'){
			i=20;
		} else {
			element += $.url.segment(i)+'/';
		}
	}
	return 'http://'+host+'/'+element + '/modules/templates/backoffice/images/';
}

function GetModuleScriptsUrl(){
	var link;
	var host = $.url.attr("host");
	var element = '';
	for(var i = 0; i < 15;i++){
		if($.url.segment(i) == 'backoffice'){
			i=20;
		} else {
			element += $.url.segment(i)+'/';
		}
	}
	return 'http://'+host+'/'+element + '/modules/templates/backoffice/scripts/';
}

