YUI.add('aui-template-deprecated', function (A, NAME) {

var Lang = A.Lang,
	UA = A.UA,
	LString = Lang.String,
	isArray = Lang.isArray,
	isDate = Lang.isDate,
	isString = Lang.isString,
	isObject = Lang.isObject,
	isValue = Lang.isValue,
	isUndefined = Lang.isUndefined,

	REGEX_TPL = /<tpl\b[^>]*?((for|if|exec)="([^"]+)")*?>((?:(?=([^<]+))\5|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
	REGEX_NEWLINE = /\r\n|\n/g,
	REGEX_QUOTE = /'/g,
	REGEX_QUOTE_ESCAPED = /\\'/g,
	REGEX_PREFIX_GLOBAL_REPLACE = /^(?!\$)/,

	REGEX_TPL_VAR = /\{([\w-.#$]+)(?:\:([\w.]*)(?:\((.*?)?\))?)?(\s?[+\-*\/]\s?[\d.+\-*\/()]+)?\}/g,
	REGEX_TPL_SCRIPTLET = /\{\[((?:\\\]|.|\n)*?)\]\}/g,

	STR_BLANK = '',
	STR_COLON = ':',
	STR_COMMA = ',',
	STR_DOT = '.',
	STR_FOR = 'for',
	STR_EXEC = 'exec',
	STR_IF = 'if',
	STR_QUOTE = '\'',
	STR_BRACE_OPEN = '{',
	STR_BRACE_CLOSE = '}',
	STR_PAREN_OPEN = '(',
	STR_PAREN_CLOSE = ')',
	STR_GLOBAL_SYMBOL = '$',

	STR_JOIN_OPEN = STR_QUOTE + STR_COMMA,
	STR_JOIN_CLOSE = STR_COMMA + STR_QUOTE,

	STR_JOIN_GROUP_OPEN = STR_JOIN_OPEN + STR_PAREN_OPEN,
	STR_JOIN_GROUP_CLOSE = STR_PAREN_CLOSE + STR_JOIN_CLOSE,

	STR_COMPILE_TPL_ARGS = 'values, parent, $index, $i, $count, $last, $ns, $ans, $yns',

	BUFFER_HTML = ['<tpl>', null, '</tpl>'],

	BUFFER_STR_COMPILE_SUB_TPL = [
		STR_JOIN_OPEN + 'this._compileSubTpl(',
		null,
		STR_COMMA + STR_COMPILE_TPL_ARGS + STR_PAREN_CLOSE + STR_JOIN_CLOSE
	],

	BUFFER_COMPILED_TPL_FN = [
		'compiledTplFn = function(' + STR_COMPILE_TPL_ARGS + ') { return [' + STR_QUOTE,
		null,
		STR_QUOTE + '].join("");};'
	],

	BUFFER_GLOBAL_PROP = ['MAP_GLOBALS["', null, '"]'],

	BUFFER_VALUES_LOOKUP = [
		'values["',
		null,
		'"]'
	],

	STR_INVOKE_METHOD_NAME_OPEN = 'this._invokeMethod("',
	STR_INVOKE_METHOD_NAME_CLOSE = '"' + STR_COMMA,

	STR_UNDEFINED_OUT = ' === undefined ? "" : ',

	STR_REPLACE_NEWLINE = '\\n',
	STR_REPLACE_QUOTE =  "\\'",

	STR_VALUES = 'values',
	STR_PARENT = 'parent',
	STR_SPECIAL_I = '$i',
	STR_SPECIAL_INDEX = '$index',
	STR_SPECIAL_COUNT = '$count',
	STR_SPECIAL_LAST = '$last',
	STR_SPECIAL_ANS = '$ans',
	STR_SPECIAL_NS = '$ns',
	STR_SPECIAL_YNS = '$yns',
	STR_RETURN = 'return ',
	STR_WITHVALUES = 'with(values){ ',
	STR_WITHCLOSE = '; }',

	STR_LANGSTRING_VAR = 'LString.',

	TOKEN_TPL = 'AUITPL',
	TOKEN_TPL_LENGTH = TOKEN_TPL.length,
	TOKEN_PARENT_PROP = STR_PARENT + STR_DOT,
	TOKEN_THIS_PROP = 'this.',
	TOKEN_THIS_PROP_LENGTH = TOKEN_THIS_PROP.length,
	TOKEN_VALUES_PROP = STR_VALUES + STR_DOT,

	MAP_TPL_FN = {
		'.': new Function(
			STR_VALUES,
			STR_PARENT,
			STR_WITHVALUES + STR_RETURN + STR_VALUES + STR_WITHCLOSE
		),
		'..': new Function(
			STR_VALUES,
			STR_PARENT,
			STR_WITHVALUES + STR_RETURN + STR_PARENT + STR_WITHCLOSE
		)
	},

	MAP_TPL_FILTERED_TYPES = {
		'boolean': true,
		'number': true,
		'string': true
	},

	MAP_TPL_VALUES = {
		'.': 'this._getValidValues(values)',
		'#': STR_SPECIAL_INDEX,
		'$index': STR_SPECIAL_INDEX,
		'$i': STR_SPECIAL_I,
		'$count': STR_SPECIAL_COUNT,
		'$last': STR_SPECIAL_LAST,
		'$ans': STR_SPECIAL_ANS,
		'$ns': STR_SPECIAL_NS,
		'$yns': STR_SPECIAL_YNS
	},

	MAP_GLOBALS = {},

	SRC_CREATE = {},

	AUI_NS = A.getClassName(STR_BLANK),
	YUI_NS = A.ClassNameManager.getClassName(STR_BLANK),

	_INSTANCES = {};

	var Template = function(html, src) {
		var instance = this;

		var tpl;
		var fromStaticCall = (src === SRC_CREATE);

		if (fromStaticCall || A.instanceOf(instance, Template)) {
			if (!fromStaticCall) {
				html = instance._parseArgs(arguments);
			}

			tpl = instance._parse(html);
		}
		else {
			html = Template.prototype._parseArgs(arguments);

			tpl = new Template(html, SRC_CREATE);
		}

		return tpl;
	};

	Template.prototype = {
		html: function(tpl) {
			var instance = this;

			var retVal = instance;

			if (isValue(tpl)) {
				instance._parse(instance._parseArgs(arguments));
			}
			else {
				retVal = instance._html;
			}

			return retVal;
		},

		parse: function(values) {
			var instance = this;

			return instance._parentTpl.compiled.call(instance, values, {}, 1, 1, 1);
		},

		render: function(values, node) {
			var instance = this;

			var rendered = A.Node.create(instance.parse(values));

			node = node && A.one(node);

			if (node) {
				node.setContent(rendered);
			}

			return rendered;
		},

		_compile: function(tpl) {
			var instance = this;

			var compiledTplFn;

			var fnBody = tpl.tplBody;

			fnBody = fnBody.replace(REGEX_NEWLINE, STR_REPLACE_NEWLINE);
			fnBody = fnBody.replace(REGEX_QUOTE, STR_REPLACE_QUOTE);
			fnBody = fnBody.replace(REGEX_TPL_VAR, instance._compileTpl);
			fnBody = fnBody.replace(REGEX_TPL_SCRIPTLET, instance._compileCode);

			BUFFER_COMPILED_TPL_FN[1] = fnBody;

			var body = BUFFER_COMPILED_TPL_FN.join(STR_BLANK);

			var $yns = instance.$yns;
			var $ans = instance.$ans;
			var $ns = instance.$ns;

			eval(body);

			tpl.compiled = function(values, parent, $index, $i, $count, $last) {
				var buffer = [];

				var subTpl = STR_BLANK;

				var testFn = tpl.testFn;

				if (!testFn || testFn.call(instance, values, parent, $index, $i, $count, $last, $ns, $ans, $yns)) {
					var subValues = values;

					var tplFn = tpl.tplFn;

					if (tplFn) {
						subValues = tplFn.call(instance, values, parent);
						parent = values;
					}

					if (tplFn && isArray(subValues)) {
						var length = subValues.length;

						var execFn = tpl.execFn;

						for (var i = 0; i < length; i++) {
							var index = i + 1;
							var last = (index == length);
							var subValue = subValues[i];

							buffer[buffer.length] = compiledTplFn.call(instance, subValue, parent, index, i, length, last, $ns, $ans, $yns);

							if (execFn) {
								execFn.call(instance, subValues[i]);
							}
						}

						subTpl = buffer.join(STR_BLANK);
					}
					else {
						subTpl = compiledTplFn.call(instance, subValues, parent, $index, $i, $count, $last, $ns, $ans, $yns);
					}
				}

				return subTpl;
			};

			return instance;
		},

		_compileCode: function(match, code) {
			return STR_JOIN_GROUP_OPEN + code.replace(REGEX_QUOTE_ESCAPED, STR_QUOTE) + STR_JOIN_GROUP_CLOSE;
		},

		_compileSubTpl: function(id, values, parent, $index, $i, $count, $last, $ns, $ans, $yns) {
			var instance = this;

			var length;
			var tpl = instance.tpls[id];

			return tpl.compiled.call(instance, values, parent, $index, $i, $count, $last, $ns, $ans, $yns);
		},

		_compileTpl: function(match, name, methodName, args, math, offset, str) {
			var compiled;

			if (name.indexOf(TOKEN_TPL) === 0) {
				BUFFER_STR_COMPILE_SUB_TPL[1] = name.substr(TOKEN_TPL_LENGTH);

				compiled = BUFFER_STR_COMPILE_SUB_TPL.join(STR_BLANK);
			}
			else {
				var value = MAP_TPL_VALUES[name];

				if (!value) {
					if (name.indexOf(TOKEN_PARENT_PROP) === 0) {
						value = name;
					}
					else if (name.indexOf(STR_DOT) > -1) {
						value = TOKEN_VALUES_PROP + name;
					}
					else if (name.indexOf(STR_GLOBAL_SYMBOL) === 0 && (name in MAP_GLOBALS)) {
						BUFFER_GLOBAL_PROP[1] = name;

						value = BUFFER_GLOBAL_PROP.join(STR_BLANK);

					}
					else {
						BUFFER_VALUES_LOOKUP[1] = name;

						value = BUFFER_VALUES_LOOKUP.join(STR_BLANK);
					}
				}

				if (math) {
					value = STR_PAREN_OPEN + value + math + STR_PAREN_CLOSE;
				}

				if (methodName) {
					args = args ? STR_COMMA + args : STR_BLANK;

					if (methodName.indexOf(TOKEN_THIS_PROP) !== 0) {
						methodName = STR_LANGSTRING_VAR + methodName + STR_PAREN_OPEN;
					}
					else {
						methodName = STR_INVOKE_METHOD_NAME_OPEN + methodName.substr(TOKEN_THIS_PROP_LENGTH) + STR_INVOKE_METHOD_NAME_CLOSE;

						args = STR_COMMA + STR_VALUES;
					}
				}
				else {
					args = STR_BLANK;

					methodName = STR_PAREN_OPEN + value + STR_UNDEFINED_OUT;
				}

				compiled = STR_JOIN_OPEN + methodName + value + args + STR_JOIN_GROUP_CLOSE;
			}

			return compiled;
		},

		_getValidValues: function(values) {
			var instance = this;

			var val = STR_BLANK;

			if (MAP_TPL_FILTERED_TYPES[typeof values] || isDate(values)) {
				val = values;
			}

			return val;
		},

		_invokeMethod: function(methodName, value, allValues) {
			var instance = this;

			return instance[methodName](value, allValues);
		},

		_parse: function(html) {
			var instance = this;

			instance._html = html;

			var match;
			var id = 0;
			var tpls = [];

			BUFFER_HTML[1] = html;

			html = BUFFER_HTML.join(STR_BLANK);

			while (match = html.match(REGEX_TPL)) {
				var testFn = null;
				var execFn = null;
				var tplFn = null;
				var expression = match[2];
				var expressionValue = match[3];

				if (expressionValue) {
					if (expression == STR_FOR) {
						tplFn = MAP_TPL_FN[expressionValue] || new Function(
							STR_VALUES,
							STR_PARENT,
							STR_WITHVALUES + STR_RETURN + expressionValue + STR_WITHCLOSE
						);
					}
					else {
						expressionValue = LString.unescapeEntities(expressionValue);

						if (expression == STR_IF) {
							testFn = new Function(
								STR_VALUES,
								STR_PARENT,
								STR_SPECIAL_INDEX,
								STR_SPECIAL_I,
								STR_SPECIAL_COUNT,
								STR_SPECIAL_LAST,
								STR_SPECIAL_NS,
								STR_SPECIAL_ANS,
								STR_SPECIAL_YNS,
								STR_WITHVALUES + STR_RETURN + expressionValue + STR_WITHCLOSE
							);
						}
						else if (expression == STR_EXEC) {
							execFn = new Function(
								STR_VALUES,
								STR_PARENT,
								STR_SPECIAL_INDEX,
								STR_SPECIAL_I,
								STR_SPECIAL_COUNT,
								STR_SPECIAL_LAST,
								STR_SPECIAL_NS,
								STR_SPECIAL_ANS,
								STR_SPECIAL_YNS,
								STR_WITHVALUES + expressionValue + STR_WITHCLOSE
							);
						}
					}
				}

				var tplBody = match[4] || STR_BLANK;

				html = html.replace(match[0], STR_BRACE_OPEN + TOKEN_TPL + id + STR_BRACE_CLOSE);

				id = tpls.push(
					{
						execFn: execFn,
						id: id,
						testFn: testFn,
						tplBody: tplBody,
						tplFn: tplFn
					}
				);
			}

			var lastIndex = id - 1;

			while (id--) {
				instance._compile(tpls[id]);
			}

			instance._parentTpl = tpls[lastIndex];

			instance.tpls = tpls;

			return instance;
		},

		_parseArgs: function(args) {
			var instance = this;

			var config;

			var tpl = args[0];

			if (isArray(tpl)) {
				if (isObject(tpl[tpl.length - 1])) {
					config = tpl.pop();
				}
				else if (isObject(args[1])) {
					config = args[1];
				}

				tpl = tpl.join(STR_BLANK);
			}
			else if (args.length > 1) {
				var buffer = [];

				args = A.Array(args, 0, true);

				var length = args.length;
				var item;

				var lastItem = args[length - 1];

				if (isObject(lastItem)) {
					config = args.pop();
				}

				for (var i = 0; i < length; i++) {
					item = args[i];

					buffer.push(item);
				}

				tpl = buffer.join(STR_BLANK);
			}

			if (config) {
				A.mix(instance, config, true);
			}

			return tpl;
		},

		$ans: AUI_NS,
		$yns: YUI_NS
	};

	var TEMPLATE_PROTO = Template.prototype;

	TEMPLATE_PROTO.$ns = AUI_NS;

	var globalVar = function(key, value) {
		var retVal = null;

		if (isUndefined(value) && key) {
			retVal = MAP_GLOBALS[key];
		}
		else {
			if (key) {
				key = String(key).replace(REGEX_PREFIX_GLOBAL_REPLACE, STR_GLOBAL_SYMBOL);

				if (value !== null) {
					MAP_GLOBALS[key] = value;
					retVal = value;
				}
				else {
					delete MAP_GLOBALS[key];
				}
			}
		}

		return retVal;
	};

	Template.globalVar = TEMPLATE_PROTO.globalVar = globalVar;

	Template._GLOBALS = MAP_GLOBALS;

	var NODE_PROTO = A.Node.prototype;

	NODE_PROTO.toTPL = function() {
		return Template.from(this);
	};

	NODE_PROTO.renderTPL = function(tpl, data) {
		var instance = this;

		if (isString(tpl) || isArray(tpl)) {
			tpl = new Template(tpl);
		}

		if (tpl && data) {
			tpl.render(instance, data);
		}

		return instance;
	};

	A.NodeList.importMethod(
		NODE_PROTO,
		[
			'renderTPL'
		]
	);

	A.mix(
		Template,
		{
			from: function(node) {
				node = A.one(node);

				var content = STR_BLANK;

				if (node) {
					node = node.getDOM();

					var nodeName = String(node && node.nodeName).toLowerCase();

					if (nodeName != 'script') {
						content = node.value || node.innerHTML;
					}
					else {
						content = node.text || node.textContent || node.innerHTML;
					}
				}

				return new Template(content);
			},

			get: function(key) {
				var template = _INSTANCES[key];

				if (template && !A.instanceOf(template, Template)) {
					template = new Template(template);

					_INSTANCES[key] = template;
				}

				return template;
			},

			parse: function(key, data) {
				var template = Template.get(key);

				return template && template.parse(data);
			},

			register: function(key, value) {
				var instance = this;

				var tpl = value;

				if (!(key in _INSTANCES) &&
					(Lang.isArray(value) || A.instanceOf(value, Template))) {

					_INSTANCES[key] = value;
				}

				return value;
			},

			render: function(key, data, node) {
				var template = Template.get(key);

				return template && template.render(data, node);
			},

			_INSTANCES: _INSTANCES
		}
	);

	A.Template = Template;

}, '2.0.0', {"requires": ["aui-base-deprecated"]});
