Source: mvc/parser/templateRenderUtils.js


define (['mmirf/commonUtils','mmirf/languageManager','mmirf/controllerManager','mmirf/presentationManager','mmirf/parserModule','mmirf/viewConstants',
	'mmirf/logger', 'module'
	],

	/**
	 * A Utility class for rendering parsed (eHTML) templates, or more specifically ParsingResult objects.<br>
	 *
	 * @example mmir.parser.RenderUtils.render(parseResult, contentElementList);
	 *
	 * @class RenderUtils
	 * @name mmir.parser.RenderUtils
	 * @export RenderUtils as mmir.parser.RenderUtils
	 * @public
	 * @static
	 * @hideconstructor
	 *
	 */
	function (
		commonUtils, languageManager, controllerManager, presentationManager, parser, ViewConstants,
		Logger, module
) {

		/**
		 * Object containing the instance of the class RenderUtils
		 *
		 * @type RenderUtils
		 *
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var instance = null;

		/**
		 * the logger for the RenderUtils
		 *
		 * @type mmir.tools.Logger
		 *
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var logger = Logger.create(module);

	//internal "constants" for the RENDERING mode
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_LAYOUT 			= 0;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_PARTIAL 		= 2;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_VIEW_CONTENT 	= 4;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_VIEW_DIALOGS 	= 8;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_JS_SOURCE       = 16;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var RENDER_MODE_JS_SOURCE_FORCE_VAR_PREFIX = 32;

		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var DATA_NAME       = parser.element.DATA_NAME;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var PARAM_DATA_NAME = parser.element.DATA_ARGUMENT_NAME;
		/**
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var PARAM_ARGS_NAME = parser.element.ARGUMENT_ARGUMENT_NAME;


		/**
		 * HELPER for detecting if an object is an Array
		 *
		 * @function
		 *
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 *
		 * @see mmir.CommonUtils#isArray
		 */
		var isArray = commonUtils.isArray;

		/**
		 * helper for sorting an Arrays.
		 *
		 * Notes:
		 * 1. all array elements must have a function {Number} getStart()
		 * 2. the array will be sorted ascending by getStart(), e.g. sort by occurrence in the raw template-text
		 *
		 * Usage example:
		 * <code>
		 * theArray.sort(sortAscByStart);
		 * </code>
		 *
		 * @private
		 * @memberOf mmir.parser.RenderUtils#
		 */
		var sortAscByStart=function(parsedElem1, parsedElem2){
			return parsedElem1.getStart() - parsedElem2.getStart();
		};

		/**
		 * Constructor-Method of Singleton mmir.parser.RenderUtils
		 *
		 * @private
		 * @ignore
		 *
		 * @memberOf mmir.parser.RenderUtils#
		 */
		function constructor(){
		//private members.

			/**
			 * @type mmir.LanguageManager
			 * @name localizer
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			var localizer = languageManager;

			/**
			 * Prepares the layout:
			 *
			 * after loading a layout file, this methods prepares the layout
			 * for rendering content into it
			 * (i.e. "prepare layout definition for later view-renderings").
			 *
			 *
			 * NOTE: this does not actually render the layout for "viewing"
			 *       (see renderContent(..))!
			 *
			 * @param {ParsingResult} result the parsing result for the layout string
			 * @param {Array<mmir.view.ContentElement>} contentForArray (usually this would be NULL for pre-rendering layouts)
			 * @param {Number} renderingMode the rendering mode for layouts
			 * @returns {String} the (pre-) rendered layout
			 *
			 *
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 * @name renderLayoutImpl
			 */
			function renderLayout(result, contentForArray, renderingMode) {

				//TODO need to enable dynamic elements for this LAYOUT-rendering
				//     (e.g. for 'calculating' variables that can be used as @yield-arguments ... should vars for this be disabled?)

				//create list of all template-expressions
				var all = result.scripts.concat(
					result.styles//[DISABLED: only process script- and style-tags at this stage], result.yields, result.localizations
				);
				//sort list by occurrence:
				all.sort(sortAscByStart);

				var renderResult = new Array();

				var pos = 1;
				for(var i=0, size = all.length; i < size; ++i){

					var scriptElem = all[i];
					//render the "static" content, beginning from the
					//	lastly rendered "dynamic" element up to the start
					//  of the current "dynamic" element:
					renderResult.push(result.rawTemplateText.substring(pos-1, scriptElem.getStart()));

					//render the current "dynamic" element:
					renderElement(scriptElem, contentForArray, renderingMode, result.rawTemplateText, renderResult);

					//set position-marker for "static" content after entry position
					// of current "dynamic" element:
					pos = scriptElem.getEnd() + 1;

					//alert('Replacing \n"'+rawTemplateText.substring(scriptElem.getStart(), scriptElem.getEnd())+'" with \n"'+content+'"');
				}

				if(pos - 1 < result.rawTemplateText.length){
					renderResult.push(result.rawTemplateText.substring(pos - 1));
				}

				return renderResult.join('');
			}

			/**
			 * Prepares JavaScript source code for usage in rendering the template (view/partial etc.).
			 *
			 * The replacement-list contains information which parts of the raw JavaScript code should be
			 * modified (e.g. indices [start,end] for replacing text in the source code).
			 *
			 * The function returns the modified JavaScript source code as a String.
			 *
			 *
			 * If the mode is <code>RENDER_MODE_JS_SOURCE_FORCE_VAR_PREFIX</code>, the variable-names that correspond
			 * to replacementObjectsList are check: if a name does not start with @, then the name will prepended with @ before
			 * rendering.
			 *
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 * @name renderJSSourceImpl
			 */
			function renderJSSource(rawJSSourceCode, replacementObjectsList, renderingMode) {

				if(!replacementObjectsList || replacementObjectsList.length < 1){
					return rawJSSourceCode; //////////////////////////// EARLY EXIT //////////////////////////
				}

				var all = replacementObjectsList;
				//sort list by occurrence:
				all.sort(sortAscByStart);

				var renderResult = new Array();

				var pos = 1;
				for(var i=0, size = all.length; i < size; ++i){

					var scriptElem = all[i];
					//render the "static" content, beginning from the
					//	lastly rendered "dynamic" element up to the start
					//  of the current "dynamic" element:
					renderResult.push(rawJSSourceCode.substring(pos-1, scriptElem.getStart()));

					//render the current "dynamic" element:
					renderElement(scriptElem, null, renderingMode, rawJSSourceCode, renderResult);

					//set position-marker for "static" content after entry position
					// of current "dynamic" element:
					pos = scriptElem.getEnd() + 1;

					//alert('Replacing \n"'+rawTemplateText.substring(scriptElem.getStart(), scriptElem.getEnd())+'" with \n"'+content+'"');
				}

				if(pos - 1 < rawJSSourceCode.length){
					renderResult.push(rawJSSourceCode.substring(pos - 1));
				}

				return renderResult.join('');
			}

			/**
			 * Render a View
			 *
			 * Renders the contents into a layout definition (i.e. "render for viewing").
			 *
			 * @param {String} htmlContentString the "raw" content string that was parsed
			 * @param {Array<mmir.view.YieldDeclaration>} yieldDeclarationsArray a list of yield-declarations for the parsed htmlContentString
			 * @param {Array<mmir.view.ContentElement>} contentForObjectsArray a list of content-for objects for the parsed htmlContentString. This list must supply a corresponding object for each entry in the <tt>yieldDeclarationsArray</tt>.
			 * @param {Number} renderingMode the render mode
			 * @param {Object} data the rendering data
			 * @returns {String} the evaluated and rendered view-content
			 *
			 *
			 * @private
			 * @function
			 * @memberOf mmir.parser.RenderUtils#
			 * @name renderContentImpl
			 */
			function renderContent(htmlContentString, yieldDeclarationsArray, contentForArray, renderingMode, data) {

				yieldDeclarationsArray.sort(sortAscByStart);

				var renderResult = new Array();

				var pos = 1;
				for(var i=0, size = yieldDeclarationsArray.length; i < size; ++i){

					var yieldDeclaration = yieldDeclarationsArray[i];

					if(
							(renderingMode === RENDER_MODE_VIEW_CONTENT && yieldDeclaration.getAreaType() !== ViewConstants.CONTENT_AREA_BODY)
						||	(renderingMode === RENDER_MODE_VIEW_DIALOGS && yieldDeclaration.getAreaType() !== ViewConstants.CONTENT_AREA_DIALOGS)
					){
						continue;
					}

					//render the "static" content, beginning from the
					//	lastly rendered "dynamic" element up to the start
					//  of the current "dynamic" element:
					renderResult.push(htmlContentString.substring(pos-1, yieldDeclaration.getStart()));

					//render the current "dynamic" element:
					renderYield(yieldDeclaration, contentForArray, renderingMode, htmlContentString, renderResult, data);

					//set position-marker for "static" content after entry position
					// of current "dynamic" element:
					pos = yieldDeclaration.getEnd() + 1;

				}

				if(pos - 1 < htmlContentString.length){
					renderResult.push(htmlContentString.substring(pos - 1));
				}

				return renderResult.join('');
			}

			/**
			 * Renders a ContentElement object into the renderingBuffer.
			 *
			 *
			 * @param {mmir.view.ContentElement} contentElement
			 * 						the ContentElement object that should be rendered
			 * @param {Array} renderingBuffer
			 * 						of Strings (if <code>null</code> a new buffer will be created)
			 * @param {Object} data
			 * 						the data/arguments/variables object;
			 * 						the event data with which the rendering was invoked is accessible via <DATA_NAME>[<PARAM_DATA_NAME>]
			 *
			 * @param {Array<mmir.view.ContentElement>} [contentForObjectsArray] OPTIONAL
			 * 						for rendering layouts, i.e. when YieldDeclarations are contained in the contentElement.allContentElements fields:
			 * 						a list of content-for objects for the parsed htmlContentString. This list must supply a corresponding object for each entry in the <tt>yieldDeclarationsArray</tt>.
			 * @returns {Array}
			 * 						a list of Strings the renderingBuffer where the contents of this object are added at the end of the Array
			 *
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 * @name renderContentElementImpl
			 */
			function renderContentElement(contentElement, renderingBuffer, data, contentForObjectsArray){

				//create "buffer" if necessary:
				var renderResult = getRenderingBuffer(renderingBuffer);

				//initialize the contentElement with the current rendering-data object:
				contentElement.setRenderData(data);

				contentForObjectsArray = contentForObjectsArray || null;

				var pos = 1;
				//iterate over elements, and render them into the "buffer":
				for(var i=0, size = contentElement.allContentElements.length; i < size; ++i){

					var childContentElement = contentElement.allContentElements[i];

					//render the "static" content, beginning from the
					//	lastly rendered "dynamic" element up to the start
					//  of the current "dynamic" element:
					renderResult.push(contentElement.definition.substring(pos-1, childContentElement.getStart()));

					if(!contentForObjectsArray && childContentElement.isYield()){

						logger.e('encountered mmir.view.YieldDeclaration, but now ContentFor list was supplied');

					} else {

						//render the current "dynamic" element:
						renderElement(
								childContentElement,
								contentForObjectsArray,//<- contentForArray: must be specified, if not used (only for LAYOUTS)
								RENDER_MODE_VIEW_CONTENT,//<- renderingMode: render as normal view (i.e. generate all replacements)
								contentElement.getRawText(),
								renderResult,
								data,
								contentElement
						);
					}

					//set position-marker for "static" content after entry position
					// of current "dynamic" element:
					pos = childContentElement.getEnd() + 1;

					//alert('Replacing \n"'+rawTemplateText.substring(childContentElement.getStart(), childContentElement.getEnd())+'" with \n"'+content+'"');
				}

				//append the last part, i.e. if there is some template-text after the last element:
				if(pos - 1 < contentElement.definition.length){
					if(pos === 1){
						renderResult.push(contentElement.definition);
					}
					else {
						renderResult.push(contentElement.definition.substring(pos-1));
					}
				}

				return renderResult;
			}

			/**
			 * HELPER creates a new rendering buffer if neccessary
			 * @returns {Array} rendering buffer
			 *
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function getRenderingBuffer(renderingBuffer){
			if(renderingBuffer)// && isArray(renderingBuffer))
					return renderingBuffer;

				return new Array();
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderElement(elem, contentForArray, renderingMode, rawTemplateText, renderingBuffer, data, /*optional: */ containingContentElement) {
				var type = elem.type;
				if(type === parser.element.INCLUDE_SCRIPT){
					return renderIncludeScript(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.INCLUDE_STYLE){
					return renderIncludeStyle(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.LOCALIZE){
					return renderLocalize(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.YIELD_DECLARATION){
					return renderYield(elem, contentForArray, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.ESCAPE_ENTER){
					return renderEscape(elem, renderingMode, rawTemplateText, renderingBuffer);
				}
				else if(type === parser.element.ESCAPE_EXIT){
					return renderEscape(elem, renderingMode, rawTemplateText, renderingBuffer);
				}
				else if(type === parser.element.COMMENT){
					return renderComment(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.HELPER){
					return renderHelper(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement);
				}

				else if(type === parser.element.YIELD_CONTENT){
					//ignore: this should not be rendered itself, but instead its content should be rendered
					//        in for the corresponding yield-declaration element.
					logger.warn('ParseUtil.renderElement: encountered YIELD_CONTENT for '+elem.name+' -> this sould be handled by renderYieldDeclaration!');
				}

				else if(type === parser.element.IF){
					return renderIf(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement);
				}

				else if(type === parser.element.FOR){
					return renderFor(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement);
				}

				else if(type === parser.element.RENDER){
					return renderPartial(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement);
				}

				else if(type === parser.element.BLOCK){
					return renderScriptBlock(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.STATEMENT){
					return renderScriptStatement(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.VAR_DECLARATION){
					return renderVarDeclaration(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else if(type === parser.element.VAR_REFERENCE){
					return renderVarReference(elem, renderingMode, rawTemplateText, renderingBuffer, data);
				}
				else {
					logger.error('ParseUtil.renderElement: unknown element type -> '+type);
					return null;
				}
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer){
				renderingBuffer = getRenderingBuffer(renderingBuffer);
				renderingBuffer.push(
						rawTemplateText.substring(elem.getStart(), elem.getEnd())
				);
				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderIncludeScript(elem, renderingMode, rawTemplateText, renderingBuffer, data){
				renderingBuffer = getRenderingBuffer(renderingBuffer);

				renderingBuffer.push('<script type="text/javascript" charset="utf-8" src="');
				renderingBuffer.push( elem.getValue(elem.scriptPath, elem.scriptPathType, data) );
				renderingBuffer.push('.js"></script>');

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderIncludeStyle(elem, renderingMode, rawTemplateText, renderingBuffer, data){
				renderingBuffer = getRenderingBuffer(renderingBuffer);

				renderingBuffer.push('<link rel="stylesheet" type="text/css" href="');
				if(parser.stylesRoot){
					renderingBuffer.push(parser.stylesRoot);
				}
				renderingBuffer.push( elem.getValue(elem.stylePath, elem.stylePathType, data) );
				renderingBuffer.push('.css" />');

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderLocalize(elem, renderingMode, rawTemplateText, renderingBuffer, data){
				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var name = elem.getValue(elem.name, elem.nameType, data);
				var text = localizer.getText(name);
				if(!text){
					logger.warn('RenderUtils.renderLocalize: could not find localization text for "'+elem.name+'"');
				}
				else{
					renderingBuffer.push(text);
				}

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderHelper(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var name = elem.getValue(elem.helper, elem.helperType, data);

				//set arguments, if helper-statement was given a data-argument:
				var prevArgs = null;
				if(typeof elem.argsEval !== 'undefined'){
					//TODO handle scope & collisions more elaborately?
					if(typeof data[PARAM_ARGS_NAME] !== 'undefined'){
						prevArgs = data[PARAM_ARGS_NAME];
					}
					data[PARAM_ARGS_NAME] = elem.argsEval(data);
				}

				var text = containingContentElement.getController().performHelper(name, data[PARAM_DATA_NAME], data[PARAM_ARGS_NAME]);

				//clean-up: handle scope for ARGS
				delete data[PARAM_ARGS_NAME];
				if(prevArgs !== null){
					data[PARAM_ARGS_NAME] = prevArgs;
				}

				if(typeof text !== 'string'){
					logger.debug('RenderUtils.renderHelper: not a STRING result for '+containingContentElement.getController().getName()+'::Helper.'+name+'(), but '+(typeof text));
					text = text === null || typeof text === 'undefined'? '' + text : text.toString();
				}

				//TODO HTML escape for toString before pushing the result (?)
				renderingBuffer.push(text);

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderPartial(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var partialName = elem.getValue(elem.partial, elem.partialType, data);

				//set arguments, if render-statement was given a data-argument:
				var prevArgs = null;
				if(typeof elem.argsEval !== 'undefined'){
					//TODO handle scope & collisions more elaborately?
					if(typeof data[PARAM_ARGS_NAME] !== 'undefined'){
						prevArgs = data[PARAM_ARGS_NAME];
					}
					data[PARAM_ARGS_NAME] = elem.argsEval(data);
				}

				//get the Controller object:
				var ctrlName = elem.getValue(elem.controller, elem.controllerType, data);
				var ctrl;
				//check if we already have the controller:
				if(containingContentElement.getController() && containingContentElement.getController().getName() == ctrlName){
					ctrl = containingContentElement.getController();
				}
				else {
					//...if not: retrieve controller

						ctrl = controllerManager.getController(ctrlName);


				}

				//TODO (?) move getPartial-method from PresentationManager (i.e. remove dependency here)?


				//NOTE previously, there was a dependency cycle: upon loading of templateRendererUtils.js, the presentationManager was not yet loaded.
				//     This should not happen anymore, but just to be save, load the presentationManager, if it is not available yet
				if(!presentationManager){
					presentationManager = require('mmirf/presentationManager');
				}

				var partial = presentationManager.getPartial(ctrl, partialName);


				if(!partial){
					logger.warn('RenderUtils.renderPartial: no partial for controller '+containingContentElement.getController().getName()+', with name >'+partialName+'<');
				}
				else {
					renderContentElement(partial.getContentElement(), renderingBuffer, data);
				}

				//clean-up: handle scope for ARGS
				delete data[PARAM_ARGS_NAME];
				if(prevArgs !== null){
					data[PARAM_ARGS_NAME] = prevArgs;
				}

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderIf(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var evalCondResult = elem.ifEval(data);
				if(evalCondResult) {
					renderContentElement(elem.content, renderingBuffer, data);
				}
				else if(elem.elseContent) {
					renderContentElement(elem.elseContent.content, renderingBuffer, data);
				}

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderFor(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				if(elem.forControlType === 'FORITER'){
					//FOR-type: for(prop in obj)...

					//get iterator for prop-values:
					var it = elem.forIterator(data);
					var current;

					while( it.hasNext() ){
						current = it.next();

						//make the prop-name available in inner FOR-block through the data-object:
						data[elem.forPropName] = current;

						try{

							//render inner FOR-content:
							renderContentElement(elem.content, renderingBuffer, data);

						} catch(err){
							//FIXME experimental mechanism for BREAK within @for
							//      (need to add syntax for this: @break)

							//simulate BREAK statement:
							if(err == 'break'){//FIXME use internal/private element for this! (add @break to syntax?)
								break;
							}
							else {
								//if it is an error: re-throw it
								throw err;
							}
						}
					}
				}
				else {
					//FOR-type: for(var i=0; i < size; ++i)...

					elem.forInitEval(data);

					while( elem.forConditionEval(data) ){

						try{

							//render inner FOR-content:
							renderContentElement(elem.content, renderingBuffer, data);

						} catch(err){

							//simulate BREAK statement:
							if(err == 'break'){//FIXME use internal/private element for this! (add @break to syntax?)
								break;
							}
							else {
								//if it is an error: re-throw it
								throw err;
							}
						}

						//execute INCREMENT of FOR-statement:
						elem.forIncrementEval(data);
					}

				}

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderScriptBlock(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				evaluate(elem.scriptContent, data, elem, containingContentElement);

				//return unchanged renderingBuffer
				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderScriptStatement(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var result = evaluate(elem.scriptContent, data, elem, containingContentElement);
				//TODO escape rendered string (?)
				renderingBuffer.push(result);

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderVarDeclaration(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				//NOTE all template-vars start with special char @
//    			var fieldName = '@'+ elem.getValue(elem.name, elem.nameType, data);
				var varName = elem.getValue(elem.name, elem.nameType);//FIXME: do not invoke with data; we only want the VAR-name!//, data);
				var fieldName = '@'+ varName;

				//initialize field for var-declaration
				if(typeof data[fieldName] === 'undefined'){
					data[fieldName] = null;
					//TODO impl. structures/mechanisms for deleting/removing the var on exiting
					//		the scope of the corresponding ContentElement
					//		... with special case when field already existed before:
					//          (1) delete/remove only on the outer most scope exit
					//          (2) on 'inner' exits we need to restore the value that the variable had
					//              when we entered the scope (i.e. when we "overwrote" the existing var
					//              with an inner local var)
					//              --> (a) we need to store the value here when var already exists
					//                  (b) on parsing JS code we need to consider var-declarations, i.e. @-variables
					//                      that are proceeded with a 'var'-statement, e.g.: 'var @varName = ...'

				}
				// TODO handle case when field already exists (?)

				//return unchanged renderingBuffer
				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderVarReference(elem, renderingMode, rawTemplateText, renderingBuffer, data, containingContentElement){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText, renderingBuffer);
				}

				var varName = rawTemplateText.substring(elem.getStart(),elem.getEnd());

				//handle "import"/"export" of call/args-data
				if(varName === PARAM_DATA_NAME || varName === PARAM_ARGS_NAME){

					//TODO should there be a check included -> for var-existance?
					//     --> currently: on assignment, if var does not exists, it will be created
					//     --> change (?), so that there is a check first, and if var does not exists an ReferenceError is thrown (?)
					renderingBuffer.push(DATA_NAME);
					renderingBuffer.push('["');

					if(renderingMode === RENDER_MODE_JS_SOURCE_FORCE_VAR_PREFIX){
						//ensure that the replacement variable-name starts with an @:
						if( ! varName[0] === '@'){
							varName = '@' + varName;
						}
					}

					//extract var-name from original source-code (NOTE this var must start with @)
					renderingBuffer.push(varName);
					renderingBuffer.push('"]');


				} else {
					//render variable (without leading @ symbol)
					renderingBuffer.push(varName[0] === '@'? varName.substring(1) : varName);
				}

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderEscape(elem, renderingMode, rawTemplateText, renderingBuffer){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText);
				}

				renderingBuffer.push( elem.text);

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderComment(elem, renderingMode, rawTemplateText, renderingBuffer){

				//render comment: omit comment text from rendering!
//	    		renderingBuffer = getRenderingBuffer(renderingBuffer);
//
//	    		if(RENDER_MODE_LAYOUT === renderingMode){
//	    			return renderRaw(elem, renderingMode, rawTemplateText);
//	    		}
//	    		var comment = rawTemplateText.substring(elem.getStart()+2,elem.getEnd()-2);
//	    		renderingBuffer.push( comment );

				return renderingBuffer;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function getContentForYield(name, contentForArray){
				for(var i=0, size = contentForArray.length; i < size; ++i){
					if(name === contentForArray[i].getName()){
						return contentForArray[i];
					}
				}
				return null;
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function renderYield(elem, contentForArray, renderingMode, rawTemplateText, renderingBuffer, data){

				renderingBuffer = getRenderingBuffer(renderingBuffer);

				if(RENDER_MODE_LAYOUT === renderingMode){
					return renderRaw(elem, renderingMode, rawTemplateText);
				}
				else {

					var name = elem.getValue(elem.name, elem.nameType, data);
					var contentFor = getContentForYield(name, contentForArray);
					if(!contentFor){
						logger.info('ParseUtil.renderYield: could not find content-definition for yield '+name);
						return renderingBuffer;
					}

					if(contentFor.hasDynamicContent()){
						return renderContentElement(contentFor, renderingBuffer, data);
					}
					else {
						renderingBuffer.push(contentFor.getRawText());
						return renderingBuffer;
					}

//		    		return contentFor.toHtml();
				}
			}

			/**
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function evaluate(evalStatement, data, element, containingContentElement){

				var result = element.scriptEval(data);

				return result;
			}

			/**
			 * HELPER for creating the data-object
			 * @private
			 * @memberOf mmir.parser.RenderUtils#
			 */
			function createInternalData(eventData){

				//create DATA object that contains (or will be filled with)
				// 1. event data (in PARAM_DATA_NAME)
				// TODO 2. arguments (for template expressions that support arguments; in ARGS_NAME)
				// 3. declared template variables (under the name of their declaration)
				// TODO 4. variables in template script-blocks/script-statements that were not declared (for avoiding global var declarations when evaluating these script fragements)
				var dataArgs = new Object();
				dataArgs[PARAM_DATA_NAME] = eventData;

				return dataArgs;
			}

			/** @lends mmir.parser.RenderUtils.prototype */
			return {
			//public members:

				/**
				 * Renders a layout in preparation for displaying content:
				 * This function should be used to preperare the layout content, so that its
				 * views can be rendered into it (needs to be done only once, after the layout is loaded).
				 *
				 * @param {mmir.parser.ParsingResult} parseResult the parsed view template
				 * @param {mmir.view.ContentElement[]} [contentForArray]
				 * @returns {String} the prepared layout content
				 *
				 * @public
				 * @memberOf mmir.parser.RenderUtils.prototype
				 */
				renderLayout: function(parseResult, contentForArray){
					return renderLayout(parseResult, contentForArray, RENDER_MODE_LAYOUT);
				},

				/**
				 * Renders a view.
				 *
				 * <p>During rendering, the view's template-expressions are evaluated, and the results rendered into
				 * the returned String.
				 *
				 * @param {String|mmir.view.ContentElement} htmlContentString
				 * 				the original view-content of the layout-template text, see {@link mmir.view.Layout#getBodyContents}
				 * 				or a ContentElement with its YieldDeclarations in its allContentElements field (by default yields are not contained in ContentElement.allContentElements)
				 * @param {Array<mmir.view.YieldDeclaration>} yieldDeclarationsArray
				 * 				a list of yield-declarations of the layout
				 * @param {Array<mmir.view.ContentElement>} contentForObjectsArray
				 * 				a list of content-for objects of the view. This list must supply a corresponding objecet for each entry in the <tt>yieldDeclarationsArray</tt>.
				 * @param {Object} [data] OPTIONAL
				 * 				a JSON object which's fields will be available during rendering/evaluation of the template expressions
				 * @returns {String} the evaluated and rendered view-content
				 *
				 * @public
				 * @memberOf mmir.parser.RenderUtils.prototype
				 */
				renderViewContent: function(htmlContentString, yieldDeclarationsArray, contentForObjectsArray, data){

					var dataArgs = createInternalData(data);

					if(typeof htmlContentString === 'string'){

						return renderContent(htmlContentString, yieldDeclarationsArray, contentForObjectsArray, RENDER_MODE_VIEW_CONTENT, dataArgs);

					} else {

						return renderContentElement(
								htmlContentString,
								null,//<- yields should be in htmlContentString.allContentElements
								dataArgs,
								contentForObjectsArray
						).join('');
					}
				},

				/**
				 * Renders a single {@link mmir.view.ContentElement} object.
				 *
				 * <p>During rendering, the view's template-expressions are evaluated, and the results rendered into
				 * the returned String.
				 *
				 * @param {mmir.view.ContentElement} contentElement the ContentElement object that should be rendered
				 * @param {Object} [data] a JSON object which's fields will be available during rendering/evaluation of the template expressions
				 * @param {Array<String>} [renderingBuffer] if provided, the partial rendering results will be appended to this Array
				 * @returns {String} the evaluated and rendered ContentElement; if <tt>renderingBuffer</tt> was provided and not empty, the result will be prepended with the concatenated contents of the Array's Strings
				 *
				 * @public
				 * @memberOf mmir.parser.RenderUtils.prototype
				 */
				renderContentElement: function(contentElement, data, renderingBuffer/*optional*/){

					var dataArgs = createInternalData(data);

					return renderContentElement(contentElement, renderingBuffer, dataArgs);
				},

				/**
				 * Renders the dialog content for a view.
				 *
				 * <p>During rendering, the view's template-expressions are evaluated, and the results rendered into
				 * the returned String.
				 *
				 * @param {String} htmlContentString the original dialog-content of the layout-template text, see {@link mmir.view.Layout#getDialogsContents}
				 * @param mmir.view.YieldDeclaration[]} yieldDeclarationsArray a list of yield-declarations of the layout
				 * @param {mmir.view.ContentElement[]} contentForObjectsArray a list of content-for objects of the view. This list must supply a corresponding objecet for each entry in the <tt>yieldDeclarationsArray</tt>.
				 * @param {Object} [data] a JSON object which's fields will be available during rendering/evaluation of the template expressions
				 * @returns {String} the evaluated and rendered dialog-content
				 *
				 * @public
				 * @memberOf mmir.parser.RenderUtils.prototype
				 */
				renderViewDialogs: function(htmlContentString, yieldDeclarationsArray, contentForObjectsArray, data){

					var dataArgs = createInternalData(data);

					return renderContent(htmlContentString, yieldDeclarationsArray, contentForObjectsArray, RENDER_MODE_VIEW_DIALOGS, dataArgs);
				},

				/**
				 * Prepares JavaScript source code for usage in rendering the template (view/partial etc.).
				 *
				 * The replacement-list contains information which parts of the raw JavaScript code should be
				 * modified (e.g. indices [start,end] for replacing text in the source code).
				 *
				 * The function returns the modified JavaScript source code as a String.
				 *
				 *
				 * If the mode is <code>isForcePrefix == true</code>, the variable-names that correspond
				 * to replacementObjectsList are check: if a name does not start with @, then the name will prepended with @ before
				 * rendering.
				 *
				 * @param {String} rawJSSourceCode the original JavaScript source code
				 * @param {mmir.parser.ParsingResult[]} replacementObjectsList
				 * @param {Boolean} [isForcePrefix]
				 *
				 * @public
				 * @memberOf mmir.parser.RenderUtils.prototype
				 */
				renderJS: function(rawJSSourceCode, replacementObjectsList, isForcePrefix){
					var mode = isForcePrefix? RENDER_MODE_JS_SOURCE_FORCE_VAR_PREFIX : RENDER_MODE_JS_SOURCE;
					return renderJSSource(rawJSSourceCode, replacementObjectsList, mode);
				}

			};//END: return{}

		}//END: constructor()

		instance = new constructor();

		//FIXME should the renderer be exported to parser.RenderUtils here?
		parser.RenderUtils = instance;

		return instance;
	});//END: define(..., function(){

//}( this.mmir = this.mmir || {} ));