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;
});