Source: env/view/viewLoader.js

define([
	'mmirf/layout', 'mmirf/view', 'mmirf/partial'
	, 'mmirf/util/deferredWithState', 'mmirf/util/loadFile', 'mmirf/util/forEach'
	, 'mmirf/configurationManager', 'mmirf/checksumUtils', 'mmirf/controllerManager', 'mmirf/resources', 'mmirf/core', 'mmirf/commonUtils'
	, 'mmirf/logger', 'module'
	, 'mmirf/parserModule'//<- loaded, but not directly used
	//,'mmirf/renderUtils' DISABLED: loaded on-demand (see loadViews())
],
/**
 * @class
 * @name ViewLoader
 * @memberOf mmir.env.view
 * @hideconstructor
 */
function(
	Layout, View, Partial,
	deferred, loadFile, forEach,
	configurationManager, checksumUtils, controllerManager, resources, core, commonUtils,
	Logger, module
	//renderUtils
){

	/**
	 * Loads views (layouts and partials):
	 * either compiled views (i.e. JS files), or raw views (i.e. eHTML files).
	 *
	 * The loader checks, if compiled views are up-to-date (using the corresponding
	 * checksum file).
	 * If a compiled view is not up-to-date (or its checksum file is missing), then
	 * the raw view will be loaded and compiled.
	 *
	 * If raw views are loaded, the parsing-module will loaded for compiling the
	 * views (i.e. if only compiled views are loaded, the dependency for the template
	 * parser and renderer is not required).
	 *
	 *
	 * <br>
	 * <strong>Configuration (configuration.json)</strong>
	 * <br>
	 *
	 * The configuration value <code>"usePrecompiledViews"</code> (Boolean) allows
	 * the determine, if views should always be compiled from the eHTML files (even
	 * if up-to-date compiled views are present).
	 *
	 * For example, configuration <code>"usePrecompiledViews": true</code> will use
	 * compiled views, while <code>"usePrecompiledViews": false</code> will always
	 * compile the eHTML files.
	 *
	 *
	 * If the configuration value for {@link mmir.PresentationManager.CONFIG_DEFAULT_LAYOUT_NAME} is
	 * set to <code>NULL</code> no default layout will be loaded.
	 *
	 * If {@link mmir.PresentationManager.CONFIG_DEFAULT_LAYOUT_NAME} is a non-empty string, then
	 * the corresponding layout will be used as default layout, instead of
	 * {@link mmir.PresentationManager.DEFAULT_LAYOUT_NAME}.
	 *
	 *
	 * @param  {PresentationManager} _instance
	 *          the instance of the PresentationManager
	 * @param  {Map<string, Layout>} _layouts
	 *          the layout collection of the PresentationManager for adding loaded layouts
	 * @param  {Map<string, View>} _views
	 *          the view collection of the PresentationManager for adding loaded views
	 * @param  {Map<string, Partial>} _partials
	 *          the partials collection of the PresentationManager for adding loaded partials
	 * @param  {Function} createViewKey
	 *          the PresentationManager's helper function for creating keys to be used when
	 *          adding views to <code>_views</code>:<br>
	 *          <code>createViewKey(ctrl: {mmir.ctrl.Controller|String}, view: {mmir.view.View|String}) : {String}</code>
	 * @param  {Function} createPartialKey
	 *          the PresentationManager's helper function for creating keys to be used when
	 *          adding partials to <code>_partials</code>:<br>
	 *          <code>createPartialKey(partial: {mmir.view.Partial|String}, view: {mmir.view.View|String}) : {String}</code>
	 * @return {Promise}
	 *          a deferred promise that gets resolved when the views (layouts, and partials) are loaded
	 *
	 * @memberOf mmir.env.view.ViewLoader
	 */
	function loadViews (
			_instance, _layouts, _views, _partials, createViewKey, createPartialKey
	) {

		/**
		 * The name of the configuration field that holds
		 * the name for the default layout.
		 *
		 * @private
		 * @type String
		 * @constant
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var CONFIG_DEFAULT_LAYOUT_NAME = _instance.CONFIG_DEFAULT_LAYOUT_NAME;

		/**
		 * Name for the default layout, that will be loaded.
		 *
		 * If NULL, no default layout will be loaded
		 * (see below configurationManager.get(CONFIG_DEFAULT_LAYOUT_NAME...))
		 *
		 * @private
		 * @type String
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var defaultLayoutName = _instance.DEFAULT_LAYOUT_NAME;


		/**
		 * The logger for the PresentationManager.
		 *
		 * @private
		 * @type mmir.tools.Logger
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var logger = Logger.create(module);//initialize with requirejs-module information

		/**
		 * Name of the configuration property that specifies whether or not to use
		 * pre-compiled views, i.e. whether to use generated JavaScript files
		 * instead of parsing & compiling the "raw" templates (eHTML files).
		 *
		 * <p>
		 * NOTE: the configuration value, that can be retrieved by querying this configuration-property
		 * 	  has is either a Boolean, or a String representation of a Boolean value:
		 * 		<code>[true|false|"true"|"false"]</code>
		 * <br>
		 * NOTE2: There may be no value set at all in the configuration for this property.
		 * 	   In this case you should assume that it was set to <code>false</code>.
		 *
		 * @type String
		 * @private
		 * @constant
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @example var isUsePrecompiledViews = mmir.conf.getBoolean("usePrecompiledViews");
		 *
		 */
		var CONFIG_PRECOMPILED_VIEWS_MODE = 'usePrecompiledViews';//TODO move this to somewhere else (collected config-vars?)? this should be a public CONSTANT...


		// determine if default-layout has a custom name (or is disabled, in it was set to null)
		var defLayoutName = configurationManager.get(CONFIG_DEFAULT_LAYOUT_NAME, void(0));
		if(typeof defLayoutName !== 'undefined'){
			defaultLayoutName = defLayoutName;
		}

		/**
		 * Checks if a pre-compiled view is up-to-date:
		 * loads the view, if it is current.
		 *
		 * If the pre-compiled view is not current, or loading-errors
		 * occur, the fail-callback will be triggered
		 * (the callback argument may contain information about the cause).
		 *
		 * @async
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @param {String} rawViewData
		 * 					the text content of the view template (i.e. content of a eHTML file "as is")
		 * @param {String} targetPath
		 * 					the path to the pre-compiled view file
		 * @param {Function} success
		 * 					callback that will be triggered, if pre-compiled view file was loaded
		 * 					NOTE: the JS code of the loaded file may not have been fully executed yet!
		 * @param {Function} fail
		 * 					callback that will be triggered, if pre-compiled view is not up-to-date or
		 * 					an error occurs while loading the file
		 */
		function loadPrecompiledView(rawViewData, targetPath, success, fail){

			//NOTE: stored template require the renderUtils:
			core.require(['mmirf/renderUtils'], function(){

				if(rawViewData){

					isUpToDate(rawViewData, targetPath, function(upToDate){

						if(!upToDate){
							if(fail) fail('Precompiled view file is outdated!');
							else logger.warn('Outdated pre-compiled view at: '+targetPath);
							//-> do not load the pre-compiled view, instead let fail-callback handle re-parsing for the view
							return;/////////////////////// EARLY EXIT /////////////////////
						}

						commonUtils.loadScript(targetPath, success, fail);
					});

				} else {
					commonUtils.loadScript(targetPath, success, fail);
				}

			});

		}

		/**
		 * Flag for determining if pre-compiled views (*.js) should be used
		 *
		 * Reads property {@link #CONFIG_PRECOMPILED_VIEWS_MODE}. If the property is not set,
		 * <code>false</code> is used by default, i.e. no pre-compiled views are used.
		 *
		 * @protected
		 * @default
		 * @type Boolean
		 * @default false: use templates files (*.ehtml) and compile them (freshly) on-the-fly
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var isUsePreCompiledViews = configurationManager.getBoolean(CONFIG_PRECOMPILED_VIEWS_MODE, false);

		/**
		 * Read the checksum file that was created when the pre-compiled view was created:
		 *
		 * it contains the view's template size (the length of its String representation) and MD5 hash.
		 *
		 * -> by calculating the viewContent's size and MD5 hash, we can determine, if it has changed
		 *    by comparing it with the data of the checksum file.
		 *
		 * @sync the checksum file is loaded in synchronous mode
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @param {String} viewContent
		 * 						the content of the view template (i.e. loaded eHTML file)
		 * @param {String} preCompiledViewPath
		 * 						the path to the corresponding pre-compiled view file
		 * @param {Function} callback
		 * 						callback that will be invoked with the result
		 * 						<pre>callback(upToDate: Boolean)</pre>
		 *
		 */
		function isUpToDate(viewContent, preCompiledViewPath, callback){

			//there is no pre-compiled view -> need to compile ehtml
			if(!preCompiledViewPath){
				return false;///////////////////// EARLY EXIT ////////////////////////
			}

			//replace file extension with the checksum-file's one: '.js' -> '.checksum.txt'
			var  viewVerificationInfoPath =
					preCompiledViewPath.substring(0, preCompiledViewPath.length - 3)
						+ checksumUtils.getFileExt();

			loadFile({
				async: true,
				dataType: "text",
				url: viewVerificationInfoPath,
				success: function onSuccess(data){

					//compare raw String to checksum-data from file
					var isCompiledViewUpToDate = checksumUtils.isSame(viewContent, data);
					callback && callback(isCompiledViewUpToDate);
				},
				error: function onError(_jqxhr, status, err){

					// print out an error message
					var errMsg = err && err.stack? err.stack : err;
					logger.error("[" + status + "] On checking up-to-date, could not load '" + viewVerificationInfoPath + "': "+errMsg); //failure

					// assume that compiled view is not up-to-date (and needs to be recompiled):
					callback && callback(false);
				}
			});
		}

		/**
		 * This function loads the layouts for every controller and puts the
		 * name of the layouts into the <b>_layouts</b> array.
		 *
		 * @function
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @returns {Promise} a deferred promise that gets resolved upon loading all layouts; fails/is rejected, if not at least 1 layout was loaded
		 */
		function loadLayouts(){
			// Load application's layouts.

			/**
			 * @type Promise
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#loadLayouts
			 */
			var defer = deferred();

			/**
			 * @type String
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#loadLayouts
			 */
			var ctrlNameList = controllerManager.getNames();

			/**
			 * HELPER object for tracking the loading-status of the layouts
			 *
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#loadLayouts
			 */
			var loadStatus = {
					loader: defer,
					remainingCtrlCount: ctrlNameList.length + 1,//+1: for the default layout
					currentLoadCount: 0,

					//additional property for keeping track on how many layouts were load overall
					// NOTE: this additional counter is necessary, since currentLoadCount
					//       keeps only track of how many controller's were checked. But since
					//       a controller may not have a layout-definition of its own, we have
					//       to use another counter to keep track of the actually loaded layouts.
					loadedLayoutsCount: 0,

					//need a custom function for checking the load status: if no layout was loaded,
					//                                                     the Derred will be rejected
					onCompletionImpl: function(status){

						//if there is a default-layout specified, but no layout was loaded -> fail
						if(status.loadedLayoutsCount < 1 && defaultLayoutName){

							//there must be at least on layout-file for the default-controller:
							status.loader.reject( 'Could not load any layout! At least one layout must be present at '
									+ resources.getLayoutPath()
									+ defaultLayoutName[0].toLowerCase() + defaultLayoutName.substring(1)
									+ '.ehtml'
							);
						}
						else {
							status.loader.resolve();
						}
					},

					//extend the status-update function: in case loading succeeded, increase the counter
					//                                   for overall loaded layouts.
					extLoadStatusFunc: function(status, hasLoadingFailed){
						if(hasLoadingFailed === true){
							//do nothing
						}
						else {
							++status.loadedLayoutsCount;
						}
					}
			};

			/**
			 * HELPER object for loading/creating the layouts
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#loadLayouts
			 */
			var createLayoutConfig = {
					constructor: Layout,
					typeName: 'Layout',
					collection: _layouts
			};

			/**
			 * helper for loading a single layout-file
			 *
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#loadLayouts
			 */
			var doLoadLayout = function(ctrlName, _index, theDefaultLayoutName){

				var layoutInfo;
				if(typeof theDefaultLayoutName === 'string'){

					ctrlName = theDefaultLayoutName;

					//create info-object for default-layout
					var layoutFileName = theDefaultLayoutName[0].toLowerCase()
											+ theDefaultLayoutName.substring(1, theDefaultLayoutName.length);
					layoutInfo = {
						name: theDefaultLayoutName,
						fileName: layoutFileName,
						genPath: resources.getCompiledLayoutPath()//TODO add compiled-path to view-info object (and read it from file-structure/JSON)
									+ layoutFileName + '.js',
						path: resources.getLayoutPath() + layoutFileName + '.ehtml'
					};

				}
				else {
					var ctrl = controllerManager.get( ctrlName );
					ctrlName = ctrl.getName();
					layoutInfo = ctrl.getLayout();
				}

				if(layoutInfo){

					doLoadTemplateFile(null, layoutInfo, createLayoutConfig, loadStatus);

				}

				--loadStatus.remainingCtrlCount;
				checkCompletion(loadStatus);

			};//END: doLoadLayout(){...

			//load the default layout:
			if(defaultLayoutName){
				doLoadLayout(null, null, defaultLayoutName);
			} else {
				logger.info('The name for the default Layout was set to "'+defaultLayoutName+'", no default Layout will be loaded!');
				--loadStatus.remainingCtrlCount;
				checkCompletion(loadStatus);
			}

			//load layouts for controllers (there may be none defined)
			forEach(ctrlNameList, doLoadLayout);

			checkCompletion(loadStatus);
			return defer;

		}//END: loadLayouts()

		/**
		 * This function actually loads the views for every controller, creates
		 * an instance of a view class and puts the view instance in the
		 * <b>_views</b> array.<br>
		 *
		 * @function
		 * @private
		 * @async
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @returns {Promise} a deferred promise that gets resolved upon loading all views
		 *
		 * @see doProcessTemplateList
		 */
		function loadViews() {

			var creatorConfig = {
					constructor: View,
					typeName: 'View',
					collection: _views,
					keyGen: createViewKey,
					accessorName: 'getViews'
			};

			return doProcessTemplateList(creatorConfig);

		}//END: loadViews()

		/**
		 * This function actually loads the partials for every controller,
		 * creates an instance of a partial class and puts the partial instance
		 * in the <b>_partials</b> array.<br>
		 * It uses a asynchronous way of loading the partials-files one after
		 * another.<br>
		 * <b>If you want to make sure, that all partials are indeed loaded,
		 * before proceeding with the subsequent instructions, you could look at
		 * the function
		 * {@link mmir.ControllerManager#foundControllersCallBack} for
		 * reference of a function which loads the files one after another - not
		 * asynchronously.</b>
		 *
		 * @function
		 * @private
		 * @async
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @returns {Promise} a deferred promise, that resolves after all partials have been loaded
		 * 					NOTE: loading failures will generate a warning message (on the console)
		 * 						  but will not cause the Promise to fail.
		 */
		function loadPartials() {

			var creatorConfig = {
					constructor: Partial,
					typeName: 'Partial',
					collection: _partials,
					keyGen: createPartialKey,
					accessorName: 'getPartials'
			};

			return doProcessTemplateList(creatorConfig);

		}//END: loadPartials()

		/**
		 * HELPER for checking the loading status.
		 *
		 * As long as the Deferred <code>status.loader</code> is
		 * still pending, the loading status will be checked:
		 *
		 * Depending on <code>status.currentLoadCount</code> and
		 * <code>status.remainingCtrlCount</code> the completion
		 * of the loading process is checked.
		 *
		 * If loading is completed, the Deferred <code>status.loader</code>
		 * will be resolved.
		 *
		 * If OPTIONAL <code>status.loader</code> (Function) exists, intead of resolving
		 * <code>status.loader</code>, this function is invoked in case of completion
		 * with <code>status</code> as argument.
		 *
		 * @private
		 * @function
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @param {PlainObject} status
		 * 		the object for managing the laoding status.
		 *
		 */
		var checkCompletion = function(status){
			if(status.loader.state() === 'pending' && status.remainingCtrlCount === 0 && status.currentLoadCount === 0){

				if(status.onCompletionImpl){
					status.onCompletionImpl(status);
				}
				else {
					status.loader.resolve();
				}

			}
		};

		/**
		 * HELPER for updating the loading status.
		 *
		 * Invokes {@link checkCompletion} with <code>status</code> as argument.
		 *
		 * @private
		 * @function
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @param {PlainObject} status
		 * 		the object for managing the laoding status:
		 * 		<code>status.currentLoadCount</code> (Integer): this property will be decreased by 1.
		 * 											This value should initially be set to the count
		 * 											of files, that will / should be loaded.
		 * 		OPTIONAL <code>status.extLoadStatusFunc</code> (Function): if this property is set, the
		 * 											function will be invoked with <code>status</code>
		 * 											and <code>hasLoadingFailed</code> as arguments.
		 *
		 * @param {Boolean} [hasLoadingFailed] OPTIONAL
		 * 		if present and <code>true</code>: this indicates that the loading process for the current
		 * 		template file (*.ehtml) has failed. NOTE that this is NOT used, when loading of a
		 * 		_compiled_ template file (*.js) fails!
		 */
		var updateLoadStatus = function(status, hasLoadingFailed){
			--status.currentLoadCount;

			if(status.extLoadStatusFunc){
				status.extLoadStatusFunc(status, hasLoadingFailed);
			}

			checkCompletion(status);
		};

		/**
		 * HELPER: creates a template-object (e.g. a View or a Partial) for the
		 *         raw template conent.
		 *
		 *         If necessary, the parser-classes (module 'mmirf/parseUtils') are loaded,
		 *         which are necessary to process the raw template content.
		 *
		 * @private
		 * @function
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @param {mmir.ctrl.Controller} controller
		 * 		the controller to which the template files belong.
		 * 		May be <code>null</code>: in this case, this argument will be omitted when
		 * 		creating the template object and creating the lookup-key (e.g. in case of a Layout).
		 *
		 * @param {String} templateName
		 * 		the name for the template (e.g. file-name without extension)
		 *
		 * @param {PlainObject} createConfig
		 * 		configuration that is used to create the template-object
		 * 		for the template-contents:
		 * 		<code>createConfig.constructor</code>: the constructor function
		 *                    IF controller IS NOT null: <code>(controller, templateName, templateContent)</code>
		 *                    							 (e.g. View, Partial)
		 *                    IF controller IS null:     <code>(templateName, templateContent)</code>
		 *                    							 (e.g. Layout)
		 *
		 * 		<code>createConfig.collection</code>: the Map to which the created
		 * 											 template-object will be added
		 * 		<code>createConfig.keyGen</code>: a generator function for creating the lookup-key when adding
		 * 										the template-object to the collection.
		 * 										This function is invoked with <code>(controller.getName(), templateName)</code>,
		 * 										that is <code>(String, String)</code>.
		 *
		 * 										NOTE: if controller IS null, the keyGen function will not be used, and
		 * 											  instead the template-object will be added with the
		 * 											  created template-object's name as lookup-key.
		 * @param {PlainObject} status
		 * 			the object for managing the loading status.
		 * 			After creating and adding the template-object to the collection, the loading
		 * 			status will be updated via {@link updateLoadStatus}
		 *
		 */
		var doParseTemplate = function(controller, templateName, config, templateContent, status){

			//NOTE need to request renderUtils here too, since it is needed during parsing!
			core.require(['mmirf/parseUtils', 'mmirf/renderUtils'], function(){

				var templateObj;
				if(controller){
					//"normal" view constructor: (Controller, nameAsString, templateAsString)
					templateObj = new config.constructor(controller, templateName , templateContent);
					config.collection.set( config.keyGen(controller.getName(), templateName), templateObj );
				}
				else {
					//in case of Layout: omit controller argument
					// -> layout constructor: (nameAsString, templateAsString)
					// -> there is a 1:1 correspondence betwenn controller and layout,
					//    and Layout.name === Controller.name
					//    => no need to create a lookup-key
					templateObj = new config.constructor(templateName , templateContent);
					config.collection.set( templateObj.getName(), templateObj );
				}

				updateLoadStatus(status);

			});

		};

		/**
		 * Generic helper for loading a list of template files (*.ehtml)
		 * that correspond to a specific template type (e.g. <code>View</code>s, or <code>Partial</code>s).
		 *
		 *  If compiled representations of the template file exist AND is up-to-date AND
		 *  the configuration is set to load the compiled files rather than the raw template
		 *  file (see <code>isUsePreCompiledViews</code>), then the compiled template is used.
		 *
		 *
		 * This function uses an asynchronous method for loading the template-files.<br>
		 *
		 * <b>If you want to make sure, that all templates have indeed been loaded, before
		 * proceeding with the subsequent program flow, you should have a look at the
		 * function {@link mmir.ControllerManager#foundControllersCallBack}: use the returned
		 * Deferred.Promise for executing code that depends on the templates being loaded.</b>
		 *
		 * <p>
		 * Uses {@link doloadTemplateFile} for loading single template files.
		 *
		 * @function
		 * @private
		 * @async
		 * @memberOf mmir.env.view.ViewLoader#
		 *
		 * @see #doLoadTemplateFile
		 *
		 * @param {PlainObject} createConfig
		 * 			configuration object that determines which templates are loaded, and how
		 * 			the loaded data is processed.
		 *
		 *
		 * @returns {Promise} a deferred promise, that resolves after all partials have been loaded
		 * 					NOTE: loading failures will generate a warning message (on the console)
		 * 						  but will not cause the Promise to fail.
		 */
		var doProcessTemplateList = function(createConfig){

			/**
			 * @type Promise
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#doProcessTemplateList
			 */
			var defer = deferred();

			/**
			 * @type String
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#doProcessTemplateList
			 */
			var ctrlNameList = controllerManager.getNames();

			/**
			 * HELPER object for tracking the loading-status of the views
			 *
			 * @private
			 * @memberOf mmir.env.view.ViewLoader#doProcessTemplateList
			 */
			var loadStatus = {
					loader: defer,
					remainingCtrlCount: ctrlNameList.length,
					currentLoadCount: 0
			};

			forEach(ctrlNameList, function(controllerName){

				var controller = controllerManager.get(controllerName);

				forEach(controller[createConfig.accessorName](), function(templateInfo){

					doLoadTemplateFile(controller, templateInfo, createConfig, loadStatus);

				});//END: each(templateInfo)

				-- loadStatus.remainingCtrlCount;
				checkCompletion(loadStatus);

			});//END: each(ctrlName)

			checkCompletion(loadStatus);
			return defer;

		};//END: doProcessTemplateList()

		/**
		 * HELPER that loads a single template file asynchronously and creates a corresponding template-class instance
		 * (depending on <code>createConfig</code>).
		 *
		 * The <code>status</code> is updated on successful loading or on error (see {@link updateLoadStatus}).
		 *
		 * @example
		 *
		 * //EXAMPLE for createConfig for loading template contents into a Partial
		 * var theCreateConfig = {
		 *   constructor: Partial,        // the class constructor that takes the loaded template data
		 *   typeName: 'Partial',         // the name of the class that will be created
		 *   collection: _partials,        // the map/dictionary to which the created class-instance will be added
		 *   keyGen: createPartialKey,    // the function for creating the lookup-key (for the dictionary)
		 *   accessorName: 'getPartials'  // the accessor-function's name for accessing the info-objects on the controller-instance
		 * };
		 * doLoadTemplateFiles(theCreateConfig).then(function(){
		 * 	//do something that depends on loading of the template files...
		 * });
		 *
		 * //EXAMPLE for createConfig for loading template contents into a Layout
		 * var theCreateLayoutConfig = {
		 *   constructor: Layout,        // the class constructor that takes the loaded template data
		 *   typeName: 'Layout',         // the name of the class that will be created
		 *   collection: _layouts,        // the map/dictionary to which the created class-instance will be added
		 * };
		 *
		 * doLoadTemplateFiles(theCreateLayoutConfig).then(function(){
		 * 	//do something that depends on loading of the template files...
		 * });
		 *
		 * //for createConfig for loading template contents into a Partial
		 *
		 * @param {mmir.ctrl.Controller} controller
		 * 		the controller to which the template files belong.
		 * 		May be <code>null</code>: see {@link doParseTemplate}.
		 *
		 * @param {PlainObject} templateInfo
		 * 		the JSON-like object containing information for the template
		 * 		(e.g. <code>name</code>, <code>file-path</code> etc.; see
		 * 		 {@link mmir.ControllerManager#getControllerResources} for
		 *          more information).
		 *
		 * @param {PlainObject} createConfig
		 * 		configuration that is used to create a corresponding template-object
		 * 		for the loaded template-contents.
		 * 		The created object will be added to <code>createConfig.collection</code>
		 * 		(Map; with controller's name as key).
		 *
		 * @param {PlainObject} loadStatus
		 * 		Object for managing the loading-status. The status is updated and used to
		 * 		determine, if all templates (e.g. from a list) have been (asynchronously)
		 * 		loaded.
		 *
		 * @function
		 * @private
		 * @async
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var doLoadTemplateFile = function(controller, templateInfo, createConfig, loadStatus){
			++loadStatus.currentLoadCount;

			if(!templateInfo.path){
				logger.warn('cannot check if pre-compiled view is updated: no template file available for '+templateInfo.name);
				loadPrecompiledView(null, templateInfo.genPath, function(){

					updateLoadStatus(loadStatus);

				}, function(err){

					logger.error('Could not load precompiled '+createConfig.typeName+' from '
							+templateInfo.genPath+'", because: '+err
					);
					updateLoadStatus(loadStatus);

				});
				return;///////////// EARLY EXIT //////////////////
			}

			loadFile({
				async: true,
				dataType: "text",
				url: templateInfo.path,
				success: function onSuccess(data){

					if(isUsePreCompiledViews){

						loadPrecompiledView(data, templateInfo.genPath, function(){

							updateLoadStatus(loadStatus);

						}, function(err){

							logger.warn('Could not load precompiled '+createConfig.typeName+' from '
									+templateInfo.genPath+'", because: '+err
									+', compiling template instead: '
									+templateInfo.path
							);

							doParseTemplate(controller, templateInfo.name, createConfig , data, loadStatus);

						});

					}
					else {

						doParseTemplate(controller, templateInfo.name, createConfig, data, loadStatus);

					}

				},
				error: function onError(_jqxhr, status, err){

					// print out an error message
					var errMsg = err && err.stack? err.stack : err;
					logger.error("[" + status + "] Could not load eHTML file '" + templateInfo.path + "': "+errMsg); //failure

					updateLoadStatus(loadStatus, true);
				}
			});

			checkCompletion(loadStatus);

		};//END: doLoadTemplateFile()


		///////////// start intialization: ////////////////

		/**
		 * Deferred / promise for loading views.
		 *
		 * @type Promise
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var defer = deferred();

		var isLayoutsLoaded = false;
		var isViewsLoaded = false;
		var isPartialsLoaded = false;

		/**
		 * Helper: called each time a loading-function finishes.
		 * Checks if all other loading-functions have finished, and if so, resolves the init-promise.
		 *
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var checkResolved = function(){
			if(isLayoutsLoaded && isViewsLoaded && isPartialsLoaded){
				defer.resolve();
			}
		};
		/**
		 * Helper: called if an error occured in one of the loading-functions:
		 * rejects/fails the init-promise.
		 *
		 * @private
		 * @memberOf mmir.env.view.ViewLoader#
		 */
		var failPromise = function(msg){
			defer.reject(msg);
		};

		//util for checking if pre-compiled views are up-to-date
		// (i.e.: can we use the pre-compiled view, or do we need to use the template file and compile it on-the-fly)
		//TODO should this also be configurable -> up-to-date check (e.g. use pre-compiled views without checking for changes)
		checksumUtils = checksumUtils.init();

		loadLayouts().then(
				function(){ isLayoutsLoaded = true; checkResolved(); },
				failPromise
		);
		loadViews().then(
				function(){ isViewsLoaded = true; checkResolved(); },
				failPromise
		);
		loadPartials().then(
				function(){ isPartialsLoaded = true; checkResolved(); },
				failPromise
		);

		return defer;

	};//END: loadViews(){

	return loadViews;
});