/*
 * Copyright Joachim Zobel
 *
 * You can consider this MIT style licensed. Do with it as you like, 
 * but don't blame me for anything evil that this code does and 
 * do not remove my name.
 *
 */


/*
 * function:	TALprocess
 * description: Fills a tal template using recursion
 * parameters:	oTmpl  - the template root node
 *				oData  - the data to fill in
 */

function TALprocess(oTmpl, oData) {
	var	bRecurse	= true;
	if (oTmpl.getAttributeNode) {
		var	sError	= null;	
		if (oTmpl.getAttributeNode(addNsp("repeat"))) {
			sError		= TALrepeat(oTmpl, oData)
			//	In this case, TALrepeat has already
			//	taken care for recursion.
			bRecurse	= false;
		}
		if (oTmpl.getAttributeNode(addNsp("condition"))) {
			sError		= TALcondition(oTmpl, oData)
		}
		if (oTmpl.getAttributeNode(addNsp("content"))) {
			sError		= TALcontent(oTmpl, oData);
			bRecurse	= false;
		}
		if (oTmpl.getAttributeNode(addNsp("replace"))) {
			sError		= TALreplace(oTmpl, oData)
			bRecurse	= false;
		}
		if (oTmpl.getAttributeNode(addNsp("attributes"))) {
			sError		= TALattributes(oTmpl, oData)
			//	In this case, we do recurse
		}
		if (oTmpl.getAttributeNode(addNsp("omit-tag"))) {
			sError		= TALomit_tag(oTmpl, oData)
			//	In this case, we do recurse
		}

		//	Error handling
		if (	sError
			&&	handleError(sError)) {
			return;
		}

	}

	if (bRecurse) {
		//	recursion
		for (var i=0; i<oTmpl.childNodes.length; i++) {
			TALprocess(oTmpl.childNodes[i], oData);
		}
	}
}

/***********************************************************************
 * TAL tag processors
***********************************************************************/

/*
 * function:	TALcontent
 */

function TALcontent(oTmpl, oData) {
	var sText	= entryByPath(oData, 
							oTmpl.getAttribute(addNsp("content")));
	if (typeof sText == "undefined") {
		return "No data found for "+oTmpl.getAttribute(addNsp("content"))+".";
	}
	var oTextNode = document.createTextNode(sText);

	oTmpl.replaceChild(oTextNode, oTmpl.firstChild);
	oTmpl.removeAttributeNode(oTmpl.getAttributeNode(addNsp("content")));

	return null;
}

/*
 * function:	TALreplace
 */

function TALreplace(oTmpl, oData) {
	var sText	= entryByPath(oData, 
							oTmpl.getAttribute(addNsp("replace")));
	if (typeof sText == "undefined") {
		return "No data found for "+oTmpl.getAttribute(addNsp("replace"))+".";
	}
	var oTextNode = document.createTextNode(sText);
	
	var	oParent	= oTmpl.parentNode;
	oParent.replaceChild(oTextNode, oTmpl);

	return null;
}

/*
 * function:	TALomit_tag
 */

function TALomit_tag(oTmpl, oData) {
	var sPath	= oTmpl.getAttribute(addNsp("omit-tag"));
	var	sCond	= entryByPath(oData, sPath);

	oTmpl.removeAttributeNode(oTmpl.getAttributeNode(addNsp("omit-tag")));

	if (isTrue(sCond)) {
		var	oParent	= oTmpl.parentNode;
		var	oFirstChild	= oTmpl.firstChild;

		//	rememer the first child
		var	oFirstChild	= oTmpl.firstChild;

		//	Move the child nodes before the tag
		while (oTmpl.hasChildNodes()) {
			var	oCurrent	= oTmpl.firstChild;
			//	On firefox, the remove is done
			//	implicitely by insertBefore.
			oTmpl.removeChild(oCurrent);
			oParent.insertBefore(oCurrent, oTmpl);
			
		}
		//	remove tag
		oParent.removeChild(oTmpl);
		
		oTmpl	= oFirstChild;
	}

	return null;
}

/*
 * function:	TALattributes
 */

function TALattributes(oTmpl, oData) {
	var sAttrs	= oTmpl.getAttribute(addNsp("attributes"));
	var	aAttrs	= sAttrs.split(/;/);
	
	var sAttr	= null;
	for (;sAttr = aAttrs.shift();) { 
		var aNmX	= getNameExpr(sAttr);
		var oVal	= entryByPath(oData, aNmX[1]);
		if (typeof oVal == "undefined"
			||	oVal == null) {
			//	remove attribute
			oTmpl.removeAttributeNode(oTmpl.getAttributeNode(aNmX[0]));
		}
		else {
			oTmpl.setAttribute(aNmX[0], oVal); 
		}
	}

	oTmpl.removeAttributeNode(oTmpl.getAttributeNode(addNsp("attributes")));

	return null;
}

