/*
* Copyright (C) 2012-2013 DFKI GmbH
* Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
* German Research Center for Artificial Intelligence
* http://www.dfki.de
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
define(['mmirf/constants','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
*
* @public
*
* @requires Constants (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(
constants, deferred, loadFile, _isArray, paramsParseFunc, Logger, module, require
) {
/** @scope mmir.CommonUtils.prototype *///for jsdoc2
/**
* @private
* @type CommonUtils
* @memberOf mmir.CommonUtils#
*/
var instance = null;
/**
* @private
* @type Logger
* @memberOf mmir.CommonUtils#
*/
var logger = Logger.create(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 {Constants} constants
* the constants-provider (e.g. URL for base directory etc)
*
* @constructs mmir.CommonUtils
* @memberOf mmir.CommonUtils#
* @function
* @private
*/
function constructor(constants) {
// private members.
/**
* The Prefix for the file names of partial-files.<br>
* Files named <PARTIAL-PREFIX>filename.ehtml inside a
* views-directory are recognized as partials.
*
* @type String
* @private
*/
var partialsPrefix = '~';
/**
* 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 = constants.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,
/**
* Same as <code>getLocalScript</code>
*
* @see #getLocalScript
* @memberOf mmir.CommonUtils.prototype
*/
loadScript : function(url, successCallback, errorCallback) {
return this.getLocalScript.apply(this, arguments);
},
/**
* 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(librariesPath, /^.*\.js$/ig);//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? files[i] : generatedGrammarsPath + files[i];
}
}
}
return '';
},
/**
* 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) {
var i = grammarFileName.lastIndexOf('_');
if (i !== -1) {
var id = grammarFileName.substring(0, i);
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)</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
*
* @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 isPath = true;//TODO use this for creating absolute paths (-> in case librariesPath is an Array)!
var theFileList;
if(typeof librariesPath === 'string'){
theFileList = instance.listDir(librariesPath, /^.*\.js$/ig);//get *.js files
}
else {
isPath = false;
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 -> triggere completedCallback
// if (typeof completedCallback == 'function'){
// completedCallback();
// } else {
// if(statusCallback){
// statusCallback('warning', fileName, 'provided callback for COMPLETION is not a function: '+completedCallback);
// }
// else {
// logger.warn('[loadImpl] callback for COMPLETION is not a function: '+completedCallback);
// }
// }
_defer.resolve();
};
if ( checkIsAlreadyLoadedFunc && checkIsAlreadyLoadedFunc(fileName) ){
if(statusCallback){
statusCallback('warning', fileName, 'already loaded ' + librariesPath+fileName);
}
handleScriptDone();
} else {
/// ATTENTION: $.getScript --> mmir.CommonUtils.getLocalScript
/// under Android 4.0 getScript is not wokring properly
instance.getLocalScript(librariesPath+fileName,
function(){
if(statusCallback){
statusCallback('info', fileName, 'done loading ' + librariesPath+fileName);
}
handleScriptDone();
},
function(exception) {
if(statusCallback){
statusCallback('error', fileName, 'could not load "' + librariesPath+fileName + '": ' + exception);
}
else {
// print out an error message
logger.error('[loadImpl] Could not load "' + librariesPath+fileName + '": ', exception);
}
//NOTE: in case of an error, will still try to load the other files from the list:
handleScriptDone();
}
);//END: getLocalScript(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;
}
},
/**
* <div class="box important"> <b>Note:</b> On Android 4.0
* jQuery.getScript() is not working properly - so use this function instead!
* </div>
*
* Similar to the jQuery.getScript() function - appending a url of a
* javascript-source to the header of the main document.<br>
* This function also calls a success-callback if the script was
* successfully loaded or a fail-callback.<br>
*
*
* @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() {
if(success){
success.apply(this, arguments);
}
else {
logger.debug('CommonUtils', 'getLocalScript', 'Successfully loaded script from ' + this.src)
}
};
script.onerror = function(e) {
if(fail){
fail.apply(this, arguments);
}
else {
logger.error('CommonUtils', 'getLocalScript', 'Loading Script Failed from "' + scriptUrl + '"', e);
}
};
head.appendChild(script);
},
/**
* @copydoc #listDir
* @deprecated use {@link #listDir} instead
* @memberOf mmir.CommonUtils.prototype
*/
getDirectoryContents : function(pathname) {
return this.getDirectoryContentsWithFilter(pathname);
},
/**
* @copydoc #listDir
* @deprecated use {@link #listDir} with RegExp for filter instead (see example for converting pseudo-wildcard string to RegExp)
* @memberOf mmir.CommonUtils.prototype
*
* @example
* //convert pseudo-wildcard string to RegExp
* var filterStr = '^' + filter.replace('.', '\\.').replace('*', '.*').replace('\$', '\\$') + '$'; // e.g.,// '^.*\.js$'
* var regexpr = new RegExp(filterStr, 'gi');
* mmir.CommonUtils.listDir(pathname, regexpr);
*
*/
getDirectoryContentsWithFilter : function(pathname, filter) {
if(filter){
var filterStr = '^' + filter.replace('.', '\\.').replace('*', '.*').replace('\$', '\\$') + '$'; // e.g.,// '^.*\.js$'
filter = new RegExp(filterStr, 'gi');
}
return this.listDir(pathname, filter);
},
/**
* 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 {String|RegExp|Function} [filter]
* Filter for file-names:
* if <code>String</code> the file-name may contain the wildcard <code>*</code>
* (comparison is <b>not case-sensitive</b>),
* e.g.: <b>*.js</b>, <b>*</b> or <b>*.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);
try {
var tmp = this.directoryStructure[pathname];
if (typeof tmp === 'undefined') {
logger.debug('CommonUtils', 'listDir', 'path "' + pathname + '" not found.');
return null;////////////////// EARLY EXIT ///////////////////////////////
}
else {
var i, size, retValue;
if(filter && typeof filter !== 'string'){//evaluate filter as RegExp or Function
retValue = [];
var isFunc = typeof filter === 'function';
//reset search-position for RegExp
isFunc || (filter.lastIndex = 0);
for (i = 0, size = tmp.length; i < size; ++i) {
if((isFunc && filter(tmp[i])) || (!isFunc && filter.test(tmp[i]))) {
retValue.push(tmp[i]);
}
isFunc || (filter.lastIndex = 0);
}
return retValue;////////////////// EARLY EXIT ///////////////////////////////
}
var pattern = typeof filter === 'string'? filter.split('*') : null;
if(!pattern || pattern.length === 0){
//no filter or invalid/all-allowing wildcard filter -> return complete result
return tmp;////////////////// EARLY EXIT ////////////////////////////////////
}
//evaluate filter as wildcard-string
//ASSERT pattern.length >= 1
for (i = 0, size = pattern.length; i < size; ++i) {
pattern[i] = pattern[i].toLowerCase();
}
var e, elen, j, index, part, doAdd, isStartWc;
var plen = pattern.length;
retValue = [];
for (i = 0, size = tmp.length; i < size; ++i) {
e = tmp[i].toLowerCase();
if(e){
//ASSERT e.length >= 1
elen = e.length;
doAdd = true;
index = 0;
isStartWc = false;
//match all entries of pattern-list (or exclude e from retValue)
for(j=0; j < plen; ++j){
part = pattern[j];
if(!part){
if(j===0){
//-> very first pattern-part is a wildcard
isStartWc = true;
} else if(j=== plen-1) {
//-> very last pattern-part is a wildcard
break;
} else {
//-> double wildcard, i.e. '**' ... just ignore, and continue with next part
continue;
}
}
index = e.indexOf(part, index);
if(index === -1){
doAdd = false;
break;
} else {
//special case j==0: matching for part must be at index 0,
// if pattern does not start with a wildcard
if(j===0 && index!==0 && !isStartWc){
doAdd = false;
break;
}
//continue matching for next pattern-part at pos+1
index += part.length;
if(j === plen-1 && index < elen){
//if last pattern-part (and it is not a wildcard),
//then it must match the remaining string, otherwise
//exclude e from retValue
doAdd = false;
}
}
}//END for(j in pattern)
if(doAdd){
retValue.push(tmp[i]);
}
}//END if(e)
}//END for(i in tmp)
return retValue;////////////////// EARLY EXIT ///////////////////////////////
}//END else tmp
} catch (e) {
logger.error('CommonUtils', 'listDir', '[' + pathname + ' | ' + filter + '] ', e);
}
return null;
},
/**
* 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> →
* <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 returnd "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){
if(logger.isInfo()) logger.warn('Cannot check 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);
}
var directoryFileUrl = constants.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.");//debug
_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(constants);
return instance;
});