shuffle answers in exercise order + fix answer numbering
[fnpeditor.git] / src / fnpjs / runner.js
1 define(['libs/jquery', 'libs/underscore', 'fnpjs/logging/logging', 'fnpjs/actions'], function($, _, logging, actions) {
2
3 'use strict';
4
5 var Runner = function(app, modules) {
6
7     function getModuleInstance(moduleName) {
8         var module = moduleInstances[moduleName] = (moduleInstances[moduleName] || modules[moduleName](new Sandbox(moduleName)));
9         return module;
10     }
11
12     var bootstrappedData = {},
13         moduleInstances = {},
14         eventListeners = [],
15         plugins = [],
16         actionDefinitions = {},
17         config,
18         actionsAppObject,
19         currentContextMenu;
20         
21     _.each(_.keys(modules || {}), function(moduleName) {
22         if(_.contains(app.permissions[moduleName] || [], 'handleEvents')) {
23             eventListeners.push(moduleName);
24         }
25     });
26
27     
28         
29     var Sandbox = function(moduleName) {
30         this.$ = $;
31         this._ = _;
32         
33         this.getBootstrappedData = function() {
34             return bootstrappedData[moduleName];
35         };
36         
37         this.publish = function(eventName) {
38             var eventArgs = Array.prototype.slice.call(arguments, 1);
39             _.each(eventListeners, function(listenerModuleName) {
40                 var listener = moduleInstances[listenerModuleName];
41                 if(listener) {
42                     listener.handleEvent(moduleName, eventName, eventArgs);
43                 }
44             });
45         };
46         
47         var permissions = app.permissions[moduleName];
48         
49         this.getModule = _.contains(permissions, 'getModule') ? function(requestedModuleName) {
50             return getModuleInstance(requestedModuleName);
51         } : undefined;
52         
53         this.getDOM = _.contains(permissions, 'getDOM') ? function() {
54             return $(config.rootSelector);
55         } : undefined;
56
57         this.getPlugins = function() {
58             return plugins;
59         };
60
61         this.getConfig = function() {
62             return config;
63         };
64
65         this.createAction = function(fqName, config) {
66             var definition = actionDefinitions[fqName];
67             if(!definition) {
68                 throw new Error('Invalid action: ' + fqName);
69             }
70             return new actions.Action(fqName, definition, config, actionsAppObject);
71         };
72
73         this.registerKeyHandler = function(eventName, handler) {
74             $('body').on(eventName, function(e) {
75                 handler(e);
76             });
77         };
78
79         this.registerActionsAppObject = function(_actionsAppObject) {
80             actionsAppObject = _actionsAppObject;
81         };
82
83         this.showContextMenu = function(menu, coors) {
84             if(currentContextMenu) {
85                 currentContextMenu.close();
86             }
87             currentContextMenu = menu;
88             $(config.rootSelector).append(menu.dom);
89             menu.dom.css({top: coors.y, left: coors.x});
90             menu.show();
91         };
92     };
93       
94     this.setBootstrappedData = function(moduleName, data) {
95         bootstrappedData[moduleName] = data;
96     };
97
98     this.registerPlugin = function(plugin) {
99         plugins.push(plugin);
100         (plugin.actions || []).forEach(function(definition) {
101             var actionFqName = plugin.name + '.' + definition.name;
102             actionDefinitions[actionFqName] = definition;
103         });
104     };
105     
106     this.start = function(_config) {
107         config = _.extend({
108             rootSelector: 'body'
109         }, _config);
110
111
112         if(config.logging) {
113             logging.setConfig(config.logging);
114         }
115
116         _.pairs(config.plugins || {}).forEach(function(pair) {
117             var pluginName = pair[0],
118                 pluginConfig = pair[1];
119
120             plugins.some(function(plugin) {
121                 if(plugin.name === pluginName) {
122                     if(_.isFunction(plugin.config)) {
123                         plugin.config(pluginConfig);
124                     }
125                     return true; //break
126                 }
127             });
128         });
129
130         app.initModules.forEach(function(moduleName) {
131             getModuleInstance(moduleName).start();
132         });
133
134         $(config.rootSelector)[0].addEventListener('click', function(e) {
135             if(currentContextMenu && !currentContextMenu.dom[0].contains(e.target)) {
136                 currentContextMenu.close();
137                 currentContextMenu = null;
138             }
139         }, true);
140     };
141 };
142
143 return {
144     Runner: Runner
145 };
146
147 });