Code coverage report for src/marionette.module.js

Statements: 98.61% (71 / 72)      Branches: 95.83% (23 / 24)      Functions: 100% (15 / 15)      Lines: 100% (68 / 68)     

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