backbone-marionette-rails 1.0.0.6 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0945d593536da471a9ef33e4d2497f62a788e856
4
+ data.tar.gz: c4764defcd4105080f613a12095165f426cbe4f8
5
+ SHA512:
6
+ metadata.gz: af35db2132767695a9a5df6d5d8321f1298ad73e829c17f2077f936653ea43c9bb479d283c6c5519522a96236e7c68807a042c10176d8866ded40e2b5d555d31
7
+ data.tar.gz: e1b7bbb931458a0e760c279ea9140b9e90d2fad27f7b58ebd8325a24e4b8bc9bdbfa621e4befdb8428e74fb39051ea610cef8671dece8adccb35f5ccff563539
@@ -1,7 +1,7 @@
1
1
  module Backbone
2
2
  module Marionette
3
3
  module Rails
4
- VERSION = "1.0.0.6"
4
+ VERSION = "1.0.1"
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,12 @@
1
- // Backbone.Marionette, v1.0.0-rc6
2
- // Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
3
- // Distributed under MIT license
4
- // http://github.com/marionettejs/backbone.marionette
1
+ // MarionetteJS (Backbone.Marionette)
2
+ // ----------------------------------
3
+ // v1.0.1
4
+ //
5
+ // Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
6
+ // Distributed under MIT license
7
+ //
8
+ // http://marionettejs.com
9
+
5
10
 
6
11
 
7
12
  /*!
@@ -12,7 +17,6 @@
12
17
  * https://github.com/marionettejs/backbone.wreqr/
13
18
  */
14
19
 
15
-
16
20
  // Backbone.BabySitter, v0.0.4
17
21
  // Copyright (c)2012 Derick Bailey, Muted Solutions, LLC.
18
22
  // Distributed under MIT license
@@ -207,147 +211,297 @@ Backbone.ChildViewContainer = (function(Backbone, _){
207
211
  return Container;
208
212
  })(Backbone, _);
209
213
 
210
- // Backbone.Wreqr, v0.1.1
214
+ // Backbone.Wreqr (Backbone.Marionette)
215
+ // ----------------------------------
216
+ // v0.2.0
217
+ //
211
218
  // Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
212
219
  // Distributed under MIT license
220
+ //
213
221
  // http://github.com/marionettejs/backbone.wreqr
