UNPKG

mmir-lib

Version:

MMIR (Mobile Multimodal Interaction and Relay) library

401 lines (351 loc) 11.8 kB
define( [ 'mmirf/resources', 'mmirf/commonUtils', 'mmirf/logger', 'mmirf/util/deferred', 'module' ], /** * * A class for managing the models of the application (MVC-Component). <br> * It's purpose is to load the models automatically. * * This "class" is a singleton - so that only one instance is in * use.<br> * * TODO add example for usage (models as "class" / models as "singleton") * * @class * @name ModelManager * @memberOf mmir * @static * @hideconstructor * */ function( resources, commonUtils, Logger, deferred, module ){ //the next comment enables JSDoc2 to map all functions etc. to the correct class description /** @scope mmir.ModelManager.prototype */ /** * @private * @type mmir.tools.Logger * @memberOf mmir.ModelManager# * @see mmir.Logging#create */ var logger = Logger.create(module); // private members /** * Map of models * * @type Map * @private * * @memberOf mmir.ModelManager# */ var models = new Map(); /** * Name of the default namespace * (within the manager's space) * into which Models will be loaded * * @constant * @type String * @private * * @memberOf mmir.ModelManager# */ var MODEL_DEFAULT_NAMESPACE_NAME = 'mmir'; /** * The "global" namespace for the model manager * * @constant * @type Object * @private * * @memberOf mmir.ModelManager# */ var MODEL_MANAGER_NAMESPACE = typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : typeof global !== 'undefined' ? global : this; /** * * This function returns the fully qualified model name (including namespace(s)). * * @function * @param {String} * modelClassName the model's class-name (i.e. without namespace) * @returns {String} fully qualified name for the model * @private * * @memberOf mmir.ModelManager# */ function getFullModelName(modelClassName){ if( ! MODEL_DEFAULT_NAMESPACE_NAME){ return modelClassName; } else { return MODEL_DEFAULT_NAMESPACE_NAME + '.' + modelClassName; } } /** * * This function returns all loaded models. * * @function * @returns {Array<String>} all loaded model names * @public * @memberOf mmir.ModelManager# */ function getModelNames(){//TODO export this function on _instance? return Array.from(models.keys()); } /** * This function invokes the method * {@link mmir.ModelManager#foundModelsCallBack} to load all * models in the path specified by *modelPath*. * * @function * @param {Function} [initCallbackFunction] OPTIONAL * The callback function from the constructor * which shall be called after the initialization of the * {@link mmir.ModelManager}. * * @param {Object} [ctx] OPTIONAL * the context for the model implementations (DEFAULT: the global context, i.e. window) * @returns {Promise} * a deferred promise that gets fulfilled when models are loaded. * @private * @memberOf mmir.ModelManager# */ function _init(initCallbackFunction, ctx) { /** @scope mmir.ModelManager.prototype */ //shift arguments if necessary: //shift arguments if necessary: if(!ctx && typeof initCallbackFunction !== 'function'){ ctx = initCallbackFunction; initCallbackFunction = void(0); } /** * * This function returns the fully qualified model name (incl. namespace(s)). * * @function * @name getModelByName * @param {String|Array<String>} fullModelName the fully qualified model name (i.e. with namespace(s)) * Note, if {String} components/namespaces are separated by a <tt>.</tt> (dot) * If {Array<String>} the entries correspond to the namespace components (without dots), * where the last entry corresponds to the class/singleton name * @param {Object} [ctx] OPTIONAL * the (base) context for the model implementations (DEFAULT: the global context MODEL_MANAGER_NAMESPACE) * @returns {Object} the "raw" model object (may be a constructor or the main-singleton-namespace). * Or <tt>null</tt> if there is no Model with the name. * @private * * @requires mmir.CommonUtils#isArray * * @see mmir.ModelManager#getFullModelName * @see mmir.ModelManager#doGetModelInstance * * @memberOf mmir.ModelManager# */ function getModelByName(fullModelName, ctx){ ctx = ctx || MODEL_MANAGER_NAMESPACE; var components; if(commonUtils.isArray(fullModelName)){ components = fullModelName; } else { components = fullModelName.split('.'); } //try to resolve fully-qualified name (without triggering an error) var currentNameSpace = ctx; for(var i=0, size = components.length; i < size; ++i){ currentNameSpace = currentNameSpace[components[i]]; if(typeof currentNameSpace !== 'undefined' ){ if(i === size-1){ return currentNameSpace; } } else { break; } } if(size > 0 && ctx[components[size-1]]){ //try simple model name return ctx[components[size-1]]; } var isReTry = ctx !== MODEL_MANAGER_NAMESPACE; var logFunc = isReTry? 'info' : 'error'; logger[logFunc]('ModelManager.getModelByName: could not find model' +(isReTry? ' in model context with path "<modelContext>.' : ' "') +(components.join('.')) +'": ' +(isReTry? 'there is no' : 'invalid') +' namespace/class: "' +components[i] +'"' +(isReTry? ', trying to find model in global namespace ...' : '') ); return null; } /** * Returns the instance for a model implementation: * * If the model-object is a constructor (i.e. a function), * a new instance is created and returned. * * Otherwise the model-object itself is returned (e.g. for * singleton pattern models). * * @function * @private * * @see mmir.ModelManager#getModelByName * * @memberOf mmir.ModelManager# */ function doGetModelInstance(modelImplObject){ if(typeof modelImplObject === 'function'){ return new modelImplObject(); } //TODO allow alternative initialization methods for models?: // else if(typeof modelImplObject.getInstance === 'function'){ // return modelImplObject.getInstance(); // } // else if(typeof modelImplObject.init === 'function'){ // return modelImplObject.init(); // } else{ return modelImplObject; } //TODO export to requirejs? //define(modelImplObject.toString(), function(){ return THE_MODEL_INSTANCE;}); } var _defer = deferred(); if(initCallbackFunction){ _defer.then(initCallbackFunction, initCallbackFunction); } commonUtils.loadImpl( resources.getModelPath(), false, function () { logger.debug('[loadModels] done'); _defer.resolve(_instance); }, function isAlreadyLoaded(_name) { return false; // ( _instance && _instance.getModel(name) ); TODO }, function callbackStatus(status, fileName, msg) { if (status === 'info') { logger.info('[loadModel] "' + fileName); var modelName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/\.[^.]+$/g, ""); var fullName = getFullModelName(modelName); var modelImpl = getModelByName(fullName, ctx); if(!modelImpl){ modelImpl = getModelByName(fullName); } var modelInstance; if (modelImpl) { modelInstance = doGetModelInstance(modelImpl); } else { logger.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + fileName); modelInstance = modelName; } models.set(fullName, modelInstance); } else if (status === 'warning') { logger.warn('[loadModel] "' + fileName + '": ' + msg); } else if (status === 'error') { logger.error('[loadModel] "' + fileName + '": ' + msg); } else { logger.error('[loadModel] ' + status + ' (UNKNOWN STATUS) -> "' + fileName + '": ' + msg); } } // , function callbackAfterLoading(jsfile) { // var modelName = jsfile.charAt(0).toUpperCase() + jsfile.slice(1).replace(/\.[^.]+$/g, ""); // var fullName = getFullModelName(modelName); // var modelImpl = getModelByName(fullName); // var modelInstance; // if (modelImpl) { // modelInstance = doGetModelInstance(modelImpl); // } else { // logger.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + jsfile); // modelInstance = modelName; // } // models.set(fullName, modelInstance); // } ); return _defer; }; /** * Object containing the instance of the class {@link mmir.ModelManager} * * @type Object * @private * @ignore */ var _instance = { /** @scope mmir.ModelManager.prototype */ // public members /** * This function gets the model by name. * * @function * @param {String} * modelName Name of the model which should be returned * @returns {Object} The model if found, null else * @public * @memberOf mmir.ModelManager.prototype */ get: function(modelName) { var retModel = null; // TODO implement mechanism for multiple/configurable model namespaces // (add optional namespace argument to getModel) var fullModelName = getFullModelName(modelName); retModel = models.get(fullModelName); if (!retModel) { logger.error('Could not find Model "' + modelName + '" at ' + fullModelName); return null; } return retModel; }, /** * This function returns all loaded model names. * * @function * @returns {Array<string>} All loaded model names * @public */ getNames: getModelNames, /** * This function must be called before using the {@link mmir.ModelManager}. The Initialization process is asynchronous, * because javascript-files must be loaded (the models), so it forces a synchronous behavior by using * a callback function containing the instructions, which rely on the presence of the loaded models.<br> * * It loads the models and then calls the callback functions and returns the instance of this class. * * <div class="box important"> * <b>Note:</b> * The callback function should contain all (!) instructions which require the prior loading of the models.<br> * The callback mechanism is necessary, because loading the models is asynchronous.<br><br> * If provided, the callback function is invoked with 1 argument, the ModelManager instance:<br> * <code> callbackFunction(modelManagerInstance) </code> * </div> * * NOTE: use EITHER callback-function OR returned Promise -- do not use both! * * @function * @async * @param {Function} [callbackFunction] * The function which should be called after loading all controllers * @param {Object} [ctx] OPTIONAL * the context for the model implementations (DEFAULT: the global context, i.e. window) * @returns {Promise} * a deferred promise that gets fulfilled when models are loaded. * @example * function afterLoadingModels(modelManagerInstance){ * var userModel = modelManagerInstance.get('User'); * //do something... * } * mmir.model.create(afterLoadingModels); * @public */ init: _init }; return _instance; });