module.js

Maintainability

71.78

Lines of code

201

Created with Raphaël 2.1.002550751002013-2-252013-1-262012-12-272012-11-272012-10-282013-3-27Maintainability: 71.78

Created with Raphaël 2.1.00601201792392013-2-252013-1-262012-12-272012-11-272012-10-282013-3-27Lines of Code: 201

Difficulty

30.43

Estimated Errors

1.04

Function weight

By Complexity

Created with Raphaël 2.1.0_addModuleDefinition5

By SLOC

Created with Raphaël 2.1.0_addModuleDefinition44
1
// Module
2
// ------
3
 
4
// A simple module system, used to create privacy and encapsulation in
5
// Marionette applications
6
Marionette.Module = function(moduleName, app){
7
  this.moduleName = moduleName;
8
 
9
  // store sub-modules
10
  this.submodules = {};
11
 
12
  this._setupInitializersAndFinalizers();
13
 
14
  // store the configuration for this module
15
  this.app = app;
16
  this.startWithParent = true;
17
 
18
  this.triggerMethod = Marionette.triggerMethod;
19
};
20
 
21
// Extend the Module prototype with events / listenTo, so that the module
22
// can be used as an event aggregator or pub/sub.
23
_.extend(Marionette.Module.prototype, Backbone.Events, {
24
 
25
  // Initializer for a specific module. Initializers are run when the
26
  // module's `start` method is called.
27
  addInitializer: function(callback){
28
    this._initializerCallbacks.add(callback);
29
  },
30
 
31
  // Finalizers are run when a module is stopped. They are used to teardown
32
  // and finalize any variables, references, events and other code that the
33
  // module had set up.
34
  addFinalizer: function(callback){
35
    this._finalizerCallbacks.add(callback);
36
  },
37
 
38
  // Start the module, and run all of its initializers
39
  start: function(options){
40
    // Prevent re-starting a module that is already started
41
    if (this._isInitialized){ return; }
42
 
43
    // start the sub-modules (depth-first hierarchy)
44
    _.each(this.submodules, function(mod){
45
      // check to see if we should start the sub-module with this parent
46
      if (mod.startWithParent){
47
        mod.start(options);
48
      }
49
    });
50
 
51
    // run the callbacks to "start" the current module
52
    this.triggerMethod("before:start", options);
53
 
54
    this._initializerCallbacks.run(options, this);
55
    this._isInitialized = true;
56
 
57
    this.triggerMethod("start", options);
58
  },
59
 
60
  // Stop this module by running its finalizers and then stop all of
61
  // the sub-modules for this module
62
  stop: function(){
63
    // if we are not initialized, don't bother finalizing
64
    if (!this._isInitialized){ return; }
65
    this._isInitialized = false;
66
 
67
    Marionette.triggerMethod.call(this, "before:stop");
68
 
69
    // stop the sub-modules; depth-first, to make sure the
70
    // sub-modules are stopped / finalized before parents
71
    _.each(this.submodules, function(mod){ mod.stop(); });
72
 
73
    // run the finalizers
74
    this._finalizerCallbacks.run(undefined,this);
75
 
76
    // reset the initializers and finalizers
77
    this._initializerCallbacks.reset();
78
    this._finalizerCallbacks.reset();
79
 
80
    Marionette.triggerMethod.call(this, "stop");
81
  },
82
 
83
  // Configure the module with a definition function and any custom args
84
  // that are to be passed in to the definition function
85
  addDefinition: function(moduleDefinition, customArgs){
86
    this._runModuleDefinition(moduleDefinition, customArgs);
87
  },
88
 
89
  // Internal method: run the module definition function with the correct
90
  // arguments
91
  _runModuleDefinition: function(definition, customArgs){
92
    if (!definition){ return; }
93
 
94
    // build the correct list of arguments for the module definition
95
    var args = _.flatten([
96
      this,
97
      this.app,
98
      Backbone,
99
      Marionette,
100
      Marionette.$, _,
101
      customArgs
102
    ]);
103
 
104
    definition.apply(this, args);
105
  },
106
 
107
  // Internal method: set up new copies of initializers and finalizers.
108
  // Calling this method will wipe out all existing initializers and
109
  // finalizers.
110
  _setupInitializersAndFinalizers: function(){
111
    this._initializerCallbacks = new Marionette.Callbacks();
112
    this._finalizerCallbacks = new Marionette.Callbacks();
113
  }
114
});
115
 
116
// Type methods to create modules
117
_.extend(Marionette.Module, {
118
 
119
  // Create a module, hanging off the app parameter as the parent object.
120
  create: function(app, moduleNames, moduleDefinition){
121
    var module = app;
122
 
123
    // get the custom args passed in after the module definition and
124
    // get rid of the module name and definition function
125
    var customArgs = slice(arguments);
126
    customArgs.splice(0, 3);
127
 
128
    // split the module names and get the length
129
    moduleNames = moduleNames.split(".");
130
    var length = moduleNames.length;
131
 
132
    // store the module definition for the last module in the chain
133
    var moduleDefinitions = [];
134
    moduleDefinitions[length-1] = moduleDefinition;
135
 
136
    // Loop through all the parts of the module definition
137
    _.each(moduleNames, function(moduleName, i){
138
      var parentModule = module;
139
      module = this._getModule(parentModule, moduleName, app);
140
      this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
141
    }, this);
142
 
143
    // Return the last module in the definition chain
144
    return module;
145
  },
146
 
147
  _getModule: function(parentModule, moduleName, app, def, args){
148
    // Get an existing module of this name if we have one
149
    var module = parentModule[moduleName];
150
 
151
    if (!module){
152
      // Create a new module if we don't have one
153
      module = new Marionette.Module(moduleName, app);
154
      parentModule[moduleName] = module;
155
      // store the module on the parent
156
      parentModule.submodules[moduleName] = module;
157
    }
158
 
159
    return module;
160
  },
161
 
162
  _addModuleDefinition: function(parentModule, module, def, args){
163
    var fn; 
164
    var startWithParent;
165
 
166
    if (_.isFunction(def)){
167
      // if a function is supplied for the module definition
168
      fn = def;
169
      startWithParent = true;
170
 
171
    } else if (_.isObject(def)){
172
      // if an object is supplied
173
      fn = def.define;
174
      startWithParent = def.startWithParent;
175
      
176
    } else {
177
      // if nothing is supplied
178
      startWithParent = true;
179
    }
180
 
181
    // add module definition if needed
182
    if (fn){
183
      module.addDefinition(fn, args);
184
    }
185
 
186
    // `and` the two together, ensuring a single `false` will prevent it
187
    // from starting with the parent
188
    module.startWithParent = module.startWithParent && startWithParent;
189
 
190
    // setup auto-start if needed
191
    if (module.startWithParent && !module.startWithParentIsConfigured){
192
 
193
      // only configure this once
194
      module.startWithParentIsConfigured = true;
195
 
196
      // add the module initializer config
197
      parentModule.addInitializer(function(options){
198
        if (module.startWithParent){
199
          module.start(options);
200
        }
201
      });
202
 
203
    }
204
 
205
  }
206
});