222
+
223
+
214
224
  Backbone.Wreqr = (function(Backbone, Marionette, _){
215
225
  "use strict";
216
226
  var Wreqr = {};
217
227
 
218
228
  // Handlers
219
- // --------
220
- // A registry of functions to call, given a name
229
+ // --------
230
+ // A registry of functions to call, given a name
231
+
232
+ Wreqr.Handlers = (function(Backbone, _){
233
+ "use strict";
221
234
 
222
- Wreqr.Handlers = (function(Backbone, _){
223
- "use strict";
235
+ // Constructor
236
+ // -----------
237
+
238
+ var Handlers = function(options){
239
+ this.options = options;
240
+ this._wreqrHandlers = {};
224
241
 
225
- // Constructor
226
- // -----------
227
-
228
- var Handlers = function(){
229
- this._handlers = {};
230
- };
231
-
232
- Handlers.extend = Backbone.Model.extend;
233
-
234
- // Instance Members
235
- // ----------------
236
-
237
- _.extend(Handlers.prototype, {
238
-
239
- // Add a handler for the given name, with an
240
- // optional context to run the handler within
241
- addHandler: function(name, handler, context){
242
- var config = {
243
- callback: handler,
244
- context: context
245
- };
246
-
247
- this._handlers[name] = config;
248
- },
249
-
250
- // Get the currently registered handler for
251
- // the specified name. Throws an exception if
252
- // no handler is found.
253
- getHandler: function(name){
254
- var config = this._handlers[name];
255
-
256
- if (!config){
257
- throw new Error("Handler not found for '" + name + "'");
242
+ if (_.isFunction(this.initialize)){
243
+ this.initialize(options);
244
+ }
245
+ };
246
+
247
+ Handlers.extend = Backbone.Model.extend;
248
+
249
+ // Instance Members
250
+ // ----------------
251
+
252
+ _.extend(Handlers.prototype, Backbone.Events, {
253
+
254
+ // Add multiple handlers using an object literal configuration
255
+ setHandlers: function(handlers){
256
+ _.each(handlers, function(handler, name){
257
+ var context = null;
258
+
259
+ if (_.isObject(handler) && !_.isFunction(handler)){
260
+ context = handler.context;
261
+ handler = handler.callback;
258
262
  }
259
-
260
- return function(){
261
- var args = Array.prototype.slice.apply(arguments);
262
- return config.callback.apply(config.context, args);
263
+
264
+ this.setHandler(name, handler, context);
265
+ }, this);
266
+ },
267
+
268
+ // Add a handler for the given name, with an
269
+ // optional context to run the handler within
270
+ setHandler: function(name, handler, context){
271
+ var config = {
272
+ callback: handler,
273
+ context: context
274
+ };
275
+
276
+ this._wreqrHandlers[name] = config;
277
+
278
+ this.trigger("handler:add", name, handler, context);
279
+ },
280
+
281
+ // Determine whether or not a handler is registered
282
+ hasHandler: function(name){
283
+ return !! this._wreqrHandlers[name];
284
+ },
285
+
286
+ // Get the currently registered handler for
287
+ // the specified name. Throws an exception if
288
+ // no handler is found.
289
+ getHandler: function(name){
290
+ var config = this._wreqrHandlers[name];
291
+
292
+ if (!config){
293
+ throw new Error("Handler not found for '" + name + "'");
294
+ }
295
+
296
+ return function(){
297
+ var args = Array.prototype.slice.apply(arguments);
298
+ return config.callback.apply(config.context, args);
299
+ };
300
+ },
301
+
302
+ // Remove a handler for the specified name
303
+ removeHandler: function(name){
304
+ delete this._wreqrHandlers[name];
305
+ },
306
+
307
+ // Remove all handlers from this registry
308
+ removeAllHandlers: function(){
309
+ this._wreqrHandlers = {};
310
+ }
311
+ });
312
+
313
+ return Handlers;
314
+ })(Backbone, _);
315
+
316
+ // Wreqr.CommandStorage
317
+ // --------------------
318
+ //
319
+ // Store and retrieve commands for execution.
320
+ Wreqr.CommandStorage = (function(){
321
+ "use strict";
322
+
323
+ // Constructor function
324
+ var CommandStorage = function(options){
325
+ this.options = options;
326
+ this._commands = {};
327
+
328
+ if (_.isFunction(this.initialize)){
329
+ this.initialize(options);
330
+ }
331
+ };
332
+
333
+ // Instance methods
334
+ _.extend(CommandStorage.prototype, Backbone.Events, {
335
+
336
+ // Get an object literal by command name, that contains
337
+ // the `commandName` and the `instances` of all commands
338
+ // represented as an array of arguments to process
339
+ getCommands: function(commandName){
340
+ var commands = this._commands[commandName];
341
+
342
+ // we don't have it, so add it
343
+ if (!commands){
344
+
345
+ // build the configuration
346
+ commands = {
347
+ command: commandName,
348
+ instances: []
263
349
  };
264
- },
265
-
266
- // Remove a handler for the specified name
267
- removeHandler: function(name){
268
- delete this._handlers[name];
269
- },
270
-
271
- // Remove all handlers from this registry
272
- removeAllHandlers: function(){
273
- this._handlers = {};
350
+
351
+ // store it
352
+ this._commands[commandName] = commands;
274
353
  }
275
- });
276
-
277
- return Handlers;
278
- })(Backbone, _);
279
-
354
+
355
+ return commands;
356
+ },
357
+
358
+ // Add a command by name, to the storage and store the
359
+ // args for the command
360
+ addCommand: function(commandName, args){
361
+ var command = this.getCommands(commandName);
362
+ command.instances.push(args);
363
+ },
364
+
365
+ // Clear all commands for the given `commandName`
366
+ clearCommands: function(commandName){
367
+ var command = this.getCommands(commandName);
368
+ command.instances = [];
369
+ }
370
+ });
371
+
372
+ return CommandStorage;
373
+ })();
374
+
280
375
  // Wreqr.Commands
281
- // --------------
282
- //
283
- // A simple command pattern implementation. Register a command
284
- // handler and execute it.
285
- Wreqr.Commands = (function(Wreqr){
286
- "use strict";
287
-
288
- return Wreqr.Handlers.extend({
289
- execute: function(){
290
- var name = arguments[0];
291
- var args = Array.prototype.slice.call(arguments, 1);
292
-
376
+ // --------------
377
+ //
378
+ // A simple command pattern implementation. Register a command
379
+ // handler and execute it.
380
+ Wreqr.Commands = (function(Wreqr){
381
+ "use strict";
382
+
383
+ return Wreqr.Handlers.extend({
384
+ // default storage type
385
+ storageType: Wreqr.CommandStorage,
386
+
387
+ constructor: function(options){
388
+ this.options = options || {};
389
+
390
+ this._initializeStorage(this.options);
391
+ this.on("handler:add", this._executeCommands, this);
392
+
393
+ var args = Array.prototype.slice.call(arguments);
394
+ Wreqr.Handlers.prototype.constructor.apply(this, args);
395
+ },
396
+
397
+ // Execute a named command with the supplied args
398
+ execute: function(name, args){
399
+ name = arguments[0];
400
+ args = Array.prototype.slice.call(arguments, 1);
401
+
402
+ if (this.hasHandler(name)){
293
403
  this.getHandler(name).apply(this, args);
404
+ } else {
405
+ this.storage.addCommand(name, args);
294
406
  }
295
- });
296
-
297
- })(Wreqr);
298
-
299
- // Wreqr.RequestResponse
300
- // ---------------------
301
- //
302
- // A simple request/response implementation. Register a
303
- // request handler, and return a response from it
304
- Wreqr.RequestResponse = (function(Wreqr){
305
- "use strict";
306
-
307
- return Wreqr.Handlers.extend({
308
- request: function(){
309
- var name = arguments[0];
310
- var args = Array.prototype.slice.call(arguments, 1);
311
-
312
- return this.getHandler(name).apply(this, args);
407
+
408
+ },
409
+
410
+ // Internal method to handle bulk execution of stored commands
411
+ _executeCommands: function(name, handler, context){
412
+ var command = this.storage.getCommands(name);
413
+
414
+ // loop through and execute all the stored command instances
415
+ _.each(command.instances, function(args){
416
+ handler.apply(context, args);
417
+ });
418
+
419
+ this.storage.clearCommands(name);
420
+ },
421
+
422
+ // Internal method to initialize storage either from the type's
423
+ // `storageType` or the instance `options.storageType`.
424
+ _initializeStorage: function(options){
425
+ var storage;
426
+
427
+ var StorageType = options.storageType || this.storageType;
428
+ if (_.isFunction(StorageType)){
429
+ storage = new StorageType();
430
+ } else {
431
+ storage = StorageType;
313
432
  }
314
- });
315
-
316
- })(Wreqr);
317
-
433
+
434
+ this.storage = storage;
435
+ }
436
+ });
437
+
438
+ })(Wreqr);
439
+
440
+ // Wreqr.RequestResponse
441
+ // ---------------------
442
+ //
443
+ // A simple request/response implementation. Register a
444
+ // request handler, and return a response from it
445
+ Wreqr.RequestResponse = (function(Wreqr){
446
+ "use strict";
447
+
448
+ return Wreqr.Handlers.extend({
449
+ request: function(){
450
+ var name = arguments[0];
451
+ var args = Array.prototype.slice.call(arguments, 1);
452
+
453
+ return this.getHandler(name).apply(this, args);
454
+ }
455
+ });
456
+
457
+ })(Wreqr);
458
+
318
459
  // Event Aggregator
319
- // ----------------
320
- // A pub-sub object that can be used to decouple various parts
321
- // of an application through event-driven architecture.
322
-
323
- Wreqr.EventAggregator = (function(Backbone, _){
324
- "use strict";
325
- var EA = function(){};
326
-
327
- // Copy the `extend` function used by Backbone's classes
328
- EA.extend = Backbone.Model.extend;
329
-
330
- // Copy the basic Backbone.Events on to the event aggregator
331
- _.extend(EA.prototype, Backbone.Events);
332
-
333
- return EA;
334
- })(Backbone, _);
335
-
460
+ // ----------------
461
+ // A pub-sub object that can be used to decouple various parts
462
+ // of an application through event-driven architecture.
463
+
464
+ Wreqr.EventAggregator = (function(Backbone, _){
465
+ "use strict";
466
+ var EA = function(){};
467
+
468
+ // Copy the `extend` function used by Backbone's classes
469
+ EA.extend = Backbone.Model.extend;
470
+
471
+ // Copy the basic Backbone.Events on to the event aggregator
472
+ _.extend(EA.prototype, Backbone.Events);
473
+
474
+ return EA;
475
+ })(Backbone, _);
476
+
336
477
 
337
478
  return Wreqr;
338
479
  })(Backbone, Backbone.Marionette, _);
339
480
 
340
- var Marionette = (function(Backbone, _, $){
481
+ var Marionette = (function(global, Backbone, _){
341
482
  "use strict";
342
483
 
484
+ // Define and export the Marionette namespace
343
485
  var Marionette = {};
344
486
  Backbone.Marionette = Marionette;
345
487
 
488
+ // Get the DOM manipulator for later use
489
+ Marionette.$ = Backbone.$;
490
+
346
491
  // Helpers
347
492
  // -------
348
493
 
349
494
  // For slicing `arguments` in functions
350
- var slice = Array.prototype.slice;
495
+ var protoSlice = Array.prototype.slice;
496
+ function slice(args) {
497
+ return protoSlice.call(args);
498
+ }
499
+
500
+ function throwError(message, name) {
501
+ var error = new Error(message);
502
+ error.name = name || 'Error';
503
+ throw error;
504
+ }
351
505
 
352
506
  // Marionette.extend
353
507
  // -----------------
@@ -373,55 +527,6 @@ Marionette.getOption = function(target, optionName){
373
527
  return value;
374
528
  };
375
529
 
376
- // Mairionette.createObject
377
- // ------------------------
378
-
379
- // A wrapper / shim for `Object.create`. Uses native `Object.create`
380
- // if available, otherwise shims it in place for Marionette to use.
381
- Marionette.createObject = (function(){
382
- var createObject;
383
-
384
- // Define this once, and just replace the .prototype on it as needed,
385
- // to improve performance in older / less optimized JS engines
386
- function F() {}
387
-
388
-
389
- // Check for existing native / shimmed Object.create
390
- if (typeof Object.create === "function"){
391
-
392
- // found native/shim, so use it
393
- createObject = Object.create;
394
-
395
- } else {
396
-
397
- // An implementation of the Boodman/Crockford delegation
398
- // w/ Cornford optimization, as suggested by @unscriptable
399
- // https://gist.github.com/3959151
400
-
401
- // native/shim not found, so shim it ourself
402
- createObject = function (o) {
403
-
404
- // set the prototype of the function
405
- // so we will get `o` as the prototype
406
- // of the new object instance
407
- F.prototype = o;
408
-
409
- // create a new object that inherits from
410
- // the `o` parameter
411
- var child = new F();
412
-
413
- // clean up just in case o is really large
414
- F.prototype = null;
415
-
416
- // send it back
417
- return child;
418
- };
419
-
420
- }
421
-
422
- return createObject;
423
- })();
424
-
425
530
  // Trigger an event and a corresponding method name. Examples:
426
531
  //
427
532
  // `this.triggerMethod("foo")` will trigger the "foo" event and
@@ -429,25 +534,35 @@ Marionette.createObject = (function(){
429
534
  //
430
535
  // `this.triggerMethod("foo:bar") will trigger the "foo:bar" event and
431
536
  // call the "onFooBar" method.
432
- Marionette.triggerMethod = function(){
433
- var args = Array.prototype.slice.apply(arguments);
434
- var eventName = args[0];
435
- var segments = eventName.split(":");
436
- var segment, capLetter, methodName = "on";
437
-
438
- for (var i = 0; i < segments.length; i++){
439
- segment = segments[i];
440
- capLetter = segment.charAt(0).toUpperCase();
441
- methodName += capLetter + segment.slice(1);
537
+ Marionette.triggerMethod = (function(){
538
+
539
+ // split the event name on the :
540
+ var splitter = /(^|:)(\w)/gi;
541
+
542
+ // take the event section ("section1:section2:section3")
543
+ // and turn it in to uppercase name
544
+ function getEventName(match, prefix, eventName) {
545
+ return eventName.toUpperCase();
442
546
  }
443
547
 
444
- this.trigger.apply(this, args);
548
+ // actual triggerMethod name
549
+ var triggerMethod = function(event) {
550
+ // get the method name from the event name
551
+ var methodName = 'on' + event.replace(splitter, getEventName);
552
+ var method = this[methodName];
445
553
 
446
- if (_.isFunction(this[methodName])){
447
- args.shift();
448
- return this[methodName].apply(this, args);
449
- }
450
- };
554
+ // trigger the event
555
+ this.trigger.apply(this, arguments);
556
+
557
+ // call the onMethodName if it exists
558
+ if (_.isFunction(method)) {
559
+ // pass all arguments, except the event name
560
+ return method.apply(this, _.tail(arguments));
561
+ }
562
+ };
563
+
564
+ return triggerMethod;
565
+ })();
451
566
 
452
567
  // DOMRefresh
453
568
  // ----------
@@ -498,7 +613,7 @@ Marionette.MonitorDOMRefresh = (function(){
498
613
  // These methods are used to bind/unbind a backbone "entity" (collection/model)
499
614
  // to methods on a target object.
500
615
  //
501
- // The first paremter, `target`, must have a `listenTo` method from the
616
+ // The first parameter, `target`, must have a `listenTo` method from the
502
617
  // EventBinder object.
503
618
  //
504
619
  // The second parameter is the entity (Backbone.Model or Backbone.Collection)
@@ -520,7 +635,7 @@ Marionette.MonitorDOMRefresh = (function(){
520
635
 
521
636
  var method = target[methodName];
522
637
  if(!method) {
523
- throw new Error("Method '"+ methodName +"' was configured as an event handler, but does not exist.");
638
+ throwError("Method '"+ methodName +"' was configured as an event handler, but does not exist.");
524
639
  }
525
640
 
526
641
  target.listenTo(entity, evt, method, target);
@@ -583,7 +698,7 @@ Marionette.MonitorDOMRefresh = (function(){
583
698
 
584
699
  })(Marionette);
585
700
 
586
-
701
+
587
702
  // Callbacks
588
703
  // ---------
589
704
 
@@ -591,7 +706,7 @@ Marionette.MonitorDOMRefresh = (function(){
591
706
  // and executing them at a later point in time, using jQuery's
592
707
  // `Deferred` object.
593
708
  Marionette.Callbacks = function(){
594
- this._deferred = $.Deferred();
709
+ this._deferred = Marionette.$.Deferred();
595
710
  this._callbacks = [];
596
711
  };
597
712
 
@@ -619,13 +734,13 @@ _.extend(Marionette.Callbacks.prototype, {
619
734
  // Resets the list of callbacks to be run, allowing the same list
620
735
  // to be run multiple times - whenever the `run` method is called.
621
736
  reset: function(){
622
- var that = this;
623
737
  var callbacks = this._callbacks;
624
- this._deferred = $.Deferred();
738
+ this._deferred = Marionette.$.Deferred();
625
739
  this._callbacks = [];
740
+
626
741
  _.each(callbacks, function(cb){
627
- that.add(cb.cb, cb.ctx);
628
- });
742
+ this.add(cb.cb, cb.ctx);
743
+ }, this);
629
744
  }
630
745
  });
631
746
 
@@ -739,12 +854,28 @@ _.extend(Marionette.Region, {
739
854
  }
740
855
 
741
856
  // build the region instance
742
-
743
- var regionManager = new RegionType({
857
+ var region = new RegionType({
744
858
  el: selector
745
859
  });
746
860
 
747
- return regionManager;
861
+ // override the `getEl` function if we have a parentEl
862
+ // this must be overridden to ensure the selector is found
863
+ // on the first use of the region. if we try to assign the
864
+ // region's `el` to `parentEl.find(selector)` in the object
865
+ // literal to build the region, the element will not be
866
+ // guaranteed to be in the DOM already, and will cause problems
867
+ if (regionConfig.parentEl){
868
+
869
+ region.getEl = function(selector) {
870
+ var parentEl = regionConfig.parentEl;
871
+ if (_.isFunction(parentEl)){
872
+ parentEl = parentEl();
873
+ }
874
+ return parentEl.find(selector);
875
+ };
876
+ }
877
+
878
+ return region;
748
879
  }
749
880
 
750
881
  });
@@ -762,10 +893,14 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
762
893
  show: function(view){
763
894
 
764
895
  this.ensureEl();
765
- this.close();
766
896
 
767
- view.render();
768
- this.open(view);
897
+ if (view !== this.currentView) {
898
+ this.close();
899
+ view.render();
900
+ this.open(view);
901
+ } else {
902
+ view.render();
903
+ }
769
904
 
770
905
  Marionette.triggerMethod.call(view, "show");
771
906
  Marionette.triggerMethod.call(this, "show", view);
@@ -782,7 +917,7 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
782
917
  // Override this method to change how the region finds the
783
918
  // DOM element that it manages. Return a jQuery selector object.
784
919
  getEl: function(selector){
785
- return $(selector);
920
+ return Marionette.$(selector);
786
921
  },
787
922
 
788
923
  // Override this method to change how the new view is
@@ -797,7 +932,10 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
797
932
  var view = this.currentView;
798
933
  if (!view || view.isClosed){ return; }
799
934
 
935
+ // call 'close' or 'remove', depending on which is found
800
936
  if (view.close) { view.close(); }
937
+ else if (view.remove) { view.remove(); }
938
+
801
939
  Marionette.triggerMethod.call(this, "close");
802
940
 
803
941
  delete this.currentView;
@@ -824,6 +962,133 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
824
962
  // Copy the `extend` function used by Backbone's classes
825
963
  Marionette.Region.extend = Marionette.extend;
826
964
 
965
+ // Marionette.RegionManager
966
+ // ------------------------
967
+ //
968
+ // Manage one or more related `Marionette.Region` objects.
969
+ Marionette.RegionManager = (function(Marionette){
970
+
971
+ var RegionManager = Marionette.Controller.extend({
972
+ constructor: function(options){
973
+ this._regions = {};
974
+ Marionette.Controller.prototype.constructor.call(this, options);
975
+ },
976
+
977
+ // Add multiple regions using an object literal, where
978
+ // each key becomes the region name, and each value is
979
+ // the region definition.
980
+ addRegions: function(regionDefinitions, defaults){
981
+ var regions = {};
982
+
983
+ _.each(regionDefinitions, function(definition, name){
984
+ if (typeof definition === "string"){
985
+ definition = { selector: definition };
986
+ }
987
+
988
+ if (definition.selector){
989
+ definition = _.defaults({}, definition, defaults);
990
+ }
991
+
992
+ var region = this.addRegion(name, definition);
993
+ regions[name] = region;
994
+ }, this);
995
+
996
+ return regions;
997
+ },
998
+
999
+ // Add an individual region to the region manager,
1000
+ // and return the region instance
1001
+ addRegion: function(name, definition){
1002
+ var region;
1003
+
1004
+ var isObject = _.isObject(definition);
1005
+ var isString = _.isString(definition);
1006
+ var hasSelector = !!definition.selector;
1007
+
1008
+ if (isString || (isObject && hasSelector)){
1009
+ region = Marionette.Region.buildRegion(definition, Marionette.Region);
1010
+ } else if (_.isFunction(definition)){
1011
+ region = Marionette.Region.buildRegion(definition, Marionette.Region);
1012
+ } else {
1013
+ region = definition;
1014
+ }
1015
+
1016
+ this._store(name, region);
1017
+ this.triggerMethod("region:add", name, region);
1018
+ return region;
1019
+ },
1020
+
1021
+ // Get a region by name
1022
+ get: function(name){
1023
+ return this._regions[name];
1024
+ },
1025
+
1026
+ // Remove a region by name
1027
+ removeRegion: function(name){
1028
+ var region = this._regions[name];
1029
+ this._remove(name, region);
1030
+ },
1031
+
1032
+ // Close all regions in the region manager, and
1033
+ // remove them
1034
+ removeRegions: function(){
1035
+ _.each(this._regions, function(region, name){
1036
+ this._remove(name, region);
1037
+ }, this);
1038
+ },
1039
+
1040
+ // Close all regions in the region manager, but
1041
+ // leave them attached
1042
+ closeRegions: function(){
1043
+ _.each(this._regions, function(region, name){
1044
+ region.close();
1045
+ }, this);
1046
+ },
1047
+
1048
+ // Close all regions and shut down the region
1049
+ // manager entirely
1050
+ close: function(){
1051
+ this.removeRegions();
1052
+ var args = Array.prototype.slice.call(arguments);
1053
+ Marionette.Controller.prototype.close.apply(this, args);
1054
+ },
1055
+
1056
+ // internal method to store regions
1057
+ _store: function(name, region){
1058
+ this._regions[name] = region;
1059
+ this.length = _.size(this._regions);
1060
+ },
1061
+
1062
+ // internal method to remove a region
1063
+ _remove: function(name, region){
1064
+ region.close();
1065
+ delete this._regions[name];
1066
+ this.triggerMethod("region:remove", name, region);
1067
+ }
1068
+
1069
+ });
1070
+
1071
+ // Borrowing this code from Backbone.Collection:
1072
+ // http://backbonejs.org/docs/backbone.html#section-106
1073
+ //
1074
+ // Mix in methods from Underscore, for iteration, and other
1075
+ // collection related features.
1076
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
1077
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
1078
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
1079
+ 'last', 'without', 'isEmpty', 'pluck'];
1080
+
1081
+ _.each(methods, function(method) {
1082
+ RegionManager.prototype[method] = function() {
1083
+ var regions = _.values(this._regions);
1084
+ var args = [regions].concat(_.toArray(arguments));
1085
+ return _[method].apply(_, args);
1086
+ };
1087
+ });
1088
+
1089
+ return RegionManager;
1090
+ })(Marionette);
1091
+
827
1092
 
828
1093
  // Template Cache
829
1094
  // --------------
@@ -844,7 +1109,6 @@ _.extend(Marionette.TemplateCache, {
844
1109
  // retrieves the cached version, or loads it
845
1110
  // from the DOM.
846
1111
  get: function(templateId){
847
- var that = this;
848
1112
  var cachedTemplate = this.templateCaches[templateId];
849
1113
 
850
1114
  if (!cachedTemplate){
@@ -864,7 +1128,7 @@ _.extend(Marionette.TemplateCache, {
864
1128
  // `clear("#t1", "#t2", "...")`
865
1129
  clear: function(){
866
1130
  var i;
867
- var args = Array.prototype.slice.apply(arguments);
1131
+ var args = slice(arguments);
868
1132
  var length = args.length;
869
1133
 
870
1134
  if (length > 0){
@@ -884,8 +1148,6 @@ _.extend(Marionette.TemplateCache.prototype, {
884
1148
 
885
1149
  // Internal method to load the template
886
1150
  load: function(){
887
- var that = this;
888
-
889
1151
  // Guard clause to prevent loading this template more than once
890
1152
  if (this.compiledTemplate){
891
1153
  return this.compiledTemplate;
@@ -904,13 +1166,10 @@ _.extend(Marionette.TemplateCache.prototype, {
904
1166
  // using a template-loader plugin as described here:
905
1167
  // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
906
1168
  loadTemplate: function(templateId){
907
- var template = $(templateId).html();
1169
+ var template = Marionette.$(templateId).html();
908
1170
 
909
1171
  if (!template || template.length === 0){
910
- var msg = "Could not find template: '" + templateId + "'";
911
- var err = new Error(msg);
912
- err.name = "NoTemplateError";
913
- throw err;
1172
+ throwError("Could not find template: '" + templateId + "'", "NoTemplateError");
914
1173
  }
915
1174
 
916
1175
  return template;
@@ -939,8 +1198,7 @@ Marionette.Renderer = {
939
1198
  // custom rendering and template handling for all of Marionette.
940
1199
  render: function(template, data){
941
1200
  var templateFunc = typeof template === 'function' ? template : Marionette.TemplateCache.get(template);
942
- var html = templateFunc(data);
943
- return html;
1201
+ return templateFunc(data);
944
1202
  }
945
1203
  };
946
1204
 
@@ -993,7 +1251,6 @@ Marionette.View = Backbone.View.extend({
993
1251
  configureTriggers: function(){
994
1252
  if (!this.triggers) { return; }
995
1253
 
996
- var that = this;
997
1254
  var triggerEvents = {};
998
1255
 
999
1256
  // Allow `triggers` to be configured as a function
@@ -1010,7 +1267,7 @@ Marionette.View = Backbone.View.extend({
1010
1267
  if (e && e.preventDefault){ e.preventDefault(); }
1011
1268
  if (e && e.stopPropagation){ e.stopPropagation(); }
1012
1269
 
1013
- // buil the args for the event
1270
+ // build the args for the event
1014
1271
  var args = {
1015
1272
  view: this,
1016
1273
  model: this.model,
@@ -1018,10 +1275,10 @@ Marionette.View = Backbone.View.extend({
1018
1275
  };
1019
1276
 
1020
1277
  // trigger the event
1021
- that.triggerMethod(value, args);
1278
+ this.triggerMethod(value, args);
1022
1279
  };
1023
1280
 
1024
- });
1281
+ }, this);
1025
1282
 
1026
1283
  return triggerEvents;
1027
1284
  },
@@ -1073,6 +1330,9 @@ Marionette.View = Backbone.View.extend({
1073
1330
  return;
1074
1331
  }
1075
1332
 
1333
+ // unbind UI elements
1334
+ this.unbindUIElements();
1335
+
1076
1336
  // mark as closed before doing the actual close, to
1077
1337
  // prevent infinite loops within "close" event handlers
1078
1338
  // that are trying to close other views
@@ -1087,20 +1347,37 @@ Marionette.View = Backbone.View.extend({
1087
1347
  bindUIElements: function(){
1088
1348
  if (!this.ui) { return; }
1089
1349
 
1090
- var that = this;
1091
-
1092
- if (!this.uiBindings) {
1093
- // We want to store the ui hash in uiBindings, since afterwards the values in the ui hash
1094
- // will be overridden with jQuery selectors.
1095
- this.uiBindings = _.result(this, "ui");
1350
+ // store the ui hash in _uiBindings so they can be reset later
1351
+ // and so re-rendering the view will be able to find the bindings
1352
+ if (!this._uiBindings){
1353
+ this._uiBindings = this.ui;
1096
1354
  }
1097
1355
 
1098
- // refreshing the associated selectors since they should point to the newly rendered elements.
1356
+ // get the bindings result, as a function or otherwise
1357
+ var bindings = _.result(this, "_uiBindings");
1358
+
1359
+ // empty the ui so we don't have anything to start with
1099
1360
  this.ui = {};
1100
- _.each(_.keys(this.uiBindings), function(key) {
1101
- var selector = that.uiBindings[key];
1102
- that.ui[key] = that.$(selector);
1103
- });
1361
+
1362
+ // bind each of the selectors
1363
+ _.each(_.keys(bindings), function(key) {
1364
+ var selector = bindings[key];
1365
+ this.ui[key] = this.$(selector);
1366
+ }, this);
1367
+ },
1368
+
1369
+ // This method unbinds the elements specified in the "ui" hash
1370
+ unbindUIElements: function(){
1371
+ if (!this.ui){ return; }
1372
+
1373
+ // delete all of the existing ui bindings
1374
+ _.each(this.ui, function($el, name){
1375
+ delete this.ui[name];
1376
+ }, this);
1377
+
1378
+ // reset the ui element to the original bindings configuration
1379
+ this.ui = this._uiBindings;
1380
+ delete this._uiBindings;
1104
1381
  }
1105
1382
  });
1106
1383
 
@@ -1112,8 +1389,7 @@ Marionette.View = Backbone.View.extend({
1112
1389
  // and calling several methods on extended views, such as `onRender`.
1113
1390
  Marionette.ItemView = Marionette.View.extend({
1114
1391
  constructor: function(){
1115
- var args = Array.prototype.slice.apply(arguments);
1116
- Marionette.View.prototype.constructor.apply(this, args);
1392
+ Marionette.View.prototype.constructor.apply(this, slice(arguments));
1117
1393
  },
1118
1394
 
1119
1395
  // Serialize the model or collection for the view. If a model is
@@ -1167,8 +1443,7 @@ Marionette.ItemView = Marionette.View.extend({
1167
1443
 
1168
1444
  this.triggerMethod('item:before:close');
1169
1445
 
1170
- var args = Array.prototype.slice.apply(arguments);
1171
- Marionette.View.prototype.close.apply(this, args);
1446
+ Marionette.View.prototype.close.apply(this, slice(arguments));
1172
1447
 
1173
1448
  this.triggerMethod('item:closed');
1174
1449
  }
@@ -1188,8 +1463,7 @@ Marionette.CollectionView = Marionette.View.extend({
1188
1463
  constructor: function(options){
1189
1464
  this._initChildViewStorage();
1190
1465
 
1191
- var args = Array.prototype.slice.apply(arguments);
1192
- Marionette.View.prototype.constructor.apply(this, args);
1466
+ Marionette.View.prototype.constructor.apply(this, slice(arguments));
1193
1467
 
1194
1468
  this._initialEvents();
1195
1469
  },
@@ -1263,12 +1537,11 @@ Marionette.CollectionView = Marionette.View.extend({
1263
1537
  // Internal method to loop through each item in the
1264
1538
  // collection view and show it
1265
1539
  showCollection: function(){
1266
- var that = this;
1267
1540
  var ItemView;
1268
1541
  this.collection.each(function(item, index){
1269
- ItemView = that.getItemView(item);
1270
- that.addItemView(item, ItemView, index);
1271
- });
1542
+ ItemView = this.getItemView(item);
1543
+ this.addItemView(item, ItemView, index);
1544
+ }, this);
1272
1545
  },
1273
1546
 
1274
1547
  // Internal method to show an empty view in place of
@@ -1301,9 +1574,7 @@ Marionette.CollectionView = Marionette.View.extend({
1301
1574
  var itemView = Marionette.getOption(this, "itemView");
1302
1575
 
1303
1576
  if (!itemView){
1304
- var err = new Error("An `itemView` must be specified");
1305
- err.name = "NoItemViewError";
1306
- throw err;
1577
+ throwError("An `itemView` must be specified", "NoItemViewError");
1307
1578
  }
1308
1579
 
1309
1580
  return itemView;
@@ -1312,12 +1583,10 @@ Marionette.CollectionView = Marionette.View.extend({
1312
1583
  // Render the child item's view and add it to the
1313
1584
  // HTML for the collection view.
1314
1585
  addItemView: function(item, ItemView, index){
1315
- var that = this;
1316
-
1317
1586
  // get the itemViewOptions if any were specified
1318
1587
  var itemViewOptions = Marionette.getOption(this, "itemViewOptions");
1319
1588
  if (_.isFunction(itemViewOptions)){
1320
- itemViewOptions = itemViewOptions.call(this, item);
1589
+ itemViewOptions = itemViewOptions.call(this, item, index);
1321
1590
  }
1322
1591
 
1323
1592
  // build the view
@@ -1354,7 +1623,7 @@ Marionette.CollectionView = Marionette.View.extend({
1354
1623
  // Forward all child item view events through the parent,
1355
1624
  // prepending "itemview:" to the event name
1356
1625
  this.listenTo(view, "all", function(){
1357
- var args = slice.call(arguments);
1626
+ var args = slice(arguments);
1358
1627
  args[0] = prefix + ":" + args[0];
1359
1628
  args.splice(1, 0, view);
1360
1629
 
@@ -1371,8 +1640,7 @@ Marionette.CollectionView = Marionette.View.extend({
1371
1640
  // Build an `itemView` for every model in the collection.
1372
1641
  buildItemView: function(item, ItemViewType, itemViewOptions){
1373
1642
  var options = _.extend({model: item}, itemViewOptions);
1374
- var view = new ItemViewType(options);
1375
- return view;
1643
+ return new ItemViewType(options);
1376
1644
  },
1377
1645
 
1378
1646
  // get the child view by item it holds, and remove it
@@ -1390,9 +1658,9 @@ Marionette.CollectionView = Marionette.View.extend({
1390
1658
  if (view){
1391
1659
  this.stopListening(view);
1392
1660
 
1393
- if (view.close){
1394
- view.close();
1395
- }
1661
+ // call 'close' or 'remove', depending on which is found
1662
+ if (view.close) { view.close(); }
1663
+ else if (view.remove) { view.remove(); }
1396
1664
 
1397
1665
  this.children.remove(view);
1398
1666
  }
@@ -1431,8 +1699,7 @@ Marionette.CollectionView = Marionette.View.extend({
1431
1699
  this.closeChildren();
1432
1700
  this.triggerMethod("collection:closed");
1433
1701
 
1434
- var args = Array.prototype.slice.apply(arguments);
1435
- Marionette.View.prototype.close.apply(this, args);
1702
+ Marionette.View.prototype.close.apply(this, slice(arguments));
1436
1703
  },
1437
1704
 
1438
1705
  // Close the child views that this collection view
@@ -1454,8 +1721,7 @@ Marionette.CollectionView = Marionette.View.extend({
1454
1721
  // an item view as `modelView`, for the top leaf
1455
1722
  Marionette.CompositeView = Marionette.CollectionView.extend({
1456
1723
  constructor: function(options){
1457
- var args = Array.prototype.slice.apply(arguments);
1458
- Marionette.CollectionView.apply(this, args);
1724
+ Marionette.CollectionView.apply(this, slice(arguments));
1459
1725
 
1460
1726
  this.itemView = this.getItemView();
1461
1727
  },
@@ -1479,9 +1745,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1479
1745
  var itemView = Marionette.getOption(this, "itemView") || this.constructor;
1480
1746
 
1481
1747
  if (!itemView){
1482
- var err = new Error("An `itemView` must be specified");
1483
- err.name = "NoItemViewError";
1484
- throw err;
1748
+ throwError("An `itemView` must be specified", "NoItemViewError");
1485
1749
  }
1486
1750
 
1487
1751
  return itemView;
@@ -1504,6 +1768,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1504
1768
  // this again will tell the model's view to re-render itself
1505
1769
  // but the collection will not re-render.
1506
1770
  render: function(){
1771
+ this.isRendered = true;
1507
1772
  this.isClosed = false;
1508
1773
  this.resetItemViewContainer();
1509
1774
 
@@ -1524,15 +1789,10 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1524
1789
  },
1525
1790
 
1526
1791
  _renderChildren: function(){
1527
- Marionette.CollectionView.prototype._renderChildren.call(this);
1528
- this.triggerMethod("composite:collection:rendered");
1529
- },
1530
-
1531
- // Render the collection for the composite view
1532
- renderCollection: function(){
1533
- var args = Array.prototype.slice.apply(arguments);
1534
- Marionette.CollectionView.prototype.render.apply(this, args);
1535
-
1792
+ if (this.isRendered){
1793
+ Marionette.CollectionView.prototype._renderChildren.call(this);
1794
+ this.triggerMethod("composite:collection:rendered");
1795
+ }
1536
1796
  },
1537
1797
 
1538
1798
  // Render an individual model, if we have one, as
@@ -1569,9 +1829,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1569
1829
  var selector = _.result(containerView, "itemViewContainer");
1570
1830
  container = containerView.$(selector);
1571
1831
  if (container.length <= 0) {
1572
- var err = new Error("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer);
1573
- err.name = "ItemViewContainerMissingError";
1574
- throw err;
1832
+ throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError");
1575
1833
  }
1576
1834
 
1577
1835
  } else {
@@ -1603,14 +1861,15 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1603
1861
  Marionette.Layout = Marionette.ItemView.extend({
1604
1862
  regionType: Marionette.Region,
1605
1863
 
1606
- // Ensure the regions are avialable when the `initialize` method
1864
+ // Ensure the regions are available when the `initialize` method
1607
1865
  // is called.
1608
- constructor: function () {
1609
- this._firstRender = true;
1610
- this.initializeRegions();
1866
+ constructor: function (options) {
1867
+ options = options || {};
1611
1868
 
1612
- var args = Array.prototype.slice.apply(arguments);
1613
- Marionette.ItemView.apply(this, args);
1869
+ this._firstRender = true;
1870
+ this._initializeRegions(options);
1871
+
1872
+ Marionette.ItemView.call(this, options);
1614
1873
  },
1615
1874
 
1616
1875
  // Layout's render will use the existing region objects the
@@ -1623,11 +1882,14 @@ Marionette.Layout = Marionette.ItemView.extend({
1623
1882
  // if this is the first render, don't do anything to
1624
1883
  // reset the regions
1625
1884
  this._firstRender = false;
1885
+ } else if (this.isClosed){
1886
+ // a previously closed layout means we need to
1887
+ // completely re-initialize the regions
1888
+ this._initializeRegions();
1626
1889
  } else {
1627
1890
  // If this is not the first render call, then we need to
1628
1891
  // re-initializing the `el` for each region
1629
- this.closeRegions();
1630
- this.reInitializeRegions();
1892
+ this._reInitializeRegions();
1631
1893
  }
1632
1894
 
1633
1895
  var args = Array.prototype.slice.apply(arguments);
@@ -1639,75 +1901,82 @@ Marionette.Layout = Marionette.ItemView.extend({
1639
1901
  // Handle closing regions, and then close the view itself.
1640
1902
  close: function () {
1641
1903
  if (this.isClosed){ return; }
1642
-
1643
- this.closeRegions();
1644
- this.destroyRegions();
1645
-
1904
+ this.regionManager.close();
1646
1905
  var args = Array.prototype.slice.apply(arguments);
1647
1906
  Marionette.ItemView.prototype.close.apply(this, args);
1648
1907
  },
1649
1908
 
1650
- // Initialize the regions that have been defined in a
1651
- // `regions` attribute on this layout. The key of the
1652
- // hash becomes an attribute on the layout object directly.
1653
- // For example: `regions: { menu: ".menu-container" }`
1654
- // will product a `layout.menu` object which is a region
1655
- // that controls the `.menu-container` DOM element.
1656
- initializeRegions: function () {
1657
- if (!this.regionManagers){
1658
- this.regionManagers = {};
1659
- }
1909
+ // Add a single region, by name, to the layout
1910
+ addRegion: function(name, definition){
1911
+ var regions = {};
1912
+ regions[name] = definition;
1913
+ return this.addRegions(regions)[name];
1914
+ },
1660
1915
 
1661
- var that = this;
1662
- var regions = this.regions || {};
1663
- _.each(regions, function (region, name) {
1916
+ // Add multiple regions as a {name: definition, name2: def2} object literal
1917
+ addRegions: function(regions){
1918
+ this.regions = _.extend(this.regions || {}, regions);
1919
+ return this._buildRegions(regions);
1920
+ },
1664
1921
 
1665
- var regionManager = Marionette.Region.buildRegion(region, that.regionType);
1666
- regionManager.getEl = function(selector){
1667
- return that.$(selector);
1668
- };
1922
+ // Remove a single region from the Layout, by name
1923
+ removeRegion: function(name){
1924
+ return this.regionManager.removeRegion(name);
1925
+ },
1669
1926
 
1670
- that.regionManagers[name] = regionManager;
1671
- that[name] = regionManager;
1672
- });
1927
+ // internal method to build regions
1928
+ _buildRegions: function(regions){
1929
+ var that = this;
1673
1930
 
1931
+ var defaults = {
1932
+ parentEl: function(){ return that.$el; }
1933
+ };
1934
+
1935
+ return this.regionManager.addRegions(regions, defaults);
1674
1936
  },
1675
1937
 
1676
- // Re-initialize all of the regions by updating the `el` that
1677
- // they point to
1678
- reInitializeRegions: function(){
1679
- if (this.regionManagers && _.size(this.regionManagers)===0){
1680
- this.initializeRegions();
1938
+ // Internal method to initialize the regions that have been defined in a
1939
+ // `regions` attribute on this layout.
1940
+ _initializeRegions: function (options) {
1941
+ var regions;
1942
+ this._initRegionManager();
1943
+
1944
+ if (_.isFunction(this.regions)) {
1945
+ regions = this.regions(options);
1681
1946
  } else {
1682
- _.each(this.regionManagers, function(region){
1683
- region.reset();
1684
- });
1947
+ regions = this.regions || {};
1685
1948
  }
1949
+
1950
+ this.addRegions(regions);
1686
1951
  },
1687
1952
 
1688
- // Close all of the regions that have been opened by
1689
- // this layout. This method is called when the layout
1690
- // itself is closed.
1691
- closeRegions: function () {
1692
- var that = this;
1693
- _.each(this.regionManagers, function (manager, name) {
1694
- manager.close();
1953
+ // Internal method to re-initialize all of the regions by updating the `el` that
1954
+ // they point to
1955
+ _reInitializeRegions: function(){
1956
+ this.regionManager.closeRegions();
1957
+ this.regionManager.each(function(region){
1958
+ region.reset();
1695
1959
  });
1696
1960
  },
1697
1961
 
1698
- // Destroys all of the regions by removing references
1699
- // from the Layout
1700
- destroyRegions: function(){
1701
- var that = this;
1702
- _.each(this.regionManagers, function (manager, name) {
1703
- delete that[name];
1962
+ // Internal method to initialize the region manager
1963
+ // and all regions in it
1964
+ _initRegionManager: function(){
1965
+ this.regionManager = new Marionette.RegionManager();
1966
+
1967
+ this.listenTo(this.regionManager, "region:add", function(name, region){
1968
+ this[name] = region;
1969
+ this.trigger("region:add", name, region);
1970
+ });
1971
+
1972
+ this.listenTo(this.regionManager, "region:remove", function(name, region){
1973
+ delete this[name];
1974
+ this.trigger("region:remove", name, region);
1704
1975
  });
1705
- this.regionManagers = {};
1706
1976
  }
1707
1977
  });
1708
1978
 
1709
1979
 
1710
-
1711
1980
  // AppRouter
1712
1981
  // ---------
1713
1982
 
@@ -1720,7 +1989,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1720
1989
  //
1721
1990
  // App routers can only take one `controller` object.
1722
1991
  // It is recommended that you divide your controller
1723
- // objects in to smaller peices of related functionality
1992
+ // objects in to smaller pieces of related functionality
1724
1993
  // and have multiple routers / controllers, instead of
1725
1994
  // just one giant router and controller.
1726
1995
  //
@@ -1729,8 +1998,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1729
1998
  Marionette.AppRouter = Backbone.Router.extend({
1730
1999
 
1731
2000
  constructor: function(options){
1732
- var args = Array.prototype.slice.apply(arguments);
1733
- Backbone.Router.prototype.constructor.apply(this, args);
2001
+ Backbone.Router.prototype.constructor.apply(this, slice(arguments));
1734
2002
 
1735
2003
  this.options = options;
1736
2004
 
@@ -1744,33 +2012,15 @@ Marionette.AppRouter = Backbone.Router.extend({
1744
2012
  // router, and turn them in to routes that trigger the
1745
2013
  // specified method on the specified `controller`.
1746
2014
  processAppRoutes: function(controller, appRoutes){
1747
- var method, methodName;
1748
- var route, routesLength, i;
1749
- var routes = [];
1750
- var router = this;
1751
-
1752
- for(route in appRoutes){
1753
- if (appRoutes.hasOwnProperty(route)){
1754
- routes.unshift([route, appRoutes[route]]);
1755
- }
1756
- }
2015
+ _.each(appRoutes, function(methodName, route) {
2016
+ var method = controller[methodName];
1757
2017
 
1758
- routesLength = routes.length;
1759
- for (i = 0; i < routesLength; i++){
1760
- route = routes[i][0];
1761
- methodName = routes[i][1];
1762
- method = controller[methodName];
1763
-
1764
- if (!method){
1765
- var msg = "Method '" + methodName + "' was not found on the controller";
1766
- var err = new Error(msg);
1767
- err.name = "NoMethodError";
1768
- throw err;
2018
+ if (!method) {
2019
+ throw new Error("Method '" + methodName + "' was not found on the controller");
1769
2020
  }
1770
2021
 
1771
- method = _.bind(method, controller);
1772
- router.route(route, methodName, method);
1773
- }
2022
+ this.route(route, methodName, _.bind(method, controller));
2023
+ }, this);
1774
2024
  }
1775
2025
  });
1776
2026
 
@@ -1782,7 +2032,8 @@ Marionette.AppRouter = Backbone.Router.extend({
1782
2032
  // Stores and starts up `Region` objects, includes an
1783
2033
  // event aggregator as `app.vent`
1784
2034
  Marionette.Application = function(options){
1785
- this.initCallbacks = new Marionette.Callbacks();
2035
+ this._initRegionManager();
2036
+ this._initCallbacks = new Marionette.Callbacks();
1786
2037
  this.vent = new Backbone.Wreqr.EventAggregator();
1787
2038
  this.commands = new Backbone.Wreqr.Commands();
1788
2039
  this.reqres = new Backbone.Wreqr.RequestResponse();
@@ -1810,7 +2061,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1810
2061
  // method is called, or run immediately if added after `start`
1811
2062
  // has already been called.
1812
2063
  addInitializer: function(initializer){
1813
- this.initCallbacks.add(initializer);
2064
+ this._initCallbacks.add(initializer);
1814
2065
  },
1815
2066
 
1816
2067
  // kick off all of the application's processes.
@@ -1818,7 +2069,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1818
2069
  // to the app, and runs all of the initializer functions
1819
2070
  start: function(options){
1820
2071
  this.triggerMethod("initialize:before", options);
1821
- this.initCallbacks.run(options, this);
2072
+ this._initCallbacks.run(options, this);
1822
2073
  this.triggerMethod("initialize:after", options);
1823
2074
 
1824
2075
  this.triggerMethod("start", options);
@@ -1827,32 +2078,40 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1827
2078
  // Add regions to your app.
1828
2079
  // Accepts a hash of named strings or Region objects
1829
2080
  // addRegions({something: "#someRegion"})
1830
- // addRegions{{something: Region.extend({el: "#someRegion"}) });
2081
+ // addRegions({something: Region.extend({el: "#someRegion"}) });
1831
2082
  addRegions: function(regions){
1832
- var that = this;
1833
- _.each(regions, function (region, name) {
1834
- var regionManager = Marionette.Region.buildRegion(region, Marionette.Region);
1835
- that[name] = regionManager;
1836
- });
2083
+ return this._regionManager.addRegions(regions);
1837
2084
  },
1838
2085
 
1839
2086
  // Removes a region from your app.
1840
2087
  // Accepts the regions name
1841
2088
  // removeRegion('myRegion')
1842
2089
  removeRegion: function(region) {
1843
- this[region].close();
1844
- delete this[region];
2090
+ this._regionManager.removeRegion(region);
1845
2091
  },
1846
2092
 
1847
2093
  // Create a module, attached to the application
1848
2094
  module: function(moduleNames, moduleDefinition){
1849
2095
  // slice the args, and add this application object as the
1850
2096
  // first argument of the array
1851
- var args = slice.call(arguments);
2097
+ var args = slice(arguments);
1852
2098
  args.unshift(this);
1853
2099
 
1854
2100
  // see the Marionette.Module object for more information
1855
2101
  return Marionette.Module.create.apply(Marionette.Module, args);
2102
+ },
2103
+
2104
+ // Internal method to set up the region manager
2105
+ _initRegionManager: function(){
2106
+ this._regionManager = new Marionette.RegionManager();
2107
+
2108
+ this.listenTo(this._regionManager, "region:add", function(name, region){
2109
+ this[name] = region;
2110
+ });
2111
+
2112
+ this.listenTo(this._regionManager, "region:remove", function(name, region){
2113
+ delete this[name];
2114
+ });
1856
2115
  }
1857
2116
  });
1858
2117
 
@@ -1896,7 +2155,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1896
2155
  this._finalizerCallbacks.add(callback);
1897
2156
  },
1898
2157
 
1899
- // Start the module, and run all of it's initializers
2158
+ // Start the module, and run all of its initializers
1900
2159
  start: function(options){
1901
2160
  // Prevent re-starting a module that is already started
1902
2161
  if (this._isInitialized){ return; }
@@ -1904,11 +2163,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1904
2163
  // start the sub-modules (depth-first hierarchy)
1905
2164
  _.each(this.submodules, function(mod){
1906
2165
  // check to see if we should start the sub-module with this parent
1907
- var startWithParent = true;
1908
- startWithParent = mod.startWithParent;
1909
-
1910
- // start the sub-module
1911
- if (startWithParent){
2166
+ if (mod.startWithParent){
1912
2167
  mod.start(options);
1913
2168
  }
1914
2169
  });
@@ -1962,7 +2217,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1962
2217
  this.app,
1963
2218
  Backbone,
1964
2219
  Marionette,
1965
- $, _,
2220
+ Marionette.$, _,
1966
2221
  customArgs
1967
2222
  ]);
1968
2223
 
@@ -1983,12 +2238,11 @@ _.extend(Marionette.Module, {
1983
2238
 
1984
2239
  // Create a module, hanging off the app parameter as the parent object.
1985
2240
  create: function(app, moduleNames, moduleDefinition){
1986
- var that = this;
1987
2241
  var module = app;
1988
2242
 
1989
2243
  // get the custom args passed in after the module definition and
1990
2244
  // get rid of the module name and definition function
1991
- var customArgs = slice.apply(arguments);
2245
+ var customArgs = slice(arguments);
1992
2246
  customArgs.splice(0, 3);
1993
2247
 
1994
2248
  // split the module names and get the length
@@ -2002,9 +2256,9 @@ _.extend(Marionette.Module, {
2002
2256
  // Loop through all the parts of the module definition
2003
2257
  _.each(moduleNames, function(moduleName, i){
2004
2258
  var parentModule = module;
2005
- module = that._getModule(parentModule, moduleName, app);
2006
- that._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
2007
- });
2259
+ module = this._getModule(parentModule, moduleName, app);
2260
+ this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
2261
+ }, this);
2008
2262
 
2009
2263
  // Return the last module in the definition chain
2010
2264
  return module;
@@ -2051,7 +2305,6 @@ _.extend(Marionette.Module, {
2051
2305
 
2052
2306
  // `and` the two together, ensuring a single `false` will prevent it
2053
2307
  // from starting with the parent
2054
- var tmp = module.startWithParent;
2055
2308
  module.startWithParent = module.startWithParent && startWithParent;
2056
2309
 
2057
2310
  // setup auto-start if needed
@@ -2075,4 +2328,4 @@ _.extend(Marionette.Module, {
2075
2328
 
2076
2329
 
2077
2330
  return Marionette;
2078
- })(Backbone, _, $ || window.jQuery || window.Zepto || window.ender);
2331
+ })(this, Backbone, _);