/*
 * function:	TALrepeat
 */

function TALrepeat(oTmpl, oData) {
	var sRep	= oTmpl.getAttribute(addNsp("repeat"));
	var aNmX	= getNameExpr(sRep);

	var	aDatas	= entryByPath(oData, aNmX[1]);
	if (typeof aDatas.length == "undefined") {
		return aNmX[1]+" is not an Array.";	
	}

	var	oParent	= oTmpl.parentNode;
	//	The repeat attribute is no longer needed
	oTmpl.removeAttributeNode(oTmpl.getAttributeNode(addNsp("repeat")));
	//	oTmpl wird als Vorlage verwendet
	for (var i=0; i<aDatas.length; i++) {
		var	oLocData		= new Object();
		oLocData[aNmX[0]]	= aDatas[i];

		var oNew	= oTmpl.cloneNode(true);
		oParent.appendChild(oNew);

		//	recurse
		TALprocess(oNew, oLocData);
	}

	//	Die Vorlage wird entfernt
	oParent.removeChild(oTmpl);

	return null;
}

/*
 * function:	TALcondition
 */

function TALcondition(oTmpl, oData) {
	var sPath	= oTmpl.getAttribute(addNsp("condition"));
	var	sCond	= entryByPath(oData, sPath)
	
	//	remove tal attribute
	oTmpl.removeAttributeNode(oTmpl.getAttributeNode(addNsp("condition")));

	if (!isTrue(sCond)) {
		oTmpl.parentNode.removeChild(oTmpl);
	}

	return null;
}

/***********************************************************************
 * Helpers
***********************************************************************/

/*
 * function:	entryByPath
 * parameters:	oObj   - an nested Object
 *				sPath  - a list of property names seperated by /
 *				oCreate - (opt.) Create everything along the path 
 *						  and put this at the end
 * return:      the property obtained by following the path
 */

function entryByPath(oObj, sPath, oCreate) {
	var	aPath	= sPath.split("/");
	var	oRtn	= oObj;
	for (var i=0; i < aPath.length; i++) {
		if (	typeof oRtn[aPath[i]] == "undefined"
				//	oCreate overwrites
			||	oCreate && (i==aPath.length-1)) {
			if (oCreate) {
				if (i==aPath.length-1) {
					//	Put oCreate at the end
					oRtn[aPath[i]]	= oCreate;
				}
				else {
					//	Just another node along the path 
					oRtn[aPath[i]]	= new Object();
				}
			}
			else {
				return null;
			}
		}

		oRtn	= oRtn[aPath[i]];
	}
	return oRtn;
}

/*
 * function:	handleError
 * description: Simply does an alert at the moment
 * parameters:	sError  - the template root node
 * return: true - fatal error
 */
function handleError(sError) {
	alert(sError);
}

/*
 * function:	getNameExpr
 * description: splits a string at the first blank
 * parameters:	sNX  - will be split
 * return:		the two parts as an array
 */

function getNameExpr(sNX) {
	var aNX		= sNX.split(/ /);
	var sFirst	= ""; 
	//	ltrim
	while (sFirst == "") {
		sFirst	= aNX.shift();
	}
	return [sFirst, aNX.join(" ")];
}

/*
 * function:	addNsp
 * description: adds the namespace to a tag
 * parameters:	sTag  - the tag
 * return: the tag prefixed with the namespace
 */
function getNsp() {
	return "domtal";
}

/*
 * function:	addNsp
 * description: adds the namespace to a tag
 * parameters:	sTag  - the tag
 * return: the tag prefixed with the namespace
 */
function addNsp(sTag) {
	return getNsp()+":"+sTag;
}
/*
 * function:	isTrue
 */
function isTrue(sCond) {
	return sCond && (sCond != "")
}

/*
 * function:	TALextract
 * description: Extracts the data structure from the document.
 * parameters:	oTmpl - the (current) template node
 *				oData - the Data
 */

function TALextract(oTmpl, oData)
{
	for (var i=0; i<oTmpl.attributes.length; i++) {
		var	oAttr	= oTmpl.attributes[i];
		var aSTag	= oAttr.nodeName.split(":");
		//	Check if it is a TAL attribute
		if (aSTag.length>=2 && aSTag[0] == getNsp()) {
			switch(aSTag[1]) {
				case "content":
				{
					entryByPath(oData, oAttr.nodeValue);
					break;
				}
			}
		}
	}
	
	for (var i=0; i<oTmpl.childNodes.length; i++) {
		TALextract(oTmpl.childNodes[i], oData);
	}
}

