/*  TeenyTemplate for JavaScript, version 0.8.1
 *  (c) 2007 Sean Heber (sean@fifthace.com)
 *  TeenyTemplate is freely distributable under the terms of an MIT-style license.
/*---------------------------------------------------------------------------------*/

/* CHANGES:
 * 0.8:
 *  - total rewrite + added support for tabs, panels, children + changed usage pattern
 * 0.8.1:
 *  - fixed a sly bug when using a property value of 0 or a blank string
 *  - added empty method
**/

var $TT = TeenyTemplate;
function TeenyTemplate( template, properties ) {
	function $(e) { return (typeof e == 'string')? document.getElementById(e): e; }
	function removeChildren(c) { c=$(c); while( c.firstChild ) c.removeChild( c.firstChild ); return c; }
	function classNames(e) { return (typeof e.className == 'string')? e.className.split( /\s+/ ): []; }
	function classMatch(classes,pat) { var matched = false; foreach( classes, function(c) { if( !matched ) matched = null != pat.exec(c); } ); return matched }
	function get( from, what, fail ) { return what? from[what]: (fail? from: null); }
	function foreach( a, f, clone ) {
		var z = clone? new Array(): a;
		if( clone ) { for( var i=0; i<a.length; i++ ) z[i] = a[i]; }
		for( var i=0; i<z.length; i++ ) f(z[i]);
	}
	function Template( element ) {
		var self = this;
		var childTemplates = {};
		var panelTemplates = {};
		var tabTemplates = {};
		var namedElements = {};
		var values = {};

		this.get = function(property)	{ return get(values,property,true); }
		this.set = function(properties)	{ if( properties ) { for( var p in properties ) values[p] = properties[p]; } render(); return self; }
		this.clear = function(property)	{ if( typeof property == 'string' ) delete values[property]; else foreach(property,self.clear); render(); return self; }
		this.empty = function()		{ values = {}; render(); return self; }
		this.element = function(name)	{ return get(namedElements,name); }
		this.panel = function(name)	{ return get(panelTemplates,name); }
		this.hidePanels = function()	{ for( var t in panelTemplates ) panelTemplates[t].show(false); return self; }
		this.tab = function(name)	{ return get(tabTemplates,name); }
		this.show = function(yes)	{ element.style.display = yes? '': 'none'; return self; }
		this.visible = function(yes)	{ element.style.visibility = yes? 'visible': 'hidden'; return self; }
		this.focusTab = function(name)	{ self.hideTabs(); return self.tab(name).show(true); }
		this.hideTabs = function()	{ for( var t in tabTemplates ) tabTemplates[t].show(false); return self; }
		this.appendTo = function( parent, properties ){
			var node = element.cloneNode(true);
			parent.appendChild(node);
			return (new Template(node).__parse_children().set(properties));
		}
		this.assignTo = function( e, v ) { self.appendTo( removeChildren(e), v ); return self; }
		this.appendChild = function( c, s ) { return childTemplates[c].template.appendTo( childTemplates[c].parent, s ); }
		this.appendChildren = function( c, a ) {
			var results = [];
			foreach( a, function(z) { results.push( self.appendChild(c,z) ); } );
			return results;
		}
		this.removeAllChildren = function() {
			for( var c in childTemplates )
				removeChildren( childTemplates[c].parent );
			return self;
		}
		this.removeSiblingsOf = function( c ) { removeChildren( childTemplates[c].parent ); return self; }
		this.replaceSiblingsWithChildren = function( c, a ) {
			self.removeSiblingsOf( c );
			return self.appendChildren( c, a );
		}
		this.replaceSiblingsWithChild = function( c, s ) {
			self.removeSiblingsOf( c );
			return self.appendChild( c, s );
		}
		this.remove = function() { if(element.parentNode) element.parentNode.removeChild(element); return self; }
		this.template = element;

		this.__parse_children = function() {
			function findChildren( elements ) {
				foreach( elements, function(e) {
					var children = [];
					var panel;
					var tab;
					foreach( classNames(e), function(c) {
						var m = (/^(child|panel|tab)_(.*)$/).exec(c);
						if( m ) switch( m[1] ) {
							case 'child':	children.push(m[2]); break;
							case 'panel':	panel = m[2]; break;
							case 'tab':	tab = m[2]; break;
						}
					});
					if( panel ) {
						panelTemplates[panel] = (new TeenyTemplate(e)).show(false);
					} else if( tab ) {
						var t = (new TeenyTemplate(e)).show(false);
						t.focus = function() { self.focusTab( tab ); return t; }
						tabTemplates[tab] = t;
					} else if( children.length ) {
						foreach( children, function(c) {
							childTemplates[c] = {
								parent: e.parentNode,
								template: new Template(e.cloneNode(true))
							};
						});
						e.parentNode.removeChild( e );
					} else {
						findChildren( e.childNodes );
					}
				}, true /* clone array first: IE feature/bug */ );
			}
			findChildren( element.childNodes );
			return self;
		}
		function render() {
			namedElements = {};
			process( element, true );
		}
		function process( e, isparent ) {
			apply( e, isparent );
			foreach( e.childNodes, function(c) { process(c,false); } );
		}
		function apply( e, isparent ) {
			var classes = classNames( e );
			if( !classes.length || (!isparent && classMatch(classes,/^(child|panel|tab)_/)) ) return;
			var attribute_re = /(.*?)_(is|are)_(.*)/;
			foreach( classes, function(c) {
				var m = attribute_re.exec( c );
				if( m && m[1] && m[3] ) {
					var value = values[m[3]];
					var props = m[1].split( /_+/ );
					foreach( props, function(p) {
						function setEvent( e, p, value ) {
							var js = ['submit','scroll','mouseover','mouseout','selectstart']
							for( var j=0; j<js.length; j++ ) {
								if( p == js[j] || p == 'on'+js[j] ) {
									e['on'+js[j]] = func(value,e);
									return true;
								}
							}
							return false;
						}
						switch( p ) {
							case 'body':
								e.innerHTML = value? value: '';
								break;
							case 'text': {
								var n = removeChildren(e), nl = false;
								if(typeof(value) != 'undefined') foreach( new String(value).split(/\n/), function(l) { if(nl) n.appendChild(document.createElement('br')); n.appendChild( document.createTextNode(l) ); nl=true; });
								} break;
							case 'link': case 'onlink':
							case 'click': case 'onclick':
								e.onclick = function() { return true; }
								if( typeof value == 'string' ) {
									if( e.tagName == 'A' ) e.href = value;
									else e.onclick = function() { top.location = value; return false; }
								} else {
									e.onclick = func(value,e);
								}
								break;
							case 'id':
								namedElements[m[3]] = e;
								break;
							default:
								if( value != undefined ) { if( !setEvent(e, p, value) ) e.setAttribute( p, value ); }
						}
					});
				}
			});
		}
		function func( v, el ) {
			return (!v || typeof v != 'function')? function(e) { return false; }: function(e) { var r = v(e, self, el); return r? r: false; }
		}
	}
	return (new Template($(template))).__parse_children().set(properties);
}

