1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 
 28 
 29 define([ 'controllerManager', 'constants', 'commonUtils', 'configurationManager'//DISABLED: now loaded on-demand (see init()) -> 'renderUtils'
 30          , 'layout', 'view', 'partial', 'dictionary', 'checksumUtils', 'languageManager'
 31          , 'jquery', 'core'//, 'module'
 32          , 'stringExtension', 'parserModule'
 33     ],
 34     
 35     /**
 36      * @class
 37      * @name mmir.PresentationManager
 38      * @static 
 39      *  
 40      * Libraries:
 41      *  - jQuery (>= v1.6.2); ajax, each
 42      *  
 43      *  @requires document (DOM object)
 44      *  
 45      *  @requires jQuery.Deferred
 46      *  @requires jQuery.ajax
 47      *  @requires jQuery.each
 48      *  
 49      */
 50     function ( controllerManager, constants, commonUtils, configurationManager//, renderUtils
 51     		, Layout, View, Partial, Dictionary, checksumUtils, languageManager
 52             , $, core//, module
 53 ) {
 54 	
 55 	//the next comment enables JSDoc2 to map all functions etc. to the correct class description
 56 	/** @scope mmir.PresentationManager.prototype */
 57 	
 58 	/**
 59      * Counter that keeps track of the number of times, that a view is rendered
 60      * 
 61      * NOTE: for implementation specific reasons, jQuery Mobile requires that
 62      * 		 each page has a different ID. This pageIndex is used to generating
 63      * 		 such a unique ID, by increasing the number on each page-change
 64      * 		 (i.e. by rendering a view) and appending it to the page's ID/name.
 65      * 
 66      * @type Integer
 67      * @public
 68 	 * @memberOf mmir.PresentationManager#
 69      */
 70 	var _pageIndex = 0;
 71 
 72 	 /**
 73 	  * Name for the default layout.
 74 	  * 
 75 	  * <p>
 76 	  * There must exist a layout definition by
 77 	  * this name, i.e.
 78 	  * <pre>views/layout/<DEFAULT_LAYOUT_NAME>.ehtml</pre>
 79 	  * 
 80 	  * NOTE: while the name begins with an upper case
 81 	  *       letter, the file name for the layout must
 82 	  *       start with a lower case letter, e.g. for
 83 	  *       name <code>Default</code>, the file name
 84 	  *       must be <code>default.ehtml</code>.
 85 	  * 
 86 	  * @type String
 87 	  * @private
 88 	  * @constant
 89 	  * @memberOf mmir.PresentationManager#
 90 	  */
 91 	 var DEFAULT_LAYOUT_NAME = 'Default';
 92 	 
 93 	 /**
 94 	  * Name of the configuration property that specifies whether or not to use 
 95 	  * pre-compiled views, i.e. whether to use generated JavaScript files
 96 	  * instead of parsing & compiling the "raw" templates (eHTML files).
 97 	  * 
 98 	  * <p>
 99 	  * NOTE: the configuration value, that can be retrieved by querying this configuration-property
100 	  * 	  has is either a Boolean, or a String representation of a Boolean value:
101 	  * 		<code>[true|false|"true"|"false"]</code>
102 	  * <br>
103 	  * NOTE2: There may be no value set at all in the configuration for this property.
104 	  * 	   In this case you should assume that it was set to <code>false</code>. 
105 	  * 
106 	  * @type String
107 	  * @private
108 	  * @constant
109 	  * @memberOf mmir.PresentationManager#
110 	  * 
111 	  * @example var isUsePrecompiledViews = mmir.Constants.get(CONFIG_PRECOMPILED_VIEWS_MODE);
112 	  * 
113 	  */
114 	 var CONFIG_PRECOMPILED_VIEWS_MODE = 'usePrecompiledViews';//TODO move this to somewhere else (collected config-vars?)? this should be a public CONSTANT...
115 	 
116 	 // private members
117 	 /**
118 	  * Array of layouts of the application
119 	  * 
120 	  * @type Dictionary
121 	  * @private
122 	  * @memberOf mmir.PresentationManager#
123 	  */
124 	 var _layouts = new Dictionary();
125 
126 	 /**
127 	  * Array of all the views of the application
128 	  * 
129 	  * @type Dictionary
130 	  * @private
131 	  * @memberOf mmir.PresentationManager#
132 	  */
133 	 var _views = new Dictionary();
134 
135 	 /**
136 	  * Array of all the partials of the application
137 	  * 
138 	  * @type Dictionary
139 	  * @private
140 	  * @memberOf mmir.PresentationManager#
141 	  */
142 	 var _partials = new Dictionary();
143 
144 	 /**
145 	  * An object containing data for the currently displayed view.<br>
146 	  * It contains: name of the corresponding controller, name of the view
147 	  * and optionally data for the view
148 	  * 
149 	  * @type View
150 	  * @private
151 	  * @memberOf mmir.PresentationManager#
152 	  */
153 	 var _currentView = {};
154 
155 	 /**
156 	  * An object containing data for the previously displayed view - the one
157 	  * displayed before the current view.<br>
158 	  * It contains: name of the corresponding controller, name of the view
159 	  * and optionally data for the view
160 	  * 
161 	  * @type View
162 	  * @private
163 	  * @memberOf mmir.PresentationManager#
164 	  * 
165 	  * @deprecated do not use
166 	  */
167 	 var _previousView = {};
168 
169 	 /**
170 	  * The currently displayed dialog object, if a dialog is displayed. Used
171 	  * mainly to close the dialog.
172 	  * 
173 	  * @type Object
174 	  * @private
175 	  * @memberOf mmir.PresentationManager#
176 	  * 
177 	  * @see mmir.PresentationManager#showDialog
178 	  * @see mmir.PresentationManager#hideCurrentDialog
179 	  */
180 	 var _currentDialog = null;
181 	 
182 	 /**
183 	  * @private
184 	  * @memberOf mmir.PresentationManager#
185 	  */
186 	 var viewSeparator 		= '#';
187 	 /**
188 	  * @type String
189 	  * @private
190 	  * @memberOf mmir.PresentationManager#
191 	  */
192      var partialSeparator 	= commonUtils.getPartialsPrefix();
193      /**
194 	  * @private
195 	  * @memberOf mmir.PresentationManager#
196 	  */
197      function createLookupKey(ctrl, viewObj, separator){
198      	if(typeof ctrl.getName !== 'undefined'){
199      		ctrl = ctrl.getName();
200      	}
201      	if(typeof viewObj.getName !== 'undefined'){
202      		viewObj = viewObj.getName();
203      	}
204      	//TODO remove all >partialSeparator< from partial-string beginning
205      	return ctrl+separator+viewObj;
206      }
207      /**
208 	  * @private
209 	  * @memberOf mmir.PresentationManager#
210 	  */
211      function createViewKey(ctrl, view){
212      	return createLookupKey(ctrl, view, viewSeparator);
213      }
214      /**
215 	  * @private
216 	  * @memberOf mmir.PresentationManager#
217 	  */
218      function createPartialKey(ctrl, partial){
219      	return createLookupKey(ctrl, partial, partialSeparator);
220      }
221      
222 
223 	 /**
224 	  * Default implementation for the rendering-engine:
225 	  * 
226 	  * does nothing but writing an error message to the console,
227 	  * if any of its functions is invoked.
228 	  * 
229 	  * The rendering engine can be set via {@link mmir.PresentationManager#setRenderEngine}.
230 	  * 
231 	  * @type RenderEngine
232 	  * @private
233 	  * @memberOf mmir.PresentationManager#
234 	  */
235      var _renderEngine = {
236     		 /**
237     		  * The function that actually renders the View.<br>
238     		  * 
239     		  * The function will be invoked in context of the PresentationManager instance
240     		  * (i.e. the manager will be the <em>this</em> context).
241     		  * 
242     		  * Implementations of this function should adhere to the following procedure:
243     		  * 
244     		  * <br>
245     		  * 
246     		  * First this function fetches the <em>layout</em> for the <em>controller</em>
247     		  * (or uses the <code>dialogManager.DEFAULT_LAYOUT<code>).
248     		  * 
249     		  * Then the <code>before_page_prepare</code> of the <em>controller</em> is invoked (if it exists).
250     		  * 
251     		  * Then renders the <em>view</em> into the
252     		  * layout-template; <em>partials</em>, <em>helpers</em> etc.
253     		  * that are referenced in the <em>view</em> will be processed,
254     		  * executed etc.; during this, localized Strings should be processed and rendered by
255     		  * {@link mmir.LanguageManager#getText}. 
256     		  * 
257     		  * Then <em>dialogs</em> are created and the <code>dialogManager.pageIndex</code> is updated.
258     		  * 
259     		  * The new content is inserted into the document/page (invisibly).
260     		  * 
261     		  * Then the <code>before_page_load</code> of the <em>controller</em> is invoked (if it exists).
262     		  * 
263     		  * The new content/page is made visible, and the old one invisible and / or is removed.
264     		  * 
265     		  * At the end the <b>on_page_load</b> action of the <em>controller</em> is performed.
266     		  * 
267     		  * @function
268     		  * @memberOf mmir.PresentationManager._renderingEngine
269     		  * 
270     		  * @param {String}
271     		  *            ctrlName Name of the controller
272     		  * @param {String}
273     		  *            viewName Name of the view to render
274     		  * @param {Object}
275     		  *            view View object that is to be rendered
276     		  * @param {Object}
277     		  *            ctrl Controller object of the view to render
278     		  * @param {Object}
279     		  *            [data] optional data for the view.
280     		  */
281     		 render: function(ctrlName, viewName, view, ctrl, data){
282     			 console.error('PresentationManager.render: no rendering engine set!');
283     		 },
284     		 showDialog: function(ctrlName, dialogId, data) {
285     			 console.error('PresentationManager.showDialog: no rendering engine set!');
286     		 },
287     		 hideCurrentDialog: function(){
288     			 console.error('PresentationManager.hideCurrentDialog: no rendering engine set!');
289     		 },
290     		 showWaitDialog: function(text, data) {
291     			 console.error('PresentationManager.showWaitDialog: no rendering engine set!');
292     		 },
293     		 hideWaitDialog: function() {
294     			 console.error('PresentationManager.hideWaitDialog: no rendering engine set!');
295     		 }
296      };
297 	 /**
298 	  * Reference to the rendering-engine implementation / instance.
299 	  * 
300 	  * This reference should not be accessed directly.
301 	  * Custom functions of the rendering implementation can be
302 	  * invoked via {@link mmir.PresentationManager#callRenderEngine}.
303 	  * 
304 	  * @type Object
305 	  * @private
306 	  */
307      _renderEngine._engine = _renderEngine;
308 	 
309 	 var _instance = {
310 			/** @scope mmir.PresentationManager.prototype  */
311 			 
312 			/**
313 			 * @deprecated instead: use mmir.PresentationManager directly
314 			 * 
315 			 * @memberOf mmir.PresentationManager.prototype
316 			 */
317 			getInstance: function () {
318 				return this;
319 			},
320 
321            // public members
322             addLayout : function(layout) {
323                 _layouts.put(layout.getName(), layout);
324             },
325             /**
326              * This function returns a layout object by name.<br>
327              * 
328              * @function
329              * @param {String}
330              *            layoutName Name of the layout which should be returned
331              * @param {Boolean}
332              *            [doUseDefaultIfMissing] if supplied and
333              *            <code>true</code>, the default controller's layout
334              *            will be used as a fallback, in case no corresponding
335              *            layout could be found
336              * @returns {Object} The requested layout, "false" if not found
337              * @public
338 			 * @memberOf mmir.PresentationManager.prototype
339              */
340             getLayout : function(layoutName, doUseDefaultIfMissing) {
341                 var layout = false;
342                 layout = _layouts.get(layoutName);
343                 if (!layout) {
344                     if (doUseDefaultIfMissing) {
345                         layout = _instance.getLayout(DEFAULT_LAYOUT_NAME, false);
346                     }
347                     else {
348                         console.error('[PresentationManager.getLayout]: could not find layout "' + layoutName +'"')
349                         return false;
350                     }
351                 }
352                 return layout;
353             },
354 
355             /**
356              * 
357              * @param {String|Controller} ctrlName
358              * @param {String|View} view
359              * @public
360 			 * @memberOf mmir.PresentationManager.prototype
361              */
362             addView : function(ctrlName, view) {
363                 _views.put(createViewKey(ctrlName, view), view);
364             },
365             /**
366              * This function returns a view object by name.<br>
367              * 
368              * @function
369              * @param {String}
370              *            controllerName Name of the controller for the view
371              * @param {String}
372              *            viewName Name of the view which should be returned
373              * @returns {Object} The requested view, <tt>false</tt> if not
374              *          found
375              * @public
376 			 * @memberOf mmir.PresentationManager.prototype
377              */
378             getView : function(controllerName, viewName) {
379                 viewName = createViewKey(controllerName, viewName);
380                 var view = false;
381                 view = _views.get(viewName);
382 
383                 if (!view) {
384                     console.error('[PresentationManager.getView]: could not find view "' + viewName + '"');
385                     return false;
386                 }
387                 return view;
388             },
389             /**
390              * 
391              * @param {String|Controller} ctrlName
392              * @param {String|Partial} partial
393              * 
394              * @public
395 			 * @memberOf mmir.PresentationManager.prototype
396              */
397             addPartial: function(ctrlName, partial){
398             	_partials.put(createPartialKey(ctrlName, partial), partial);
399             },
400 
401             /**
402              * This function returns a partial object by name.<br>
403              * 
404              * @function
405              * @param {String}
406              *            controllerName Name of the controller for the view
407              * @param {String}
408              *            viewName Name of the partial which should be returned
409              * @returns {Object} The requested partial, "false" if not found
410              * @public
411 			 * @memberOf mmir.PresentationManager.prototype
412              */
413             getPartial : function(controllerName, partialName) {
414                 var partial = false;
415 
416                 var partialKey = null;
417                 if (controllerName) {
418                     partialKey = createPartialKey(controllerName, partialName);
419                 }
420                 else {
421                     console.error('[PresentationManager.getPartial]: requested partial "' + partialName + '" for unknown controller: "' + (controllerName ? (controllerName.getName? controllerName.getName(): controllerName) : 'undefined')
422                             + '"');
423                     return false;
424                 }
425 
426                 partial = _partials.get(partialKey);
427                 if (!partial) {
428                     console.error('[PresentationManager.getPartial]: could not find partial "' + partialName + '" for controller "' + (controllerName ? (controllerName.getName? controllerName.getName(): controllerName) : 'undefined') + '"!');
429                     return false;
430                 }
431                 return partial;
432             },
433 
434             /**
435              * Closes a modal window / dialog (if one is open).
436              * <br>
437              * 
438              * @function
439              * @public
440 			 * @memberOf mmir.PresentationManager.prototype
441              */
442             hideCurrentDialog : function() {
443 				_renderEngine.hideCurrentDialog.apply(this,arguments);
444             },
445 
446             /**
447              * Opens the dialog for ID <code>dialogId</code>.
448              * <br>
449              * 
450              * @function
451              * @param {String} ctrlName
452              *            the Name of the controller
453              * @param {String} dialogId
454              *            the ID of the dialog
455              * @param {Object} [data] OPTIONAL
456              *            a data / options object
457              *            
458              * @returns {Object} the instance of the opened dialog (void or falsy dialog was not opened)
459              * 
460              * @public
461 			 * @memberOf mmir.PresentationManager.prototype
462              */
463             showDialog : function(ctrlName, dialogId, data) {
464             	_currentDialog = _renderEngine.showDialog.apply(this,arguments);
465 				return _currentDialog;
466 			},
467 			
468 			/**
469 			 * Shows a "wait" dialog, i.e. "work in progress" notification.
470 			 * 
471 			 * @function
472 			 * 
473 			 * @param {String} [text] OPTIONAL
474 			 * 				the text that should be displayed.
475 			 * 				If omitted the language setting for <code>loadingText</code>
476 			 * 				will be used instead (from dictionary.json)
477 			 * @param {Object} [data] OPTIONAL
478 			 * 				a data / options object
479 			 * 
480 			 * @public
481 			 * @memberOf mmir.PresentationManager.prototype
482 			 * 
483 			 * @see mmir.PresentationManager#hideWaitDialog
484 			 */
485 			showWaitDialog : function(text, data) {
486 				_renderEngine.showWaitDialog.apply(this,arguments);
487 			},
488 
489 			/**
490 			 * Hides / closes the "wait" dialog.
491 			 * 
492 			 * @function
493 			 * @public
494 			 * @memberOf mmir.PresentationManager.prototype
495 			 * 
496 			 * @see mmir.PresentationManager#showWaitDialog
497 			 */
498 			hideWaitDialog : function() {
499 				_renderEngine.hideWaitDialog.apply(this,arguments);
500 			},
501 
502 			/**
503 			 * Gets the view for a controller, then executes helper methods on
504 			 * the view data. The Rendering of the view is done by the
505 			 * {@link #doRenderView} method. Also
506 			 * stores the previous and current view with parameters.<br>
507 			 * 
508 			 * @function
509 			 * @param {String}
510 			 *            ctrlName Name of the controller
511 			 * @param {String}
512 			 *            viewName Name of the view to render
513 			 * @param {Object}
514 			 *            [data] optional data for the view.
515 			 *            Currently same jQuery Mobile specific properties are supported: <br>
516 			 *            When these are present, they will be used for animating the 
517 			 *            page transition upon rendering.
518 			 *            
519 			 *            <pre>{transition: STRING, reverse: BOOLEAN}</pre>
520 			 *            where<br>
521 			 *            <code>transition</code>: the name for the transition (see jQuery Mobile Doc for possible values)
522 			 *            							DEFAULT: "none".
523 			 *            <code>reverse</code>: whether the animation should in "forward" (FALSE) direction, or "backwards" (TRUE)
524 			 *            						DEFAULT: FALSE
525 			 * @public
526 			 * @memberOf mmir.PresentationManager.prototype
527 			 */
528             renderView : function(ctrlName, viewName, data) {
529             	
530                 var ctrl = controllerManager.getController(ctrlName);
531 
532                 if (ctrl != null) {
533                     var view = this.getView(ctrlName, viewName);
534                     
535                     _renderEngine.render.call(this, ctrlName, viewName, view, ctrl, data);
536 
537                     //TODO russa: _previousView is deprecated (should use a history instead, i.e. application level)
538                     // Only overwrite previous state if and only if the view is not re-rendered!
539 					if (ctrlName != _currentView.ctrlName || viewName != _currentView.viewName || data != _currentView.data){
540 						_previousView.ctrlName=_currentView.ctrlName;
541 						_previousView.viewName=_currentView.viewName;
542 						_previousView.data=_currentView.data;
543 					}
544 					
545 					_currentView.ctrlName=ctrlName; 
546 					_currentView.viewName=viewName; 
547 					_currentView.data=data;
548                 }
549                 else {
550                 	console.warn('PresentationManager.renderView: could not retrieve controller "'+ctrlName+'"');
551                 }
552             },
553 
554             /**
555              * Renders the current view again, using the
556              * {@link #renderView} method.
557              * 
558              * @deprecated you should use {@link #renderView} with appropriate parameters instead.
559              * 
560              * @requires mmir.DialogManager
561              * 
562              * @function
563              * @public
564 			 * @memberOf mmir.PresentationManager.prototype
565              */
566             reRenderView : function() {
567                 if (_currentView) {
568                     if (_currentView.ctrlName && _currentView.viewName) {
569                         require('dialogManager').render(_currentView.ctrlName, _currentView.viewName, _currentView.data);
570                     }
571                 }
572             },
573 
574             /**
575              * Renders the previous view again, using the
576              * {@link mmir.DialogManager#render} method.
577              * 
578              * 
579              * @deprecated you should use {@link #renderView} with appropriate parameters instead.
580              * 
581              * @requires mmir.DialogManager
582              * 
583              * @function
584              * @public
585 			 * @memberOf mmir.PresentationManager.prototype
586              */
587             renderPreviousView : function() {
588                 if (_previousView) {
589                     if (_previousView.ctrlName && _previousView.viewName) {
590                     	require('dialogManager').render(_previousView.ctrlName, _previousView.viewName, _previousView.data);
591                     }
592                 }
593             },        
594 	
595             /**
596              * @function
597 			 * @memberOf mmir.PresentationManager.prototype
598              */
599             init: init,
600             
601             /**
602              * Sets the <em>rendering engine</em> for the views.
603              * 
604              * The render engine <b>must</b> implement a function <em>render</em>
605              * and <i>may</i> implement functions <em>showDialog</em>,
606              * <em>hideCurrentDialog</em>, <em>showWaitDialog</em>, and <em>hideWaitDialog</em>:
607              * 
608              * <ul>
609              * 	<li><b>theRenderEngine.<code>render(ctrlName : String, viewName : String, view : View, ctrl : Controller, data : Object) : void</code></b></li>
610              * 	<li>theRenderEngine.<code>showDialog(ctrlName : String, dialogId : String, data : Object) : Dialog</code></li>
611              * 	<li>theRenderEngine.<code>hideCurrentDialog(): void</code></li>
612              * 	<li>theRenderEngine.<code>showWaitDialog(text : String, data : Object): void</code></li>
613              * 	<li>theRenderEngine.<code>hideWaitDialog(): void</code></li>
614              * </ul>
615              * 
616              * The functions of <code>theRenderEngine</code> will be called in
617              * context of the PresentationManager.
618              * 
619              * Custom functions of the specific rendering engine implementation 
620              * (i.e. non-standard functions) can be call via {@link #callRenderEngine}.
621              * 
622              * 
623              * <br>
624              * By default, the rendering-engine as defined by the module ID/path in
625              * <code>core.viewEngine</code> will be loaded and set during initialization
626              * of the DialogManager.
627              * 
628              * <br>
629              * The implementation of the default view-engine is at
630              * <code>mmirf/env/view/presentation/jqmViewEngine.js</code>.
631              * 
632              * @param {Object} theRenderEngine
633              * 			the render-engine for views
634              * 
635              * @function
636              * @public
637 			 * @memberOf mmir.PresentationManager.prototype
638              * 
639              * @see mmir.PresentationManager#renderView
640              * @see mmir.PresentationManager#showDialog
641              * @see mmir.PresentationManager#hideCurrentDialog
642              * @see mmir.PresentationManager#showWaitDialog
643              * @see mmir.PresentationManager#hideWaitDialog
644              * 
645              * @see mmir.PresentationManager#callRenderEngine
646              * 
647              */
648             setRenderEngine: function(theRenderEngine){
649             	_renderEngine.render 			= theRenderEngine.render;
650             	_renderEngine.showDialog		= theRenderEngine.showDialog;
651             	_renderEngine.hideCurrentDialog	= theRenderEngine.hideCurrentDialog;
652             	_renderEngine.showWaitDialog	= theRenderEngine.showWaitDialog;
653             	_renderEngine.hideWaitDialog	= theRenderEngine.hideWaitDialog;
654             	_renderEngine._engine			= theRenderEngine;
655             },
656             /**
657              * This function allows to call custom functions of the rendering-engine
658              * that was set via {@link #setRenderEngine}.
659              * 
660              * IMPORTANT:
661              * note that the function will be invoked in context of rendering-engine
662              * (i.e. <code>this</code> references will refer to rendering-engine
663              * and not to the PresentationManager instance.
664              * For example, when <code>mmir.PresentationManager.callRenderEngine('hideWaitDialog')</code>
665              * is called, any <code>this</code> references within the <code>hideWaitDialog</code>
666              * implementation would refer to object, that was set in <code>setRenderEngine(object)</code>.
667              * In comparison, when called as <code>mmir.PresentationManager.hideWaitDialog()</code> the
668              * <code>this</code> references refer to the mmir.PresentationManager instance.
669              * 
670              * <br>
671              * NOTE that calling non-existing functions on the rendering-engine 
672              *      will cause an error.
673              * 
674              * @param {String} funcName
675              * 			the name of the function, that should be invoked on the rendering
676              * 			engine.
677              * @param {Array<any>} [args] OPTIONAL
678              * 			the arguments for <code>funcName</code> invoked
679              *          via <code>Function.apply(renderingEngine, args)</code>, e.g.
680              * 			for <code>args = [param1, param2, param3]</code> the
681              * 			function will be called with
682              * 			<code>funcName(param1, param2, param3)</code>
683              * 			(note that the function receives 3 arguments, and
684              *           not 1 Array-argument).
685              * 
686              * @function
687              * @public
688 			 * @memberOf mmir.PresentationManager.prototype
689              */
690             callRenderEngine: function(funcName, args){
691             	return _renderEngine._engine[funcName].apply(_renderEngine._engine, args);
692             },
693             
694             //exported properties / constants:
695             /**
696              * @public
697              * @type Integer
698              * @constant
699 			 * @memberOf mmir.PresentationManager.prototype
700              */
701             pageIndex:		_pageIndex
702 		};//END:  return{...
703 //	})();//END: (function(){...
704 	
705 
706 	return _instance;
707 	
708 	function init () {
709 
710 		/** @scope mmir.MediaManager.prototype */
711 
712 		delete _instance.init;//FIXME should init be deleted?
713 
714 		/**
715 		 * Checks if a pre-compiled view is up-to-date:
716 		 * loads the view, if it is current.
717 		 * 
718 		 * If the pre-compiled view is not current, or loading-errors
719 		 * occur, the fail-callback will be triggered 
720 		 * (the callback argument may contain information about the cause).
721 		 * 
722 		 * @async
723 		 * @private
724 		 * @memberOf mmir.MediaManager.init
725 		 * 
726 		 * @param {String} rawViewData
727 		 * 					the text content of the view template (i.e. content of a eHTML file "as is")
728 		 * @param {String} targetPath
729 		 * 					the path to the pre-compiled view file
730 		 * @param {Function} success
731 		 * 					callback that will be triggered, if pre-compiled view file was loaded
732 		 * 					NOTE: the JS code of the loaded file may not have been fully executed yet!
733 		 * @param {Function} fail
734 		 * 					callback that will be triggered, if pre-compiled view is not up-to-date or
735 		 * 					an error occurs while loading the file
736 		 */
737 		function loadPrecompiledView(rawViewData, targetpath, success, fail){
738 
739 			//NOTE: stored template require the renderUtils:
740 			require(['renderUtils'], function(){
741 				
742 				if(! isUpToDate(rawViewData, targetpath)){
743 					if(fail) fail('Precompiled view file is outdated!');
744 					else console.warn('Outdated pre-compiled view at: '+targetpath);
745 					
746 					//-> do not load the pre-compiled view, instead let fail-callback handle re-parsing for the view
747 					return;/////////////////////// EARLY EXIT /////////////////////
748 				}
749 	
750 				commonUtils.getLocalScript( //scriptUrl, success, fail)
751 						targetpath, success, fail
752 				);
753 				
754 			});
755 			
756 		}
757 
758 		/**
759 		 * Flag for determining if pre-compiled views (*.js) should be used
760 		 * 
761 		 * Reads property {@link #CONFIG_PRECOMPILED_VIEWS_MODE}. If the property is not set,
762 		 * <code>false</code> is used by default, i.e. no pre-compiled views are used.
763 		 *
764 		 * @protected
765 		 * @default
766 		 * @type Boolean
767 		 * @default false: use templates files (*.ehtml) and compile them (freshly) on-the-fly
768 		 * @memberOf mmir.MediaManager.init
769 		 */
770 		var isUsePreCompiledViews = configurationManager.getBoolean(CONFIG_PRECOMPILED_VIEWS_MODE, true, false);
771 
772 		/**
773 		 * Read the checksum file that was created when the pre-compiled view was created:
774 		 * 
775 		 * it contains the view's template size (the length of its String representation) and MD5 hash.
776 		 * 
777 		 * -> by calculating the viewContent's size and MD5 hash, we can determine, if it has changed
778 		 *    by comparing it with the data of the checksum file.
779 		 *    
780 		 * @sync the checksum file is loaded in synchronous mode
781 		 * @private
782 		 * @memberOf mmir.MediaManager.init
783 		 * 
784 		 * @param {String} viewContent
785 		 * 						the content of the view template (i.e. loaded eHTML file)
786 		 * @param {String} preCompiledViewPath
787 		 * 						the path to the corresponding pre-compiled view file
788 		 * 
789 		 */
790 		function isUpToDate(viewContent, preCompiledViewPath){
791 			//replace file extension with the checksum-file's one: '.js' -> '.checksum.txt'
792 			var  viewVerificationInfoPath = 
793 					preCompiledViewPath.substring(0, preCompiledViewPath.length - 3) 
794 						+ checksumUtils.getFileExt();
795 
796 			var isCompiledViewUpToDate = false;
797 
798 			$.ajax({
799 				async: false,//<-- use "SYNC" modus here (NOTE: we win nothing with async here, because the following step (loading/not loading the pre-compiled view) strictly depends on the result of this)
800 				dataType: "text",
801 				url: viewVerificationInfoPath,
802 				success: function(data){
803 
804 					//compare raw String to checksum-data from file
805 					isCompiledViewUpToDate = checksumUtils.isSame(viewContent, data);
806 				}
807 			}).fail(function(jqxhr, status, err){
808 				// print out an error message
809 				var errMsg = err && err.stack? err.stack : err;
810 				console.error("[" + status + "] Could not load '" + viewVerificationInfoPath + "': "+errMsg); //failure
811 			});
812 
813 			return isCompiledViewUpToDate;
814 		}
815 
816 		/**
817 		 * This function loads the layouts for every controller and puts the
818 		 * name of the layouts into the <b>_layouts</b> array.
819 		 * 
820 		 * @function
821 		 * @private
822 		 * @memberOf mmir.MediaManager.init
823 		 * 
824 		 * @returns {Promise} a Deferred.promise that gets resolved upon loading all layouts; fails/is rejected, if not at least 1 layout was loaded
825 		 */
826 		function loadLayouts(){
827 			// Load application's layouts. 
828 
829 			/**
830 			 * @type jQuery.Deffered
831 			 * @private
832 			 * @memberOf mmir.MediaManager.init.loadLayouts
833 			 */
834 			var defer = $.Deferred();
835 			
836 			/**
837 			 * @type String
838 			 * @private
839 			 * @memberOf mmir.MediaManager.init.loadLayouts
840 			 */
841 			var ctrlNameList = controllerManager.getControllerNames();
842 			
843 			/**
844 			 * HELPER object for tracking the loading-status of the layouts
845 			 * 
846 			 * @private
847 			 * @memberOf mmir.MediaManager.init.loadLayouts
848 			 */
849 			var loadStatus = {
850 					loader: defer,
851 					remainingCtrlCount: ctrlNameList.length + 1,//+1: for the default layout
852 					currentLoadCount: 0,
853 
854 					//additional property for keeping track on how many layouts were load overall
855 					// NOTE: this additional counter is necessary, since currentLoadCount
856 					//       keeps only track of how many controller's were checked. But since
857 					//       a controller may not have a layout-defintion of its own, we have
858 					//       to use another counter to keep track of actually loaded layouts.
859 					loadedLayoutsCount: 0,
860 
861 					//need a custom function for checking the load status: if no layout was loaded, 
862 					//                                                     the Derred will be rejected
863 					onCompletionImpl: function(status){
864 						if(status.loadedLayoutsCount < 1){
865 
866 							//there must be at least on layout-file for the default-controller:
867 							status.loader.reject( 'Could not load any layout! At least one layout must be present at '
868 									+ constants.getLayoutPath() 
869 									+ DEFAULT_LAYOUT_NAME[0].toLowerCase() + DEFAULT_LAYOUT_NAME.substring(1) 
870 									+ '.ehtml'
871 							);
872 						}
873 						else {
874 							status.loader.resolve();
875 						} 
876 					},
877 
878 					//extend the status-update function: in case loading succeeded, increase the counter
879 					//                                   for overall loaded layouts.
880 					extLoadStatusFunc: function(status, hasLoadingFailed){
881 						if(hasLoadingFailed === true){
882 							//do nothing
883 						}
884 						else {
885 							++status.loadedLayoutsCount;
886 						}
887 					}
888 			};
889 
890 			/**
891 			 * HELPER object for loading/creating the layouts
892 			 * @private
893 			 * @memberOf mmir.MediaManager.init.loadLayouts
894 			 */
895 			var createLayoutConfig = {
896 					constructor: Layout,
897 					typeName: 'Layout',
898 					collection: _layouts
899 			};
900 
901 			/**
902 			 * helper for loading a single layout-file
903 			 * 
904 			 * @private
905 			 * @memberOf mmir.MediaManager.init.loadLayouts
906 			 */
907 			var doLoadLayout = function(index, ctrlName, theDefaultLayoutName){
908 
909 				var ctrlName;
910 				var layoutInfo;
911 				if(theDefaultLayoutName){
912 
913 					ctrlName = theDefaultLayoutName;
914 
915 					//create info-object for default-layout
916 					var layoutFileName = theDefaultLayoutName[0].toLowerCase() 
917 					+ theDefaultLayoutName.substring(1, theDefaultLayoutName.length);
918 					layoutInfo = {
919 						name: theDefaultLayoutName,
920 						fileName: layoutFileName,
921 						genPath: constants.getCompiledLayoutPath()//TODO add compiled-path to view-info object (and read it from file-structure/JSON) 
922 									+ layoutFileName + '.js',
923 						path: constants.getLayoutPath() + layoutFileName + '.ehtml'
924 					};
925 
926 				}
927 				else {
928 					var ctrl = controllerManager.getController( ctrlName );
929 					ctrlName = ctrl.getName();
930 					layoutInfo = ctrl.getLayout();
931 				}
932 
933 				if(layoutInfo){
934 
935 					doLoadTemplateFile(null, layoutInfo, createLayoutConfig, loadStatus);
936 
937 				}
938 
939 				--loadStatus.remainingCtrlCount;
940 				checkCompletion(loadStatus);
941 
942 			};//END: doLoadLayout(){...
943 
944 			//load the default layout:
945 			doLoadLayout(null, null, DEFAULT_LAYOUT_NAME);
946 
947 			//load layouts for controllers (there may be none defined)
948 			$.each(ctrlNameList, doLoadLayout);
949 
950 			checkCompletion(loadStatus);
951 			return defer.promise();
952 
953 		}//END: loadLayouts()
954 
955 		/**
956 		 * This function actually loads the views for every controller, creates
957 		 * an instance of a view class and puts the view instance in the
958 		 * <b>_views</b> array.<br>
959 		 * 
960 		 * @function
961 		 * @private
962 		 * @async
963 		 * @memberOf mmir.MediaManager.init
964 		 * 
965 		 * @returns {Promise} a Deferred.promise that gets resolved upon loading all views
966 		 * 
967 		 * @see doProcessTemplateList
968 		 */
969 		function loadViews() {
970 
971 			var creatorConfig = {
972 					constructor: View,
973 					typeName: 'View',
974 					collection: _views,
975 					keyGen: createViewKey,
976 					accessorName: 'getViews'
977 			};
978 
979 			return doProcessTemplateList(creatorConfig);
980 
981 		}//END: loadViews()
982 
983 		/**
984 		 * This function actually loads the partials for every controller,
985 		 * creates an instance of a partial class and puts the partial instance
986 		 * in the <b>_partials</b> array.<br>
987 		 * It uses a asynchronous way of loading the partials-files one after
988 		 * another.<br>
989 		 * <b>If you want to make sure, that all partials are indeed loaded,
990 		 * before proceeding with the subsequent instructions, you could look at
991 		 * the function
992 		 * {@link mmir.ControllerManager#foundControllersCallBack} for
993 		 * reference of a function which loads the files one after another - not
994 		 * asynchronously.</b>
995 		 * 
996 		 * @function
997 		 * @private
998 		 * @async
999 		 * @memberOf mmir.MediaManager.init
1000 		 * 
1001 		 * @returns {Promise} a Deferred.promise, that resolves after all partials have been loaded
1002 		 * 					NOTE: loading failures will generate a warning message (on the console)
1003 		 * 						  but will not cause the Promise to fail.
1004 		 */
1005 		function loadPartials() {
1006 
1007 			var creatorConfig = {
1008 					constructor: Partial,
1009 					typeName: 'Partial',
1010 					collection: _partials,
1011 					keyGen: createPartialKey,
1012 					accessorName: 'getPartials'
1013 			};
1014 
1015 			return doProcessTemplateList(creatorConfig);
1016 
1017 		}//END: loadPartials()
1018 
1019 		/**
1020 		 * HELPER for checking the loading status.
1021 		 * 
1022 		 * As long as the Deferred <code>status.loader</code> is
1023 		 * still pending, the loading status will be checked:
1024 		 * 
1025 		 * Depending on <code>status.currentLoadCount</code> and
1026 		 * <code>status.remainingCtrlCount</code> the completion
1027 		 * of the loading process is checked.
1028 		 * 
1029 		 * If loading is completed, the Deferred <code>status.loader</code>
1030 		 * will be resolved.
1031 		 * 
1032 		 * If OPTIONAL <code>status.loader</code> (Function) exists, intead of resolving
1033 		 * <code>status.loader</code>, this function is invoked in case of completion
1034 		 * with <code>status</code> as argument.
1035 		 * 
1036 		 * @private
1037 		 * @function
1038 		 * @memberOf mmir.MediaManager.init
1039 		 * 
1040 		 * @param {PlainObject} status
1041 		 * 		the object for managing the laoding status.
1042 		 * 
1043 		 */
1044 		var checkCompletion = function(status){
1045 			if(status.loader.state() === 'pending' && status.remainingCtrlCount === 0 && status.currentLoadCount === 0){
1046 
1047 				if(status.onCompletionImpl){
1048 					status.onCompletionImpl(status);
1049 				}
1050 				else {
1051 					status.loader.resolve();
1052 				}
1053 
1054 			}
1055 		};
1056 
1057 		/**
1058 		 * HELPER for updating the loading status.
1059 		 * 
1060 		 * Invokes {@link checkCompletion} with <code>status</code> as argument.
1061 		 * 
1062 		 * @private
1063 		 * @function
1064 		 * @memberOf mmir.MediaManager.init
1065 		 * 
1066 		 * @param {PlainObject} status
1067 		 * 		the object for managing the laoding status:
1068 		 * 		<code>status.currentLoadCount</code> (Integer): this property will be decreased by 1.
1069 		 * 											This value should initially be set to the count
1070 		 * 											of files, that will / should be loaded.
1071 		 * 		OPTIONAL <code>status.extLoadStatusFunc</code> (Function): if this property is set, the
1072 		 * 											function will be invoked with <code>status</code>
1073 		 * 											and <code>hasLoadingFailed</code> as arguments.
1074 		 * 
1075 		 * @param {Boolean} [hasLoadingFailed] OPTIONAL
1076 		 * 		if present and <code>true</code>: this indicates that the loading process for the current
1077 		 * 		template file (*.ehtml) has failed. NOTE that this is NOT used, when loading of a 
1078 		 * 		_compiled_ template file (*.js) fails!
1079 		 */
1080 		var updateLoadStatus = function(status, hasLoadingFailed){
1081 			--status.currentLoadCount;
1082 
1083 			if(status.extLoadStatusFunc){
1084 				status.extLoadStatusFunc(status, hasLoadingFailed);
1085 			}
1086 
1087 			checkCompletion(status);
1088 		};
1089 
1090 		/**
1091 		 * HELPER: creates a template-object (e.g. a View or a Partial) for the 
1092 		 *         raw template conent.
1093 		 *         
1094 		 *         If necessary, the parser-classes (module 'parseUtils') are loaded,
1095 		 *         which are necessary to process the raw template content.
1096 		 * 
1097 		 * @private
1098 		 * @function
1099 		 * @memberOf mmir.MediaManager.init
1100 		 * 
1101 		 * @param {Controller} controller
1102 		 * 		the controller to which the template files belong.
1103 		 * 		May be <code>null</code>: in this case, this argument will be omitted when
1104 		 * 		creating the template object and creating the lookup-key (e.g. in case of a Layout).
1105 		 * 
1106 		 * @param {String} templateName
1107 		 * 		the name for the template (e.g. file-name without extension)
1108 		 * 
1109 		 * @param {PlainObject} createConfig
1110 		 * 		configuration that is used to create the template-object
1111 		 * 		for the template-contents:
1112 		 * 		<code>createConfig.constructor</code>: the constructor function
1113 		 *                    IF controller IS NOT null: <code>(controller, templateName, templateContent)</code> 
1114 		 *                    							 (e.g. View, Partial)
1115 		 *                    IF controller IS null:     <code>(templateName, templateContent)</code>
1116 		 *                    							 (e.g. Layout)
1117 		 *                    
1118 		 * 		<code>createConfig.collection</code>: the Dictionary to which the created
1119 		 * 											 template-object will be added
1120 		 * 		<code>createConfig.keyGen</code>: a generator function for creating the lookup-key when adding
1121 		 * 										the template-object to the collection.
1122 		 * 										This function is invoked with <code>(controller.getName(), templateName)</code>,
1123 		 * 										that is <code>(String, String)</code>.
1124 		 * 										
1125 		 * 										NOTE: if controller IS null, the keyGen function will not be used, and
1126 		 * 											  instead the template-object will be added with the
1127 		 * 											  created template-object's name as lookup-key.
1128 		 * @param {PlainObject} status
1129 		 * 			the object for managing the loading status.
1130 		 * 			After creating and adding the template-object to the collection, the loading
1131 		 * 			status will be updated via {@link updateLoadStatus}
1132 		 * 
1133 		 */
1134 		var doParseTemplate = function(controller, templateName, config, templateContent, status){
1135 
1136 			//NOTE need to request renderUtils here too, since it is needed during parsing!
1137 			require(['parseUtils', 'renderUtils'], function(){
1138 
1139 				var templateObj;
1140 				if(controller){
1141 					//"normal" view constructor: (Controller, nameAsString, templateAsString)
1142 					templateObj = new config.constructor(controller, templateName , templateContent);
1143 					config.collection.put( config.keyGen(controller.getName(), templateName), templateObj );
1144 				}
1145 				else {
1146 					//in case of Layout: omit controller argument
1147 					// -> layout constructor: (nameAsString, templateAsString)
1148 					// -> there is a 1:1 correspondence betwenn controller and layout,
1149 					//    and Layout.name === Controller.name
1150 					//    => no need to create a lookup-key
1151 					templateObj = new config.constructor(templateName , templateContent);
1152 					config.collection.put( templateObj.getName(), templateObj );
1153 				}
1154 
1155 				updateLoadStatus(status);
1156 
1157 			});
1158 
1159 		};
1160 
1161 		/**
1162 		 * Generic helper for loading a list of template files (*.ehtml)
1163 		 * that correspond to a specific template type (e.g. <code>View</code>s, or <code>Partial</code>s).
1164 		 * 
1165 		 *  If compiled representations of the template file exist AND is up-to-date AND
1166 		 *  the configuration is set to load the compiled files rather than the raw template
1167 		 *  file (see <code>isUsePreCompiledViews</code>), then the compiled template is used.
1168 		 *  
1169 		 *  
1170 		 * This function uses an asynchronous method for loading the template-files.<br>
1171 		 * 
1172 		 * <b>If you want to make sure, that all templates have indeed been loaded, before
1173 		 * proceeding with the subsequent program flow, you should have a look at the
1174 		 * function {@link mmir.ControllerManager#foundControllersCallBack}: use the returned
1175 		 * Deferred.Promise for executing code that depends on the templates being loaded.</b>
1176 		 * 
1177 		 * <p>
1178 		 * Uses {@link doloadTemplateFile} for loading single template files.
1179 		 * 
1180 		 * @function
1181 		 * @private
1182 		 * @async
1183 		 * @memberOf mmir.MediaManager.init
1184 		 * 
1185 		 * @see #doLoadTemplateFile
1186 		 * 
1187 		 * @param {PlainObject} createConfig
1188 		 * 			configuration object that determines which templates are loaded, and how
1189 		 * 			the loaded data is processed.
1190 		 * 
1191 		 * 
1192 		 * @returns {Promise} a Deferred.promise, that resolves after all partials have been loaded
1193 		 * 					NOTE: loading failures will generate a warning message (on the console)
1194 		 * 						  but will not cause the Promise to fail.
1195 		 */
1196 		var doProcessTemplateList = function(createConfig){
1197 
1198 			/**
1199 			 * @type jQuery.Deferred
1200 			 * @private
1201 			 * @memberOf mmir.MediaManager.init.doProcessTemplateList
1202 			 */
1203 			var defer = $.Deferred();
1204 			
1205 			/**
1206 			 * @type String
1207 			 * @private
1208 			 * @memberOf mmir.MediaManager.init.doProcessTemplateList
1209 			 */
1210 			var ctrlNameList = controllerManager.getControllerNames();
1211 			
1212 			/**
1213 			 * HELPER object for tracking the loading-status of the views
1214 			 * 
1215 			 * @private
1216 			 * @memberOf mmir.MediaManager.init.doProcessTemplateList
1217 			 */
1218 			var loadStatus = {
1219 					loader: defer,
1220 					remainingCtrlCount: ctrlNameList.length,
1221 					currentLoadCount: 0
1222 			};
1223 
1224 			$.each(ctrlNameList, function(ctrlIndex, controllerName){
1225 
1226 				var controller = controllerManager.getController(controllerName); 
1227 
1228 				$.each(controller[createConfig.accessorName](), function(index, templateInfo){
1229 
1230 					doLoadTemplateFile(controller, templateInfo, createConfig, loadStatus);
1231 
1232 				});//END: each(templateInfo)
1233 
1234 				-- loadStatus.remainingCtrlCount;
1235 				checkCompletion(loadStatus);
1236 
1237 			});//END: each(ctrlName)
1238 
1239 			checkCompletion(loadStatus);
1240 			return defer.promise();
1241 
1242 		};//END: doProcessTemplateList()
1243 
1244 		/**
1245 		 * HELPER that loads a single template file asynchronously and creates a corresponding template-class instance
1246 		 * (depending on <code>createConfig</code>).
1247 		 * 
1248 		 * The <code>status</code> is updated on successful loading or on error (see {@link updateLoadStatus}).
1249 		 * 
1250 		 * @example
1251 		 * 
1252 		 * //EXAMPLE for createConfig for loading template contents into a Partial
1253 		 * var theCreateConfig = {
1254 		 *   constructor: Partial,        // the class constructor that takes the loaded template data
1255 		 *   typeName: 'Partial',         // the name of the class that will be created
1256 		 *   collection: _partials,        // the map/dictionary to which the created class-instance will be added
1257 		 *   keyGen: createPartialKey,    // the function for creating the lookup-key (for the dictionary)
1258 		 *   accessorName: 'getPartials'  // the accessor-function's name for accessing the info-objects on the controller-instance
1259 		 * };
1260 		 * doLoadTemplateFiles(theCreateConfig).then(function(){
1261 		 * 	//do something that depends on loading of the template files... 
1262 		 * });
1263 		 * 
1264 		 * //EXAMPLE for createConfig for loading template contents into a Layout
1265 		 * var theCreateLayoutConfig = {
1266 		 *   constructor: Layout,        // the class constructor that takes the loaded template data
1267 		 *   typeName: 'Layout',         // the name of the class that will be created
1268 		 *   collection: _layouts,        // the map/dictionary to which the created class-instance will be added
1269 		 * };
1270 		 * 
1271 		 * doLoadTemplateFiles(theCreateLayoutConfig).then(function(){
1272 		 * 	//do something that depends on loading of the template files... 
1273 		 * });
1274 		 * 
1275 		 * //for createConfig for loading template contents into a Partial
1276 		 * 
1277 		 * @param {Controller} controller
1278 		 * 		the controller to which the template files belong.
1279 		 * 		May be <code>null</code>: see {@link doParseTemplate}.
1280 		 * 
1281 		 * @param {PlainObject} templateInfo
1282 		 * 		the JSON-like object containing information for the template
1283 		 * 		(e.g. <code>name</code>, <code>file-path</code> etc.; see
1284 		 * 		 {@link mmir.ControllerManager#getControllerResources} for 
1285 		 *          more information).
1286 		 *          
1287 		 * @param {PlainObject} createConfig
1288 		 * 		configuration that is used to create a corresponding template-object
1289 		 * 		for the loaded template-contents.
1290 		 * 		The created object will be added to <code>createConfig.collection</code>
1291 		 * 		(Dictionary; with controller's name as key).
1292 		 * 
1293 		 * @param {PlainObject} loadStatus
1294 		 * 		Object for managing the loading-status. The status is updated and used to
1295 		 * 		determine, if all templates (e.g. from a list) have been (asynchronously)
1296 		 * 		loaded.
1297 		 * 
1298 		 * @function
1299 		 * @private
1300 		 * @async
1301 		 * @memberOf mmir.MediaManager.init
1302 		 */
1303 		var doLoadTemplateFile = function(controller, templateInfo, createConfig, loadStatus){
1304 			++loadStatus.currentLoadCount;
1305 
1306 			$.ajax({
1307 				async: true,
1308 				dataType: "text",
1309 				url: templateInfo.path,
1310 				success: function(data){
1311 
1312 					if(isUsePreCompiledViews){
1313 
1314 						loadPrecompiledView(data, templateInfo.genPath, function(){
1315 
1316 							updateLoadStatus(loadStatus);
1317 
1318 						}, function(err){
1319 
1320 							console.warn('Could not load precompiled '+createConfig.typeName+' from '
1321 									+templateInfo.genPath+'", because: '+err
1322 									+', compiling template instead: '
1323 									+templateInfo.path
1324 							);
1325 
1326 							doParseTemplate(controller, templateInfo.name, createConfig , data, loadStatus);
1327 
1328 						});
1329 
1330 					}
1331 					else {
1332 
1333 						doParseTemplate(controller, templateInfo.name, createConfig, data, loadStatus);
1334 
1335 					}
1336 				}
1337 
1338 			}).fail(function(jqxhr, status, err){
1339 
1340 				// print out an error message
1341 				var errMsg = err && err.stack? err.stack : err;
1342 				console.error("[" + status + "] Could not load '" + templateInfo.path + "': "+errMsg); //failure
1343 
1344 				updateLoadStatus(loadStatus, true);
1345 			});
1346 
1347 			checkCompletion(loadStatus);
1348 
1349 		};//END: doLoadTemplateFile()
1350 
1351 		
1352 		///////////// start intialization: ////////////////
1353 
1354 		/**
1355 		 * Deferred / promise for loading views.
1356 		 * 
1357 		 * @type jQuery.Deferred
1358 		 * @private
1359 		 * @memberOf mmir.MediaManager.init
1360 		 */
1361 		var defer = $.Deferred();
1362 
1363 		var isLayoutsLoaded = false;
1364 		var isViewsLoaded = false;
1365 		var isPartialsLoaded = false;
1366 		var isViewEngineLoaded = false;//MOD modularize view-engine jqm
1367 		
1368 		/**
1369 		 * Helper: called each time a loading-function finishes.
1370 		 * Checks if all other loading-functions have finished, and if so, resolves the init-promise.
1371 		 * 
1372 		 * @private
1373 		 * @memberOf mmir.MediaManager.init
1374 		 */
1375 		var checkResolved = function(){
1376 			if(isLayoutsLoaded && isViewsLoaded && isPartialsLoaded && isViewEngineLoaded){
1377 				defer.resolve();
1378 			}
1379 		};
1380 		/**
1381 		 * Helper: called if an error occured in one of the loading-functions:
1382 		 * rejects/fails the init-promise.
1383 		 * 
1384 		 * @private
1385 		 * @memberOf mmir.MediaManager.init
1386 		 */
1387 		var failPromise = function(msg){
1388 			defer.reject(msg);
1389 		};
1390 
1391 		//util for checking if pre-compiled views are up-to-date
1392 		// (i.e.: can we use the pre-compiled view, or do we need to use the template file and compile it on-the-fly)
1393 		//TODO should this also be configurable -> up-to-date check (e.g. use pre-compiled views without checking for changes)
1394 		checksumUtils = checksumUtils.init();
1395 		
1396 		loadLayouts().then(
1397 				function(){ isLayoutsLoaded = true; checkResolved(); },
1398 				failPromise
1399 		);
1400 		loadViews().then(
1401 				function(){ isViewsLoaded = true; checkResolved(); },
1402 				failPromise
1403 		);
1404 		loadPartials().then(
1405 				function(){ isPartialsLoaded = true; checkResolved(); },
1406 				failPromise
1407 		);
1408 		
1409 		//MOD modularize view-engine jqm: load viewEngine (default is based on jQuery Mobile)
1410 		require([core.viewEngine], function(viewEngineInit){
1411 			viewEngineInit.then(
1412 				function(viewEngine){
1413 					_instance.setRenderEngine(viewEngine);
1414 					isViewEngineLoaded = true;
1415 					checkResolved();
1416 				}, failPromise
1417 			);
1418 		});
1419 
1420 		return defer.promise(_instance);
1421 
1422 	};//END: init()
1423 	
1424 });
1425