UNPKG

mmir-lib

Version:

MMIR (Mobile Multimodal Interaction and Relay) library

492 lines (427 loc) 15.8 kB
define(['mmirf/core', 'mmirf/util/isArray', 'mmirf/util/deferred', 'mmirf/resources', 'mmirf/commonUtils', 'mmirf/logger', 'mmirf/configurationManager', 'mmirf/languageManager' , 'mmirf/controllerManager', 'mmirf/modelManager', 'mmirf/presentationManager' , 'mmirf/semanticInterpreter', 'mmirf/mediaManager', 'mmirf/notificationManager' , 'module' ], /** * Initializes the MMIR framework: * triggers {@link mmir.ready} when initialization has finished. * * @class * @name main * @memberof mmir * @private * @hideconstructor * * @requires require.config * @requires util/deferred * */ function(mmir, isArray, deferred, resources, utils, Logger, conf, lang , ctrlManager, modelManager, present , semantic, media, notif , module ){ var logger = Logger.create(module.config(module)); //export framework functions/objects: /** * @memberOf mmir * @type {mmir.Resources} */ mmir.res = resources; /** * @memberOf mmir * @type {mmir.CommonUtils} */ mmir.util = utils; /** * @memberOf mmir * @type {mmir.ConfigurationManager} */ mmir.conf = conf; /** * @memberOf mmir * @type {mmir.NotificationManager} */ mmir.notifier = notif.init(); //wait until configuration has been loaded/initialized: conf.init().then(function(){ var rootCtx = typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : typeof global !== 'undefined' ? global : this; /** * HELPER create a "namespace" from a package-definition (dot-separated string) from configuration * or return the global namespace, if there is not config-value * @param {String|Array<String>} configuration name * * @private * @memberOf main */ var getContextFor = function(ctxConfigName){ var ctx = rootCtx; var ctxName = conf.get(ctxConfigName); if(ctxName){ var namespaces = ctxName.split('.'); var name; for(var i=0, size= namespaces.length; i < size; ++i){ name = namespaces[i]; if(!ctx[name]){ ctx[name] = {}; } ctx = ctx[name]; } return ctx; } return ctx; } //the context where the controller implementation can be found (default: global context, i.e. window) /** @memberOf main */ var ctrlImplCtx = getContextFor('controllerContext'); //the context where the model implementations can be found (default: global context, i.e. window) /** @memberOf main */ var modelImplCtx = getContextFor('modelContext'); /** * HELPER detect compiled state-models (compiled SCXML files) and set mmirf/scion (i.e. state engine) * accordingly, either to runtime-only or leave default compiler&runtime * @memberOf main */ var selectStateEngine = function(){ //NOTE need to wrap all "non-webpack" code by the webpack-build guard, in order // to avoid webpack warning about critical dependencies (i.e. un-limited require-usage) if(typeof WEBPACK_BUILD === 'undefined' || !WEBPACK_BUILD){ if(!conf.getBoolean('detectCompiledStateModels', true)){ return; } var genDir = resources.getGeneratedStateModelsPath(); var models = utils.listDir(genDir); if(models){ //do sort state-models, so that 'dialog.xml' and 'input.xml' are prioritized before others var re = /^(dialog|input)\.js$/i models.sort(function(m1, m2){ if(m1 === m2) return 0; if(re.test(m1)) return -1; if(re.test(m2)) return 1; return m1? m1.localeCompare(m2) : 0; }); var statesConfig = { paths: {'mmirf/scion': require.toUrl('mmirf/scionRuntime')}, config: {} }; var m, match, modId, re = /^(dialog|input)(DescriptionSCXML)?\.js$/i; for(var i=0, size = models.length; i < size; ++i){ m = models[i]; re.lastIndex = 0; match = re.exec(m); if(match){ modId = 'mmirf/' + match[1].toLowerCase() + 'Manager'; } else { //for custom state-machines (in non-WEBPACK build): the module ID is the file name (without extension) modId = m.replace(/\.js$/i, ''); } // ['mmirf/dialogManager' | 'mmirf/inputManager']: {modelUri: 'gen/states/' + ['dialog' | 'input']} if(!statesConfig.config[modId]){ statesConfig.config[modId] = {modelUri: genDir + m}; } else { logger.error( 'Duplicate state-manager module configuration: ignoring file "'+genDir+m+ '", because module ID "'+modId+ '" is already used for '+statesConfig.config[modId].modelUri) } } mmir.require = require.config(statesConfig); } } }; //load/async-require inputManager & dialogManager: // if pre-compiled SCXML models, use scion-runtime instead of scion-compiler var inputManager, dialogManager; /** * HELPER set state-model runtime/implementation & load inputManager & dialogManager * * Side Effects: * * sets var dialogManager * * sets var inputManager * * @param {Deferred} deferredStateMachines promise that will get resolved when input- and dialogManager have been loaded * @memberOf main */ var initStateEngines = function(deferredStateMachines){ selectStateEngine(); //after selection the scion-lib (compiler or runtime), do load input- and dialogManager: require(['mmirf/inputManager', 'mmirf/dialogManager'], function(im, dm){ inputManager = im; dialogManager = dm; deferredStateMachines.resolve(); }); }; /** * Main Initialization: * initializes mmir and exports its functions/modules to (gobal) mmir namespace * * @memberOf main */ var mainInit = function(){ //for initializing/loading inputManager & dialogManager: var defStateMachines = new deferred(); //initialize the common-utils: utils.init()//<- load directory structure //initialize state-managers .then(function() { //need to wait until directories.json is available for checking compiled state-machines: initStateEngines(defStateMachines); return lang.init().then(function(langMng){ /** * @memberOf mmir * @type {mmir.LanguageManager} */ mmir.lang = langMng; }); }) // start the ControllerManager .then(function() { /** * @memberOf mmir * @type {mmir.ControllerManager} */ mmir.ctrl = ctrlManager; //NOTE: this also gathers information on which // views, layouts etc. are available // -> the presentationManager depends on this information return ctrlManager.init(ctrlImplCtx).then(function(){return defStateMachines}); }) //TEST parallelized loading of independent modules: .then(function(){ /** @memberOf main */ var isMediaManagerLoaded = false; /** @memberOf main */ var isModelsLoaded = false; /** @memberOf main */ var isVisualsLoaded = false; /** @memberOf main */ var isInputManagerLoaded = false; /** @memberOf main */ var isDialogManagerLoaded = false; /** @memberOf main */ var isSemanticsLoaded = false; /** @memberOf main */ var isSemanticsAsyncLoaded = false; /** @memberOf main */ var checkInitCompleted = function(){ if( isMediaManagerLoaded && isModelsLoaded && isVisualsLoaded && isInputManagerLoaded && isDialogManagerLoaded && isSemanticsLoaded && isSemanticsAsyncLoaded ){ /** * Additional configuration for requirejs * from configuration.json: * -> if property "config" is set, apply it as requirejs-config * before signaling READY * EXAMPLE: * the following entry (in config/configuration.json) would add * the dependency information for www/appjs/test.js as module "testConf" * * , "config": { * "paths": { * "testConf": "../appjs/test" * } * } * * @type PlainObject * @memberOf main */ var requireConfig = conf.get('config'); if(requireConfig){ mmir.require = require.config(requireConfig); } //"give signal" that the framework is now initialized / ready mmir.setInitialized(); } }; /** * @memberOf mmir * @type {mmir.SemanticInterpreter} */ mmir.semantic = semantic; /** ID for the grammar engine/compiler to be used, if/when JSON grammar are (re-) compiled * @see mmir.SemanticInterpreter#setGrammarEngine * @type String * @memberOf main */ var grammarEngine = conf.get('grammarCompiler'); if(grammarEngine){ semantic.setGrammarEngine(grammarEngine); } /** set synchronous/asynchronous compile-mode for grammar compilation * @see mmir.SemanticInterpreter#setEngineCompileMode * @type Boolean * @memberOf main */ var grammarCompileMode = conf.get('grammarAsyncCompileMode', semantic.getEngineCompileMode()); /** enable/disable JavaScript strict mode for compiled grammars * @see mmir.SemanticInterpreter#setEngineCompileMode * @type Boolean * @memberOf main */ var grammarDisableStrictCompileMode = conf.get('grammarDisableStrictCompileMode'); if(typeof grammarCompileMode !== 'undefined' || typeof grammarDisableStrictCompileMode !== 'undefined'){ semantic.setEngineCompileMode(grammarCompileMode, grammarDisableStrictCompileMode); } /** list of grammar IDs which should not be loaded, even if there is a compiled grammar available: * @type String * @memberOf main */ var ignoreGrammarIds = conf.get('ignoreGrammarFiles'); //for keeping track of ignored grammars (in case async-exec grammar are specified, this needs to be updated -> see below) var ignoreGrammarsSet = isArray(ignoreGrammarIds)? new Set(ignoreGrammarIds) : null; //prepare async-execution for compiled grammars var grammarAsyncExecMode = conf.get('grammarAsyncExecMode'); if(grammarAsyncExecMode){ if(grammarAsyncExecMode && (typeof Worker === 'undefined' || WEBPACK_BUILD === 'undefined' || !WEBPACK_BUILD)){//<- NOTE if WEBPACK_BUILD logger.error('configured async-grammars, but could not detect WebWorker implementation: async grammars will probably not work!'); //TODO automatically convert to sync-grammar execution? -> if grammarAsyncExecMode is config-list for async grammars -> check ignoreGrammarIds and remove, if they are contained in grammarAsyncExecMode } var asyncExecIds = grammarAsyncExecMode; if(grammarAsyncExecMode === true){ asyncExecIds = utils.getCompiledResourcesIds(resources.getGeneratedGrammarsPath()); if(ignoreGrammarsSet){//<- ignoreGrammarsSet is only set, if ignoreGrammarIds is an array! var id; for(var i=0,size=asyncExecIds.length; i < size; ++i){ id = asyncExecIds[i]; //add exec-async grammars to "normal-loading" ignore list: if(!ignoreGrammarsSet.has(id)){ ignoreGrammarIds.push(id); ignoreGrammarsSet.add(id); } } } if(typeof ignoreGrammarIds === 'undefined'){ ignoreGrammarIds = true; } //ASSERT: else if array, async-loaded grammars have been added to ignore-list } else if(typeof ignoreGrammarIds === 'undefined') { ignoreGrammarIds = []; ignoreGrammarsSet = new Set(); } else if(ignoreGrammarsSet){ // -> add grammars that will be initialized for async-mode to ignore-list (if not already contained) var aid; for(var i=0, size=asyncExecIds.length; i < size; ++i){ aid = asyncExecIds[i]; if(typeof aid !== 'string'){ aid = aid.id; } if(!ignoreGrammarsSet.has(aid)){ ignoreGrammarsSet.add(aid); ignoreGrammarIds.push(aid) } } } mmir.require(['mmirf/asyncGrammar'], function(asyncGrammar){ var count = asyncExecIds.length; var cb = function(){ if(--count <= 0){ isSemanticsAsyncLoaded = true; checkInitCompleted(); } }; var res, curr, initPhrase; for(var i=0, size=count; i < size; ++i){ curr = asyncExecIds[i]; if(typeof curr !== 'string'){ initPhrase = curr.phrase; curr = curr.id; } else { initPhrase = void(0); } res = asyncGrammar.init( curr, cb, initPhrase ); if(!res){ cb(); } } }); } else { isSemanticsAsyncLoaded = true; checkInitCompleted(); } //completionhandler for when sync-grammars have been loaded/processed var onGrammarsCompleted = function() { if(!semantic.getCurrentGrammar()){ lang._requestGrammar(lang.getLanguage(), true); } isSemanticsLoaded = true; checkInitCompleted(); }; if(ignoreGrammarIds === true || grammarAsyncExecMode === true){ //-> ignore all compiled grammars onGrammarsCompleted(); } else { // -> load compiled grammars utils.loadCompiledGrammars(resources.getGeneratedGrammarsPath(), void(0), ignoreGrammarIds).then(onGrammarsCompleted); } // start the MediaManager media.init().then(function() { isMediaManagerLoaded = true; //initialize BEEP notification (after MediaManager was initialized) notif.initBeep(); /** * @memberOf mmir * @type {mmir.MediaManager} */ mmir.media = media; checkInitCompleted(); }); //TODO models may access views etc. during their initialization // --> there should be a way to configure startup, so that models may only be loaded, after everything else was loaded modelManager.init(modelImplCtx).then(function(){ isModelsLoaded = true; /** * @memberOf mmir * @type {mmir.ModelManager} */ mmir.model = modelManager; checkInitCompleted(); }); present.init().then(function(){ isVisualsLoaded = true; /** * @memberOf mmir * @type {mmir.PresentationManager} */ mmir.present = present; checkInitCompleted(); }, function error(err){ console.error('Failed initializing PresentationManager: '+err); }); //TODO handle reject/fail of the presentationManager's init-Promise! dialogManager.init().then(function(result){//_dlgMng, _dialogEngine){ isDialogManagerLoaded = true; /** * @memberOf mmir * @type {mmir.DialogManager} */ mmir.dialog = result.manager; /** * @memberOf mmir * @type {mmir.DialogEngine} */ mmir.dialogEngine = result.engine; checkInitCompleted(); }); inputManager.init().then(function(result){//_inputMng, _inputEngine){ isInputManagerLoaded = true; /** * @memberOf mmir * @type {mmir.InputManager} */ mmir.input = result.manager; /** * @memberOf mmir * @type {mmir.InputEngine} */ mmir.inputEngine = result.engine; checkInitCompleted(); }); }); };//END: mainInit(){... mainInit(); });//END: conf.init().then(function(){... return mmir; });//END: define(...