/*
Script: Template.js
	Contains the <Template>.

License:
	MIT-style license.

Credits:
	Hong MinHee, <http://dahlia.pe.kr/>
*/

/*
Class: Template
	Creates a new Template object.

Arguments:
	formstString - (string)
	options - (object, optional) See "Options" below.

Options:
	begin - (string) The prefix character(s) of expression code to interpolate in formatString. Default value is "#(".
	end - (string) The suffix character(s) of expression code to interpolate in formatString. Default value is ")".

Example:
	(start code)
	var classAuthorLabel = new Template(
		'#(name) class by <a href="#(author.url.trim())">#(author.name)</a>'
	);

	var templateClass = {
		name: 'Template',
		author: {
			name: 'Hong, MinHee',
			email: 'http://dahlia.pe.kr/'
		}
	};

	document.writeln(classAuthorLabel.evaluate(templateClass));
	(end)
*/

var Template = new Class({
	options: {
		begin: '#(',
		end: ')'
	},

	initialize: function(formatString, options) {
		this.formatString = formatString;
		this.setOptions(options);
	},

	/*
	Property: evaluate
		Evaluate expression code to interpolate in template format string.

	Arguments:
		context - (object) Context object.

	Returns:
		(string) Evaluated string.
	*/

	evaluate: function(context) {
		var result = '';
		var format = this.formatString;
		var begin = this.options.begin;
		var end = this.options.end;

		for(var i = 0; i < format.length; ++i) {
			if(format.substr(i, begin.length) == begin) {
				var exprBegin = i + begin.length;
				var termination = Template.getExpressionLength(
					format.substr(exprBegin), end
				);

				if($type(termination) == 'number') {
					_$_$code$_$_ = format.substr(exprBegin, termination);

					if(eval('context', {context: false})) {
						with(context) {
							_$_$value$_$_ = eval('(' + _$_$code$_$_ + ')');
						}

						result += _$_$value$_$_;
					}
					else
						result += eval('(' + _$_$code$_$_ + ')', context);
				}

				i = exprBegin + termination + end.length - 1;
				continue;
			}

			result += format.charAt(i);
		}

		return result;
	}
});

Template.implement(new Options);

Template.getExpressionLength = function(code, end) {
	var brackets = {
		'(': ')',
		'{': '}',
		'[': ']'
	};

	var quotations = ['"', "'"];

	var stack = [];

	for(var i = 0; i < code.length; ++i) {
		if(stack.length < 1 && code.substr(i, end.length) == end)
			return i;

		var c = code.charAt(i);

		if(quotations.indexOf(stack.getLast()) < 1) {
			if(quotations.indexOf(c) >= 0 || $defined(brackets[c]))
				stack.push(c);
			else if(brackets[stack.getLast()] === c)
				stack.pop();
		}
		else if(c === '\\') {
			++i;
			continue;
		}
		else if(c === stack.getLast())
			stack.pop();
	}

	return false;
};

/*
Class: String
	<String> object prototype methods for <Template>.
*/

String.extend({

	/*
	Property: interpolate
		Interpolate string.

	Arguments:
		context - (object) Context object.
		options - (object) [optional] See <Template> for acceptable options.
	
	Returns:
		(string) Interpolated string.
	*/

	interpolate: function(context, options) {
		return new Template(this, options).evaluate(context);
	}
});


