Source: tools/commonUtils.js


define(['mmirf/resources','mmirf/util/deferred','mmirf/util/loadFile','mmirf/util/isArray','mmirf/paramsParseFunc','mmirf/logger', 'module', 'require'],
	/**
	 * A Utility class to support various functions.<br>
	 *
	 *
	 * @class mmir.CommonUtils
	 * @name mmir.CommonUtils
	 * @static
	 * @hideconstructor
	 *
	 * @public
	 *
	 * @requires Resources (optionally: jQuery)
	 * @requires mmir.SemanticInterpreter (in {@link mmir.CommonUtils#loadCompiledGrammars})
	 *
	 * @requires util/isArray
	 * @requires util/deferred	 in #loadImpl, #loadDirectoryStructure, #setToCompatibilityMode
	 * @requires util/loadFile	 in #loadDirectoryStructure
	 *
	 *
	 * @example var isList = mmir.CommonUtils.isArray(list);
	 *
	 */
	function(
		resources, deferred, loadFile, _isArray, paramsParseFunc, Logger, module, require
) {
	/** @scope mmir.CommonUtils.prototype *///for jsdoc2

	/**
	 * @private
	 * @type CommonUtils
	 * @memberOf mmir.CommonUtils#
	 */
	var instance = null;

	/**
	 * @private
	 * @type mmir.tools.Logger
	 * @memberOf mmir.CommonUtils#
	 */
	var logger = Logger.create(module);

	/**
	 * @private
	 * @type mmir.tools.Logger
	 * @memberOf mmir.CommonUtils#
	 */
	var _conf = module.config(module);

	/**
	 * JSON-Object containing the directory Structure of the application. Only
	 * directories defined by the Property
	 * {@link CommonUtils-constructor-directoriesToParse} are contained
	 * within the JSON-Object.
	 *
	 * @type JSON
	 * @private
	 * @memberOf mmir.CommonUtils#
	 */
	this.directoryStructure;

	/**
	 * Constructor-Method of Class {@link mmir.CommonUtils}
	 *
	 * @param {Resources} res
	 *            the resources-provider (e.g. URL for base directory etc)
	 *
	 * @constructs mmir.CommonUtils
	 * @memberOf mmir.CommonUtils#
	 * @function
	 * @private
	 */
	function constructor(res) {
		// private members.

		/**
		 * The Prefix for the file names of partial-files.<br>
		 * Files named &lt;PARTIAL-PREFIX&gt;filename.ehtml inside a
		 * views-directory are recognized as partials.
		 *
		 * @type String
		 * @private
		 */
		var partialsPrefix = '~';

		/**
		 * Protocol / prefix that is used URIs to indicate to load scripts via require()
		 * instead of loading via &lg;script&gt; tags:
		 *
		 * @type String
		 * @private
		 * @example
		 *
		 * mmir.util.loadScript('require://controllers/calendar', function(...
		 * ->
		 * mmir.require(['controllers/calendar'], function(...
		 */
		var reqProtocol = 'require://';

		/**
		 * RegExp for testing if URL starts with {@link #reqProtocol}
		 *
		 * @type RegExp
		 * @private
		 */
		var testReq = new RegExp('^'+reqProtocol.replace('/', '\\/'));

		// /**
		//  * Array of Directories (Strings) to parse at the starting process<br>
		//  * those directories are then accessable by the functions
		//  * {@link mmir.CommonUtils#listDir}
		//  *
		//  * TODO read from properties (implement mechanism such that
		//  * \build.settings and this refer to the same resource)
		//  *
		//  * @type Array
		//  * @private
		//  */
		// var directoriesToParse = [
		// 	"controllers",
		// 	"views",
		// 	"models",
		// 	"config",
		// 	"helpers"
		// ];

		/** @lends mmir.CommonUtils.prototype */
		return {

			/**
			 * Helper function for
			 * {@link mmir.CommonUtils#listDir}
			 * to clear-up/normalize the pathname parameter
			 *
			 * @function
			 * @private
			 * @param {string}
			 *            pathname The path that should be stripped of "file://" and a
			 *            beginning or trailing "/"
			 * @returns {String} The stripped pathname - devoid of beginning "file://"
			 *          or "/" and trailing "/"
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			stripPathName: function(pathname) {

				// FIXME this is a HACK; TODO handle this in a general way!
				var basePath = res.getBasePath();

				if(basePath){
					//helper: check if string starts with basePath (case-sensitive)
					var re = new RegExp('^'+basePath);
					if (re.test(pathname)) {
						pathname = pathname.substring(basePath.length);
					}
				}

				if (pathname.indexOf("file://") !== -1) {
					pathname = pathname.replace("file://", "");
				}
				if (pathname[pathname.length - 1] === "/") {
					pathname = pathname.substring(0, pathname.length - 1);
				}
				if (pathname[0] !== "/") {
					pathname = "/" + pathname;
				}

				return pathname;
			},

			// public members.
			/**
			 * @function
			 * @public
			 * @returns {String} The Prefix for the file names of partial-files
			 * @memberOf mmir.CommonUtils.prototype
			 */
			getPartialsPrefix : function() {
				return partialsPrefix;
			},

			/**
			 * @function
			 * @public
			 * @returns {Object} Directory structure as json object
			 * @memberOf mmir.CommonUtils.prototype
			 */
			getDirectoryStructure : function() {
				return this.directoryStructure;
			},
			/**
			 * extracts all the strings from a String-Array into a single string
			 *
			 * @function
			 * @public
			 * @returns {string} text
			 * @memberOf mmir.CommonUtils#
			 */
			concatArray : function(array) {
				return array.join(', ');
			},
			/**
			 * Regular Expression for matching HTML comments.<br>
			 *
			 * This RegExp also matches multi-line comments.
			 *
			 * Note upon using the RegExp that it does not consider if a HTML
			 * comment is specified within a String or data-definition (i.e. the
			 * comment is matched regardless were its defined).
			 *
			 * @type String|RegExp
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 *
			 * @example <!-- some comment -->
			 */
			regexHTMLComment : /<!--([\r\n]|.)*?-->/igm,

			/**
			 * Protocol / prefix that is used URIs to indicate to load scripts via require()
			 * instead of loading via &lg;script&gt; tags:
			 *
			 * @type String
			 * @private
			 * @example
			 * mmir.util.loadScript('require://controllers/calendar', function(...
			 * ->
			 * mmir.require(['controllers/calendar'], function(...
			 */
			requireProtocol: reqProtocol,

			/**
			 * Checks <code>uri</code> for "require://" protocol:
			 *
			 * If require-protocol, does <code>require()</code> the resource
			 * <pre>
			 * "require://controller/application" -> require('controller/application')
			 * </pre>
			 *
			 * Otherwise loads as URL via {@link #getLocalScript}.
			 *
			 * @function
			 * @async
			 * @param {String} uri
			 *            the URI for the script: either require-recouse or file path/URL
			 * @param {Function}
			 *            success success callback function
			 * @param {Function}
			 *            fail fail callback function
			 *
			 * @see #getLocalScript
			 * @memberOf mmir.CommonUtils.prototype
			 */
			loadScript : function(uri, success, fail) {

				if(testReq.test(uri)){
					var reqUri = uri.substring(reqProtocol.length);
					if(typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD){
						try {
							var expModule = __webpack_require__(reqUri);
							if(success){
								setTimeout(function(){success.call(instance, expModule)}, 0);
							} else {
								logger.debug('CommonUtils', 'loadScript', 'Successfully required script from ' + uri);
							}
						} catch(e) {
							if(fail){
								setTimeout(function(){fail.call(instance, e)}, 0);
							} else {
								logger.error('CommonUtils', 'loadScript', 'Requiring script failed for "' + uri + '"', e);
							}
						}
						return;
					} else {
						require([reqUri],
							function(expModule){success? success.call(instance, expModule) : logger.debug('CommonUtils', 'loadScript', 'Successfully required script from ' + uri)},
							function(err){fail? fail.call(instance, err) : logger.error('CommonUtils', 'loadScript', 'Requiring script failed for "' + uri + '"', e)}
						);
						return;
					}
				}

				return this.getLocalScript(uri, success, fail);
			},

			/**
			 * Get the file path/name for a compiled grammar (executable JavaScript grammars).
			 *
			 * @function
			 * @param {String} generatedGrammarsPath Path of the grammars which should be loaded, e.g. <b>gen/grammar/</b>
			 * @param {String} grammarId the ID (e.g. language code) for the grammar
			 * @param {Boolean} [isFileNameOnly] OPTIONAL
			 * 					if TRUE then only the file name will be returned, otherwise the full path is returned
			 *
			 * @returns {String} file path / name for the compiled grammar
			 *                   (returns an empty string, if there is no compile grammar for the specified grammar ID)
			 *
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			getCompiledGrammarPath : function(generatedGrammarsPath, grammarId, isFileNameOnly) {
				var files = instance.listDir(generatedGrammarsPath, /\.js$/i);//get *.js files
				if(!files){
					return '';
				}
				var f, index, id;
				for(var i=0,size=files.length; i < size; ++i){
					f = files[i];
					index = f.lastIndexOf('.');
					if (index !== -1) {
						id = f.substring(0, index);
						if(id === grammarId){
							return isFileNameOnly || (typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD)? files[i] : generatedGrammarsPath + files[i];
						}
					}
				}
				return '';
			},

			/**
			 * Get the IDs of compiled resources
			 *
			 * @function
			 * @param {String} compiledResourcesPath Path for the resources, e.g. <b>gen/grammar/</b>
			 *
			 * @returns {Array<String>} list of IDs for the compiled resources
			 *
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			getCompiledResourcesIds : function(compiledResourcesPath) {
				var files = instance.listDir(compiledResourcesPath, /\.js$/i);//get *.js files
				var ids = [];
				if(!files){
					return ids;
				}
				var f, m, re = typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? /^mmirf\/.+\/([^/]+)\.js$/i : /(.+)\.js$/i;
				for(var i=0,size=files.length; i < size; ++i){
					f = files[i];
					m = re.exec(f);
					if(m){
						ids.push(m[1]);
					}
				}
				return ids;
			},
			/**
			 * Load all compiled grammars (executable JavaScript grammars).
			 *
			 * @function
			 * @param {String} generatedGrammarsPath Path of the grammars which should be loaded, e.g. <b>gen/grammar/</b>
			 * @param {Function} cbFunction The function that should be executed after the plugins are loaded.
			 * 					 If the execution of following functions is dependent on the presence of the grammars,
			 * 					 they should be triggered from inside the callback-function.
			 * @param {Array<String>} [ignoreGrammarIds] OPTIONAL
			 * 					grammar IDs that should be ignored, i.e. not loaded, even if there is a file available
			 *
			 * @returns {Promise} a deferred promise (see loadImpl())
			 *
			 * @requires mmir.SemanticInterpreter (must be loaded as dependency "mmirf/semanticInterpreter" at least once before this function is loaded)
			 *
			 * @async
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			loadCompiledGrammars : function(generatedGrammarsPath, cbFunction, ignoreGrammarIds) {

				return instance.loadImpl(
					generatedGrammarsPath,
					false,
					cbFunction,
					function isGrammarAlreadyLoaded(grammarFileName) {
						// "file name" for webpack is "mmirf/grammar/<grammar ID>", otherwise the file-name is "<grammar ID>.js"
						var m = (typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD? /\/?([^/]+)$/ : /^(.+)\.js$/i).exec(grammarFileName);
						if (m) {
							var id = m[1];
							if(ignoreGrammarIds){
								for(var p in ignoreGrammarIds){
									if(ignoreGrammarIds.hasOwnProperty(p) && ignoreGrammarIds[p] == id){
										return true;
									}
								}
							}
							return require('mmirf/semanticInterpreter').hasGrammar(id);
						} else {
							return false;
						}
					},
					function loadCompiledGrammarsStatus(status, fileName, msg) {
						if (status === 'info') {
							if(logger.isInfo()) logger.info('CommonUtils', 'loadCompiledGrammars', 'loaded "'+ fileName + '": ' + msg);
						}
						else if (status === 'warning') {

							//filter "already loaded" warnings for ignored files:
							if(ignoreGrammarIds && /already loaded/.test(msg)){
								for(var p in ignoreGrammarIds){
									if(ignoreGrammarIds.hasOwnProperty(p) && fileName.indexOf(ignoreGrammarIds[p]) === 0){
										return;/////////////////////// EARLY EXIT ////////////////
									}
								}
							}

							if(logger.isWarn()) logger.warn('CommonUtils', 'loadCompiledGrammars', 'loading "'+ fileName + '": ' + msg);
						}
						else if (status === 'error') {
							logger.error('CommonUtils', 'loadCompiledGrammars', 'loading "' + fileName + '": ' + msg);
						}
						else {
							logger.error('CommonUtils', 'loadCompiledGrammars', status + ' (UNKNOWN STATUS) -> "' + fileName + '": ' + msg);
						}
					}
				);

			},

			/**
			 * Load implementation files (i.e. JavaScript files) from a directory (if <tt>librariesPath</tt> is a String) or
			 * or a list of files-names (if <tt>librariesPath</tt> is an Array of Strings).
			 *
			 *
			 *
			 * @function
			 * @param {String|Array<String>} librariesPath
			 * 				Path (or list of  of the plugins which should be loaded, e.g. <b>mmirf/plugins/</b>
			 * 				NOTE: The (String) path must be an entry in directories.json!
			 *                    (directories.json is used to generate/"query" the file-list for the path)
			 *
			 * @param {Boolean} isSerial
			 * 				Set <code>true</code> if the libraries should be loaded serially, i.e. synchronously, that is "one after the other" (later ones may depend on earlier ones).
			 * 				set <code>false</code> if libraries should be loaded in parallel, i.e. "asychronously" (NOTE in this case, the libraries must not depend on each other).
			 *
			 * 				NOTE: The loading process as a hole is asynchronous (regardless of parameter <tt>isSerial</tt>),
			 * 				      i.e. loading is completed when <tt>completedCallback()</tt> is invoked,
			 * 				      NOT when this function returns!
			 *
			 * @param {Function} [completedCallback]
			 * 				The function that should be executed after the libraries are loaded.
			 * 				If the execution of following functions is dependent on the presence of the libraries,
			 * 				they should be capsuled inside this callback-function.
			 * @param {Function} [checkIsAlreadyLoadedFunc]
			 * 				If provided, this function checks (based on the file-name), if the library is already
			 * 				loaded.
			 * 				The signature for the callback is  <code>checkIsAlreadyLoadedFunc(String fileName) return [true|false]</code>,
			 * 				i.e. the function may check - based on the file-name - if the library is already loaded.
			 * 				If the function returns <tt>true</tt>, the library will not be loaded, and loading continues
			 * 				with the next library-file.
			 *
			 * 				NOTE: if <tt>isSerial</tt> is <tt>false</tt>, libraries with lower indices in the list may
			 * 				      still be loading, when later entries are checked with this callback. In consequence,
			 * 				      the "is already loaded"-check may not be accurate, in case parallel loading is
			 * 				      used and the library-list contains "duplicate" entries.
			 * @param {Function} [statusCallback]
			 * 				If provided, this function is invoked, when a library was loaded loaded (INFO) or an
			 * 				error occurs.
			 * 				The signature for the callback is
			 * 				<code>statusCallback(String statusLevel, String fileName, String message, [result])</code>
			 * 				where <tt>statusLevel</tt> is one of <tt>info, warning, error</tt>,
			 * 				      <tt>fileName</tt> is the file-name for the library that this status message concerns, and
			 * 				      <tt>message</tt> is a message text with details concerning the status, and OPTIONALLY
			 * 				      <tt>result</tt> is the result (may be undefined if no result is returned; the actual value may depend on the execution environment)
			 *
			 * @returns {Promise} a deferred promise that will be fulfilled when loadImpl() has finished.
			 *
			 * @async
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			loadImpl: function (librariesPath, isSerial, completedCallback, checkIsAlreadyLoadedFunc, statusCallback){

				var _defer = deferred();

				if(completedCallback){
					_defer.then(completedCallback, completedCallback);
				}

				var isIds = false;//<- interpret file-entries as IDs (or as file-names, i.e. "as-is")
				var theFileList;
				if(typeof librariesPath === 'string'){
					theFileList = instance.listDir(librariesPath, /\.js$/ig) || [];//get *.js files
					if(typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD){
						isIds = true;
						librariesPath = reqProtocol;
						theFileList = theFileList.map(function(file){ return file.replace(/\.js$/ig, '')});
					} else {
						isIds = true;
						theFileList = theFileList.map(function(file){
							var m;
							if(m = /^mmirf\/(.+)\/([^/]+)$/.exec(file)){
								return reqProtocol + librariesPath + m[2].replace(/\.js$/ig, '');
							} else {
								return librariesPath + file;
							}
						});
						librariesPath = '';
					}
				}
				else {
					theFileList = librariesPath;
					librariesPath = '';
				}

				var size = theFileList.length;
				var progress = 0;

				var doLoadImplFile = function doLoadImplFile(fileList, index){

					if( ! index){
						index = 0;
					}

					var fileName = fileList[index];

					//handler that is invoked after file has been processed (loaded or omitted):
					var handleScriptDone = function(){
						//"notify" that this file has been DONE:
						++progress;

						//check: are all entries of the list done?
						if (progress < size){

							if( isSerial ){
								//synchronous load: load next entry recursively, when previous, i.e. this, one has finished:
								doLoadImplFile(fileList, index+1);
							}
							//all entries already have been processed -> stop now.
							return;
						}

						//ASSERT: all entries of the file-list are DONE -> trigger completedCallback by resolving deferred
						_defer.resolve();
					};

					var displayFileName = fileName;
					if(isIds){
						displayFileName = fileName.replace(/^.+\//, '');
					}

					if ( checkIsAlreadyLoadedFunc && checkIsAlreadyLoadedFunc(displayFileName) ){

						if(statusCallback){
							statusCallback('warning', displayFileName, 'already loaded ' + librariesPath+displayFileName);
						}

						handleScriptDone();

					} else {

						instance.loadScript(librariesPath + fileName,
							function(result){

								if(statusCallback){
									statusCallback('info', displayFileName, 'done loading ' + librariesPath+displayFileName, result);
								}

								handleScriptDone();
							},
							function(exception) {

								if(statusCallback){
									statusCallback('error', displayFileName, 'could not load "' + librariesPath+displayFileName + '": ' + exception);
								}
								else {
									// print out an error message
									logger.error('[loadImpl] Could not load "' + librariesPath+displayFileName + '": ', exception);
								}

								//NOTE: in case of an error, will still try to load the other files from the list:

								handleScriptDone();
							}
						);//END: localScript(callbacks)
					}
				};//END: doLoadImplFile(name,index)

				if(logger.isVerbose()) logger.verbose('about to load all libraries from path "'+librariesPath+'"...');

				if(size < 1){
					//if there are no files to resolve:
					// immediately resolve Promise / trigger callback
					_defer.resolve();
				}
				else if( ! isSerial){
					//asynchronous load: trigger loading for all at once:
					for(var counter=0; counter < size; ++counter){
						doLoadImplFile(theFileList, counter);
					}
				}
				else {
					//synchronous load: start with first (the next one will be loaded recursively, when the first one was loaded)
					doLoadImplFile(theFileList);
				}

				return _defer;
			},

			/**
			 * Detects via the user-agent-string if the application is running
			 * on Android.
			 *
			 * @function
			 * @public
			 * @returns {Boolean} <b>True</b> if application is running on
			 *          Android, <b>False</b> otherwise
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			isRunningOnAndroid : function() {
				// Testing if user-Agent-/ or appVersion-String contains 'android'
				if ((navigator.userAgent.toLowerCase().indexOf("android") > -1)
					|| (navigator.appVersion.toLowerCase().indexOf("android") > -1)) {

					return true;
				}
				else {
					return false;
				}
			},

			/**
			 * Should detect - via the user-agent-string - if the application is
			 * running on Android, Symbian or iOS; in other words: on a
			 * smartphone.
			 *
			 * @function
			 * @public
			 * @returns {Boolean} <b>True</b> if application is running on
			 *          smartphone, <b>False</b> otherwise
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			isRunningOnSmartphone : function() {
				// Testing if user-Agent-/ or appVersion-String contains
				// 'Android' or 'iOS'
				// at the moment only Android-, iOS and Symbian-strings are 'implemented'
				var testString = navigator.userAgent.toLowerCase()
									+ navigator.appVersion.toLowerCase();

				if ((testString.indexOf("android") > -1)
					|| (testString.indexOf("ios") > -1)
					|| (testString.indexOf("symbian") > -1)) {

					return true;
				}
				else {
					return false;
				}
			},

			/**
			 *
			 * Similar to the jQuery.getScript() function - appending a &lt;script&gt;
			 * element for javascript-source to the header of the main document.
			 *
			 * The success-callback is invoked if the script was
			 * successfully loaded, otherwise the fail-callback.
			 *
			 * @function
			 * @param {String}
			 *            scriptUrl source of javascript-file
			 * @param {Function}
			 *            success success callback function
			 * @param {Function}
			 *            fail fail callback function
			 * @async
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			getLocalScript : function(scriptUrl, success, fail) {

				var head = document.getElementsByTagName('head')[0];
				var script = document.createElement('script');
				script.type = 'text/javascript';
				script.src = scriptUrl;
				script.onload = function(evt) {
					if(success){
						success.call(instance, evt);
					} else {
						logger.debug('CommonUtils', 'getLocalScript', 'Successfully loaded script from ' + scriptUrl)
					}
				};
				script.onerror = function(e) {
					if(fail){
						fail.call(instance, e);
					} else {
						logger.error('CommonUtils', 'getLocalScript', 'Loading script failed for "' + scriptUrl + '"', e);
					}
				};
				head.appendChild(script);
			},

			/**
			 * This function returns an array of strings (file names) with the contents of
			 * the directory <code>pathname</code>.
			 *
			 * The <code>pathname</code> must be one of the directories (or sub-directories)
			 * of the framework's parsed folders, see {@link #directoriesToParse}.
			 *
			 * If a <code>filter</code> is use, only files which's names match
			 * the filter are included in the returned list.
			 *
			 * @function
			 * @param {String} pathname
			 *            Path of the directory which's contents should be
			 *            returned
			 * @param {RegExp|Function} [filter]
			 *            Filter for file-names:
			 *              if <code>String</code> the file-name or part of the file-name (no wild cards!)
			 *                (comparison is <b>not case-sensitive</b>),
			 *                e.g.: <b>someFile.js</b>, <b>.js</b> or <b>myview.ehtml</b>
			 *              if <code>RegExp</code> the file-name must match the regular expression,
			 *                e.g.: <b>/.*\.js/ig</b> or <b>/^.*\.ehtml$/ig</b>
			 *              if <code>Function</code> the file-name is included, if the function returns <code>true</code>,
			 *                where the function signature is <code>function(fileName: String) : Boolean</code>,
			 *                note that argument <code>fileName</code> will have been transformed to lower-case characters
			 *
			 * @public
			 * @returns {Array} Array of Strings which contains the contents of
			 *          the directory.
			 *          Or <code>null</code>, if <code>pathname</code> is not one of the framework's
			 *          parsed folders.
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			listDir : function(pathname, filter) {

				pathname = this.stripPathName(pathname);

				var tmp = this.directoryStructure[pathname];
				if (typeof tmp === 'undefined') {

					logger.debug('CommonUtils', 'listDir', 'path "' + pathname + '" not found.');

					return null;////////////////// EARLY EXIT ///////////////////////////////
				} else if(!filter){
					return tmp;////////////////// EARLY EXIT ///////////////////////////////
				}

				var retValue = [];
				var isFunc = typeof filter === 'function';
				var strFilter = typeof filter === 'string' && filter.toLowerCase();

				var isMatch, entry;
				for (var i = 0, size = tmp.length; i < size; ++i) {

					entry = tmp[i];

					if(strFilter){
						isMatch = entry === filter || (entry && entry.toLowerCase().indexOf(strFilter) !== -1);
					} else if(!isFunc){
						//reset search-position for RegExp
						filter.lastIndex = 0;
						isMatch = filter.test(entry);
					} else {
							isMatch = filter(entry);
					}

					if(isMatch) {
						retValue.push(entry);
					}
				}
				return retValue;
			},

			/**
			 * Checks if an object is an <code>Array</code>.
			 *
			 * <p>
			 * This function can be safely run in arbitrary contexts, e.g.
			 *
			 * <pre>
			 *  var checkArray = mmir.CommonUtils.isArray;
			 *  if( checkArray(someObject) ){
			 *   ...
			 * </pre>
			 *
			 * @function
			 * @param {Object}
			 *            object the Object for checking if it is an Array
			 * @public
			 * @returns {Boolean} <code>true</code> if <code>object</code>
			 *          is an <code>Array</code>, otherwise
			 *          <code>false</code>.
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			isArray : function(object) {
				return _isArray(object);
			},

			/**
			 *
			 * IMPORTED FROM paramsParseFunc.js
			 * <p>
			 *
			 * Convert parameter-part of an URL to a "dictionary", containing
			 * the parameter keys and values
			 * <p>
			 *
			 * 	<code>?id=5&name=heinz&name=kunz</code> &rarr;
			 * 	<pre>
			 * 	{
			 * 	  id: "5",
			 * 	  name: ["heinz", "kunz"],
			 *
			 * 	  //utility functions
			 * 	  has: function(key) : Boolean,
			 * 	  isMultiple: function(key) : Boolean,// is entry an Array of values
			 * 	  getKeys: function() : String[],     // get list of all keys
			 * 	}
			 * 	</pre>
			 * <p>
			 *
			 * The returned "dictionary" has the following functions:
			 * <ul>
			 * <li>has(String key): returns <code>true</code> if the
			 * dictionary contains an entry for <code>key</code></li>
			 * <li>isMultiple(String key): returns <code>true</code> if the
			 * entry for <code>key</code> is an Array (i.e. the URL contained
			 * multiple values for this key)</li>
			 * <li>getKeys(): returns an Array with the keys (String) for all
			 * entries</li>
			 * </ul>
			 *
			 * @function
			 * @param {String} urlParamsPartStrings
			 *            the parameter-part of the URL, i.e. <code>&...</code>
			 * @return {Object} a "dictionary" for the parameters
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			parseParamsToDictionary : paramsParseFunc,

			/**
			 * This function is used check whether a network connection is
			 * enabled. </br> This version of checking the network connection is
			 * based on the cordova 2.3.0 API.
			 *
			 * TODO implement with HTML5 functions (in addition to / instead of
			 * cordova)?
			 *
			 * @requires Cordova: org.apache.cordova.network-information
			 *
			 * @function
			 * @public
			 * @returns {Boolean} <code>true</code> if a network connection is enabled
			 *
			 * @memberOf mmir.CommonUtils.prototype
			 */
			checkNetworkConnection : function() {

				if(logger.isVerbose()) logger.verbose("Checking network status...");

				if(typeof navigator === 'undefined'){
					logger.error('Cannot check network status: navigator object is not available!');
					return 'UNKNOWN';
				}

				//ASSERT: navigator exists

				if(!navigator.connection || !navigator.connection.type || typeof Connection === 'undefined'){
					if(logger.isInfo()) logger.info('Cannot use Cordova plugin network-information for checking network status: object navigator.connection is not available');
					if(typeof navigator.onLine !== 'undefined'){
						return navigator.onLine;
					}
					else {
						return 'UNKNOWN';
					}
				}
				var networkState = navigator.connection.type;

				//TODO make states-obj a 'private' field of CommonUtils
				var states = {};
				states[Connection.UNKNOWN]  = 'Unknown connection';
				states[Connection.ETHERNET] = 'Ethernet connection';
				states[Connection.WIFI]     = 'WiFi connection';
				states[Connection.CELL_2G]  = 'Cell 2G connection';
				states[Connection.CELL_3G]  = 'Cell 3G connection';
				states[Connection.CELL_4G]  = 'Cell 4G connection';
				states[Connection.CELL]     = 'Cell generic connection';
				states[Connection.NONE]     = 'No network connection';

				if (Connection.NONE === networkState){
					//alert('Connection type: ' + states[networkState]);
					return false;
				}
				return true;
			},

			/**
			 * Parses the directory structure and stores the result in the class-property {@link mmir.CommonUtils-directoryStructure}
			 *
			 * @function
			 * @param {Function} [success] The function that should be executed after the diretories are parsed - it's best to include all following functions inside the callback-function.
			 * @param {Function} [errorFunc] callback function that is invoked if an error occured during initialization.
			 * @async
			 * @public
			 * @memberOf mmir.CommonUtils.prototype
			 */
			loadDirectoryStructure: function (success, errorFunc) {
				var _defer = deferred();
				var self = this;

				if(success || errorFunc){
					_defer.then(success, errorFunc);
				}

				if(_conf && _conf.directories){
					setTimeout(function(){
						if(logger.isVerbose()) logger.verbose("DirectoryListing.getDirectoryStructure: loaded from module.config().directories");
						self.directoryStructure = _conf.directories;
						if(logger.isVerbose()) logger.verbose("[getDirectoryStructure] finished.");
						_defer.resolve(self);
					}, 0);
					return _defer;/////////////////// EARLY EXIT //////////////////////
				}

				var directoryFileUrl = res.getDirectoriesFileUrl();

				//load configuration file asynchronously:
				loadFile({
					async: true,
					dataType: "json",
					url: directoryFileUrl,
					success: function(data){
						if(logger.isVerbose()) logger.verbose("DirectoryListing.getDirectoryStructure: loaded file from "+directoryFileUrl);

						if(data){
							if(logger.isVerbose()) logger.verbose("DirectoryListing.getDirectoryStructure: Succeeded to load directory structure from '"+directoryFileUrl+"'! Data: "+ JSON.stringify(data));

							self.directoryStructure = data;

							if(logger.isVerbose()) logger.verbose("[getDirectoryStructure] finished.");

							_defer.resolve(self);
						}
					},
					error: function(jqXHR, textStatus, errorThrown){
						if(logger.isVerbose()) logger.verbose("DirectoryListing.getDirectoryStructure: failed to load file from '"+directoryFileUrl+"'! Status "+textStatus+": "+ errorThrown+ ", "+JSON.stringify(jqXHR));

						var msg = "[ERROR] " + textStatus+": failed to load file from '"+directoryFileUrl+"' - "+ errorThrown;
						if( ! errorFunc){
							logger.error('CommonUtils', 'loadDirectoryStructure', msg);
						}

						_defer.reject(msg);
					}
				});

				return _defer;
			},

			init: function(success, errorFunc){

				var initPromise;

				//use the Deferred from load-dir-struct, since this is the only async initialization atm:
				initPromise = this.loadDirectoryStructure.apply(this, arguments);

				//replace init so that we do not ivoke load-dir-struct multiple times
				this.__initDeferred = initPromise;
				this.init = function initCompleted(onsuccess, onerror){
					if(onsuccess || onerror){
						this.__initDeferred.then(success, onerror);
					}
					return this.__initDeferred;
				};

				return initPromise;
			}

		};// END: return {...

	}// END: constructor()


	instance = new constructor(resources);

	return instance;



});