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( [ 'dictionary', 'constants', 'commonUtils', 'jquery' ], 30 /** 31 * 32 * A class for managing the models of the application (MVC-Component). <br> 33 * It's purpose is to load the models automatically. 34 * 35 * This "class" is a singleton - so that only one instance is in 36 * use.<br> 37 * 38 * TODO add example for usage (models as "class" / models as "singleton") 39 * 40 * @class 41 * @name ModelManager 42 * @memberOf mmir 43 * @static 44 * 45 * @requires jQuery.Deferred 46 * 47 */ 48 function( 49 Dictionary, constants, commonUtils, $ 50 ){ 51 //the next comment enables JSDoc2 to map all functions etc. to the correct class description 52 /** @scope mmir.ModelManager.prototype */ 53 54 // private members 55 /** 56 * Array of models 57 * 58 * @type Dictionary 59 * @private 60 * 61 * @memberOf mmir.ModelManager# 62 */ 63 var models = new Dictionary(); 64 65 /** 66 * Name of the default namespace 67 * (within the global space) 68 * into which Models will be loaded 69 * 70 * @constant 71 * @type String 72 * @private 73 * 74 * @memberOf mmir.ModelManager# 75 */ 76 var MODEL_DEFAULT_NAMESPACE_NAME = 'mmir'; 77 78 /** 79 * The global namespace 80 * 81 * @constant 82 * @type Object 83 * @private 84 * 85 * @memberOf mmir.ModelManager# 86 */ 87 var GLOBAL_NAMESPACE = window; 88 89 /** 90 * 91 * This function returns the fully qualified model name (including namespace(s)). 92 * 93 * @function 94 * @param {String} 95 * modelClassName the model's class-name (i.e. without namespace) 96 * @returns {String} fully qualified name for the model 97 * @private 98 * 99 * @memberOf mmir.ModelManager# 100 */ 101 function getFullModelName(modelClassName){ 102 if( ! MODEL_DEFAULT_NAMESPACE_NAME){ 103 return modelClassName; 104 } 105 else { 106 return MODEL_DEFAULT_NAMESPACE_NAME + '.' + modelClassName; 107 } 108 } 109 110 /** 111 * 112 * This function returns all loaded models. 113 * 114 * @function 115 * @returns {Array<String>} all loaded model names 116 * @public 117 * @memberOf mmir.ModelManager# 118 */ 119 function getModelNames(){//TODO export this function on _instance? 120 return models.getKeys(); 121 } 122 123 /** 124 * This function invokes the method 125 * {@link mmir.ModelManager#foundModelsCallBack} to load all 126 * models in the path specified by *modelPath*. 127 * 128 * @function 129 * @param {Function} 130 * myCallbackFunction The callback function from the constructor 131 * which shall be called after the initialization of the 132 * {@link mmir.ModelManager}. 133 * @private 134 * @memberOf mmir.ModelManager# 135 */ 136 function _init(myCallbackFunction) { 137 138 /** @scope mmir.ModelManager.prototype */ 139 140 // delete _instance.init; 141 /** 142 * <code>init</code> as alias for #getInstance 143 * @private 144 * @function 145 * @name init 146 * @memberOf mmir.ModelManager# 147 */ 148 _instance.init = _instance.getInstance; 149 150 /** 151 * 152 * This function returns the fully qualified model name (incl. namespace(s)). 153 * 154 * @function 155 * @name getModelByName 156 * @param {String|Array<String>} fullModelName the fully qualified model name (i.e. with namespace(s)) 157 * Note, if {String} components/namespaces are separated by a <tt>.</tt> (dot) 158 * If {Array<String>} the entries correspond to the namespace components (without dots), 159 * where the last entry corresponds to the class/singleton name 160 * @returns {Object} the "raw" model object (may be a constructor or the main-singleton-namespace). 161 * Or <tt>null</tt> if there is no Model with the name. 162 * @private 163 * 164 * @requires mmir.CommonUtils#isArray 165 * 166 * @see mmir.ModelManager#getFullModelName 167 * @see mmir.ModelManager#doGetModelInstance 168 * 169 * @memberOf mmir.ModelManager# 170 */ 171 function getModelByName(fullModelName){ 172 var components; 173 if(commonUtils.isArray(fullModelName)){ 174 components = fullModelName; 175 } 176 else { 177 components = fullModelName.split('.'); 178 } 179 180 var currentNameSpace = GLOBAL_NAMESPACE; 181 for(var i=0, size = components.length; i < size; ++i){ 182 currentNameSpace = currentNameSpace[components[i]]; 183 if(typeof currentNameSpace !== 'undefined' ){ 184 if(i === size-1){ 185 return currentNameSpace; 186 } 187 } 188 else { 189 console.error('ModelManager.getModelByName: could not find model "' 190 +(components.join('.')) 191 +'": invalid namespace/class: ' 192 +components[i] 193 ); 194 break; 195 } 196 } 197 return null; 198 } 199 200 /** 201 * Returns the instance for a model implementation: 202 * 203 * If the model-object is a constructor (i.e. a function), 204 * a new instance is created and returned. 205 * 206 * Otherwise the model-object itself is returned (e.g. for 207 * singleton pattern models). 208 * 209 * @function 210 * @private 211 * 212 * @see mmir.ModelManager#getModelByName 213 * 214 * @memberOf mmir.ModelManager# 215 */ 216 function doGetModelInstance(modelImplObject){ 217 218 219 if(typeof modelImplObject === 'function'){ 220 return new modelImplObject(); 221 } 222 //TODO allow alternative initialization methods for models?: 223 // else if(typeof modelImplObject.getInstance === 'function'){ 224 // return modelImplObject.getInstance(); 225 // } 226 // else if(typeof modelImplObject.init === 'function'){ 227 // return modelImplObject.init(); 228 // } 229 else{ 230 return modelImplObject; 231 } 232 233 //TODO export to requirejs? 234 //define(modelImplObject.toString(), function(){ return THE_MODEL_INSTANCE;}); 235 236 } 237 238 var _defer = $.Deferred(); 239 if(myCallbackFunction){ 240 _defer.always(myCallbackFunction); 241 } 242 243 commonUtils.loadImpl( 244 245 constants.getModelPath(), 246 247 false, 248 249 function () { 250 251 console.log('[loadModels] done'); 252 253 _defer.resolve(_instance); 254 }, 255 256 function isAlreadyLoaded(name) { 257 return false; // ( _instance && _instance.getModel(name) ); TODO 258 }, 259 260 function callbackStatus(status, fileName, msg) { 261 262 if (status === 'info') { 263 264 console.info('[loadModel] "' + fileName); 265 266 var modelName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/\.[^.]+$/g, ""); 267 var fullName = getFullModelName(modelName); 268 var modelImpl = getModelByName(fullName); 269 var modelInstance; 270 if (modelImpl) { 271 modelInstance = doGetModelInstance(modelImpl); 272 } else { 273 console.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + fileName); 274 modelInstance = modelName; 275 } 276 models.put(fullName, modelInstance); 277 278 } 279 else if (status === 'warning') { 280 console.warn('[loadModel] "' + fileName + '": ' + msg); 281 } 282 else if (status === 'error') { 283 console.error('[loadModel] "' + fileName + '": ' + msg); 284 } 285 else { 286 console.error('[loadModel] ' + status + ' (UNKNOWN STATUS) -> "' + fileName + '": ' + msg); 287 } 288 } 289 290 // , function callbackAfterLoading(jsfile) { 291 // var modelName = jsfile.charAt(0).toUpperCase() + jsfile.slice(1).replace(/\.[^.]+$/g, ""); 292 // var fullName = getFullModelName(modelName); 293 // var modelImpl = getModelByName(fullName); 294 // var modelInstance; 295 // if (modelImpl) { 296 // modelInstance = doGetModelInstance(modelImpl); 297 // } else { 298 // console.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + jsfile); 299 // modelInstance = modelName; 300 // } 301 // models.put(fullName, modelInstance); 302 // } 303 ); 304 305 306 return _defer.promise(_instance); 307 }; 308 309 /** 310 * Object containing the instance of the class {@link mmir.ModelManager} 311 * 312 * @type Object 313 * @private 314 * @ignore 315 */ 316 var _instance = { 317 /** @scope mmir.ModelManager.prototype */ 318 319 /** 320 * @deprecated use ModelManager object directly, e.g. instead of: mmir.ModelManager.getInstance().getModel() 321 * use: mmir.ModelManager.getModel() 322 * 323 * NOTE: ModelManager must be initialized before it can be used. 324 * 325 * @memberOf mmir.ModelManager.prototype 326 */ 327 getInstance : function () { 328 return this; 329 }, 330 331 // public members 332 /** 333 * This function gets the model by name. 334 * 335 * @function 336 * @param {String} 337 * modelName Name of the model which should be returned 338 * @returns {Object} The model if found, null else 339 * @public 340 */ 341 getModel : function(modelName) { 342 var retModel = null; 343 344 // TODO implement mechanism for multiple/configurable model namespaces 345 // (add optional namespace argument to getModel) 346 var fullModelName = getFullModelName(modelName); 347 348 retModel = models.get(fullModelName); 349 if (!retModel) { 350 console.error('Could not find Model "' + modelName + '" at ' + fullModelName); 351 return null; 352 } 353 return retModel; 354 }, 355 356 357 /** 358 * This function returns all loaded models. 359 * 360 * @function 361 * @returns {Array} All loaded models 362 * @public 363 */ 364 getModels : function() { 365 return models; 366 }, 367 368 /** 369 * This function must be called before using the {@link mmir.ModelManager}. The Initialization process is asynchronous, 370 * because javascript-files must be loaded (the models), so it forces a synchronous behavior by using 371 * a callback function containing the instructions, which rely on the presence of the loaded models.<br> 372 * 373 * It loads the models and then calls the callback functions and returns the instance of this class. 374 * 375 * <div class="box important"> 376 * <b>Note:</b> 377 * The callback function should contain all (!) instructions which require the prior loading of the models.<br> 378 * The callback mechanism is necessary, because loading the models is asynchronous.<br><br> 379 * If provided, the callback function is invoked with 1 argument, the ModelManager instance:<br> 380 * <code> callbackFunction(modelManagerInstance) </code> 381 * </div> 382 * 383 * NOTE: use EITHER callback-function OR returned Promise -- do not use both! 384 * 385 * @function 386 * @param {Function} [callbackFunction] 387 * The function which should be called after loading all controllers 388 * @returns {Promise} 389 * a Deferred.promise that gets fulfilled when models are loaded. 390 * @example 391 * function afterLoadingModels(modelManagerInstance){ 392 * var userModel = modelManagerInstance.getModel('User'); 393 * //do something... 394 * } 395 * mmir.ModelManager.create(afterLoadingModels); 396 * @public 397 */ 398 init: _init 399 400 }; 401 402 return _instance; 403 404 }); 405