marionette-rails 1.0.0.rc6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,11 +3,13 @@ marionette-rails
3
3
 
4
4
  [![Dependency Status](https://gemnasium.com/chancancode/marionette-rails.png)](https://gemnasium.com/chancancode/marionette-rails)
5
5
 
6
- This gem is a wrapper for Derick Bailey's [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) library. It vendors the javascript library code for use with Rails' asset pipeline (Rails 3.1+).
6
+ This gem is a wrapper for Derick Bailey's [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) library. It vendors the javascript library code for use with Rails' asset pipeline (Rails 3.1+).
7
+
8
+ This gem is currently maintained by [@chancancode](https://github.com/chancancode/) and [@wingrunr21](https://github.com/wingrunr21).
7
9
 
8
10
  ## Dependencies
9
11
 
10
- [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) depends on Backbone (and Backbone's dependencies). These dependencies are not currently managed by the `marionette-rails` gem directly, because there exists multiple options to use Backbone with the Rails asset pipeline, such as [`backbone-on-rails`](https://github.com/meleyal/backbone-on-rails), [`backbone-rails`](https://github.com/aflatter/backbone-rails), [`rails-backbone`](https://github.com/codebrew/backbone-rails), just to name a few.
12
+ [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) depends on Backbone (and Backbone's dependencies). These dependencies are not currently managed by the `marionette-rails` gem directly, because there exists multiple options to use Backbone with the Rails asset pipeline, such as [`backbone-on-rails`](https://github.com/meleyal/backbone-on-rails), [`backbone-rails`](https://github.com/aflatter/backbone-rails), [`rails-backbone`](https://github.com/codebrew/backbone-rails), just to name a few.
11
13
 
12
14
  ## Usage
13
15
 
@@ -30,20 +32,20 @@ Or, if you are using pure javascript, add this to `app/assets/javascripts/applic
30
32
 
31
33
  ## Versioning
32
34
 
33
- The gem will mirror the [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) versioning scheme. That is, version 0.8.2.* of `marionette-rails` would vendor [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) v0.8.2.
35
+ The gem will mirror the [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) versioning scheme. That is, version 0.8.2.* of `marionette-rails` would vendor [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) v0.8.2.
34
36
 
35
37
  ## Contributing
36
38
 
37
- For bugs in [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) itself, head over to their [issue tracker](https://github.com/derickbailey/backbone.marionette/issues). If you have a question, post it at [StackOverflow under the `backbone.marionette` tag](http://stackoverflow.com/questions/tagged/backbone.marionette).
39
+ For bugs in [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) itself, head over to their [issue tracker](https://github.com/marionettejs/backbone.marionette/issues). If you have a question, post it at [StackOverflow under the `backbone.marionette` tag](http://stackoverflow.com/questions/tagged/backbone.marionette).
38
40
 
39
41
  For bugs in this gem distribution, use the [GitHub issue tracker](https://github.com/chancancode/marionette-rails/issues). If you could submit a pull request - that's even better!
40
42
 
41
43
  ## Donations
42
44
 
43
- If you're using Marionette and you're finding that it is saving you time and effort, then please consider donating to the upstream [Backbone.Marionette](https://github.com/derickbailey/backbone.marionette) project.
45
+ If you're using Marionette and you're finding that it is saving you time and effort, then please consider donating to the upstream [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) project.
44
46
 
45
47
  [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7SJHYWJ487SF4)
46
48
 
47
49
  ## License
48
50
 
49
- This library is distributed under the MIT license. Please see the LICENSE file.
51
+ This library is distributed under the MIT license. Please see the LICENSE file.
@@ -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.0
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,131 @@ _.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 {
1011
+ region = definition;
1012
+ }
1013
+
1014
+ this._store(name, region);
1015
+ this.triggerMethod("region:add", name, region);
1016
+ return region;
1017
+ },
1018
+
1019
+ // Get a region by name
1020
+ get: function(name){
1021
+ return this._regions[name];
1022
+ },
1023
+
1024
+ // Remove a region by name
1025
+ removeRegion: function(name){
1026
+ var region = this._regions[name];
1027
+ this._remove(name, region);
1028
+ },
1029
+
1030
+ // Close all regions in the region manager, and
1031
+ // remove them
1032
+ removeRegions: function(){
1033
+ _.each(this._regions, function(region, name){
1034
+ this._remove(name, region);
1035
+ }, this);
1036
+ },
1037
+
1038
+ // Close all regions in the region manager, but
1039
+ // leave them attached
1040
+ closeRegions: function(){
1041
+ _.each(this._regions, function(region, name){
1042
+ region.close();
1043
+ }, this);
1044
+ },
1045
+
1046
+ // Close all regions and shut down the region
1047
+ // manager entirely
1048
+ close: function(){
1049
+ this.removeRegions();
1050
+ var args = Array.prototype.slice.call(arguments);
1051
+ Marionette.Controller.prototype.close.apply(this, args);
1052
+ },
1053
+
1054
+ // internal method to store regions
1055
+ _store: function(name, region){
1056
+ this._regions[name] = region;
1057
+ this.length = _.size(this._regions);
1058
+ },
1059
+
1060
+ // internal method to remove a region
1061
+ _remove: function(name, region){
1062
+ region.close();
1063
+ delete this._regions[name];
1064
+ this.triggerMethod("region:remove", name, region);
1065
+ }
1066
+
1067
+ });
1068
+
1069
+ // Borrowing this code from Backbone.Collection:
1070
+ // http://backbonejs.org/docs/backbone.html#section-106
1071
+ //
1072
+ // Mix in methods from Underscore, for iteration, and other
1073
+ // collection related features.
1074
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
1075
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
1076
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
1077
+ 'last', 'without', 'isEmpty', 'pluck'];
1078
+
1079
+ _.each(methods, function(method) {
1080
+ RegionManager.prototype[method] = function() {
1081
+ var regions = _.values(this._regions);
1082
+ var args = [regions].concat(_.toArray(arguments));
1083
+ return _[method].apply(_, args);
1084
+ };
1085
+ });
1086
+
1087
+ return RegionManager;
1088
+ })(Marionette);
1089
+
827
1090
 
828
1091
  // Template Cache
829
1092
  // --------------
@@ -844,7 +1107,6 @@ _.extend(Marionette.TemplateCache, {
844
1107
  // retrieves the cached version, or loads it
845
1108
  // from the DOM.
846
1109
  get: function(templateId){
847
- var that = this;
848
1110
  var cachedTemplate = this.templateCaches[templateId];
849
1111
 
850
1112
  if (!cachedTemplate){
@@ -864,7 +1126,7 @@ _.extend(Marionette.TemplateCache, {
864
1126
  // `clear("#t1", "#t2", "...")`
865
1127
  clear: function(){
866
1128
  var i;
867
- var args = Array.prototype.slice.apply(arguments);
1129
+ var args = slice(arguments);
868
1130
  var length = args.length;
869
1131
 
870
1132
  if (length > 0){
@@ -884,8 +1146,6 @@ _.extend(Marionette.TemplateCache.prototype, {
884
1146
 
885
1147
  // Internal method to load the template
886
1148
  load: function(){
887
- var that = this;
888
-
889
1149
  // Guard clause to prevent loading this template more than once
890
1150
  if (this.compiledTemplate){
891
1151
  return this.compiledTemplate;
@@ -904,13 +1164,10 @@ _.extend(Marionette.TemplateCache.prototype, {
904
1164
  // using a template-loader plugin as described here:
905
1165
  // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
906
1166
  loadTemplate: function(templateId){
907
- var template = $(templateId).html();
1167
+ var template = Marionette.$(templateId).html();
908
1168
 
909
1169
  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;
1170
+ throwError("Could not find template: '" + templateId + "'", "NoTemplateError");
914
1171
  }
915
1172
 
916
1173
  return template;
@@ -939,8 +1196,7 @@ Marionette.Renderer = {
939
1196
  // custom rendering and template handling for all of Marionette.
940
1197
  render: function(template, data){
941
1198
  var templateFunc = typeof template === 'function' ? template : Marionette.TemplateCache.get(template);
942
- var html = templateFunc(data);
943
- return html;
1199
+ return templateFunc(data);
944
1200
  }
945
1201
  };
946
1202
 
@@ -993,7 +1249,6 @@ Marionette.View = Backbone.View.extend({
993
1249
  configureTriggers: function(){
994
1250
  if (!this.triggers) { return; }
995
1251
 
996
- var that = this;
997
1252
  var triggerEvents = {};
998
1253
 
999
1254
  // Allow `triggers` to be configured as a function
@@ -1010,7 +1265,7 @@ Marionette.View = Backbone.View.extend({
1010
1265
  if (e && e.preventDefault){ e.preventDefault(); }
1011
1266
  if (e && e.stopPropagation){ e.stopPropagation(); }
1012
1267
 
1013
- // buil the args for the event
1268
+ // build the args for the event
1014
1269
  var args = {
1015
1270
  view: this,
1016
1271
  model: this.model,
@@ -1018,10 +1273,10 @@ Marionette.View = Backbone.View.extend({
1018
1273
  };
1019
1274
 
1020
1275
  // trigger the event
1021
- that.triggerMethod(value, args);
1276
+ this.triggerMethod(value, args);
1022
1277
  };
1023
1278
 
1024
- });
1279
+ }, this);
1025
1280
 
1026
1281
  return triggerEvents;
1027
1282
  },
@@ -1073,6 +1328,9 @@ Marionette.View = Backbone.View.extend({
1073
1328
  return;
1074
1329
  }
1075
1330
 
1331
+ // unbind UI elements
1332
+ this.unbindUIElements();
1333
+
1076
1334
  // mark as closed before doing the actual close, to
1077
1335
  // prevent infinite loops within "close" event handlers
1078
1336
  // that are trying to close other views
@@ -1087,20 +1345,37 @@ Marionette.View = Backbone.View.extend({
1087
1345
  bindUIElements: function(){
1088
1346
  if (!this.ui) { return; }
1089
1347
 
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");
1348
+ // store the ui hash in _uiBindings so they can be reset later
1349
+ // and so re-rendering the view will be able to find the bindings
1350
+ if (!this._uiBindings){
1351
+ this._uiBindings = this.ui;
1096
1352
  }
1097
1353
 
1098
- // refreshing the associated selectors since they should point to the newly rendered elements.
1354
+ // get the bindings result, as a function or otherwise
1355
+ var bindings = _.result(this, "_uiBindings");
1356
+
1357
+ // empty the ui so we don't have anything to start with
1099
1358
  this.ui = {};
1100
- _.each(_.keys(this.uiBindings), function(key) {
1101
- var selector = that.uiBindings[key];
1102
- that.ui[key] = that.$(selector);
1103
- });
1359
+
1360
+ // bind each of the selectors
1361
+ _.each(_.keys(bindings), function(key) {
1362
+ var selector = bindings[key];
1363
+ this.ui[key] = this.$(selector);
1364
+ }, this);
1365
+ },
1366
+
1367
+ // This method unbinds the elements specified in the "ui" hash
1368
+ unbindUIElements: function(){
1369
+ if (!this.ui){ return; }
1370
+
1371
+ // delete all of the existing ui bindings
1372
+ _.each(this.ui, function($el, name){
1373
+ delete this.ui[name];
1374
+ }, this);
1375
+
1376
+ // reset the ui element to the original bindings configuration
1377
+ this.ui = this._uiBindings;
1378
+ delete this._uiBindings;
1104
1379
  }
1105
1380
  });
1106
1381
 
@@ -1112,8 +1387,7 @@ Marionette.View = Backbone.View.extend({
1112
1387
  // and calling several methods on extended views, such as `onRender`.
1113
1388
  Marionette.ItemView = Marionette.View.extend({
1114
1389
  constructor: function(){
1115
- var args = Array.prototype.slice.apply(arguments);
1116
- Marionette.View.prototype.constructor.apply(this, args);
1390
+ Marionette.View.prototype.constructor.apply(this, slice(arguments));
1117
1391
  },
1118
1392
 
1119
1393
  // Serialize the model or collection for the view. If a model is
@@ -1167,8 +1441,7 @@ Marionette.ItemView = Marionette.View.extend({
1167
1441
 
1168
1442
  this.triggerMethod('item:before:close');
1169
1443
 
1170
- var args = Array.prototype.slice.apply(arguments);
1171
- Marionette.View.prototype.close.apply(this, args);
1444
+ Marionette.View.prototype.close.apply(this, slice(arguments));
1172
1445
 
1173
1446
  this.triggerMethod('item:closed');
1174
1447
  }
@@ -1188,8 +1461,7 @@ Marionette.CollectionView = Marionette.View.extend({
1188
1461
  constructor: function(options){
1189
1462
  this._initChildViewStorage();
1190
1463
 
1191
- var args = Array.prototype.slice.apply(arguments);
1192
- Marionette.View.prototype.constructor.apply(this, args);
1464
+ Marionette.View.prototype.constructor.apply(this, slice(arguments));
1193
1465
 
1194
1466
  this._initialEvents();
1195
1467
  },
@@ -1263,12 +1535,11 @@ Marionette.CollectionView = Marionette.View.extend({
1263
1535
  // Internal method to loop through each item in the
1264
1536
  // collection view and show it
1265
1537
  showCollection: function(){
1266
- var that = this;
1267
1538
  var ItemView;
1268
1539
  this.collection.each(function(item, index){
1269
- ItemView = that.getItemView(item);
1270
- that.addItemView(item, ItemView, index);
1271
- });
1540
+ ItemView = this.getItemView(item);
1541
+ this.addItemView(item, ItemView, index);
1542
+ }, this);
1272
1543
  },
1273
1544
 
1274
1545
  // Internal method to show an empty view in place of
@@ -1301,9 +1572,7 @@ Marionette.CollectionView = Marionette.View.extend({
1301
1572
  var itemView = Marionette.getOption(this, "itemView");
1302
1573
 
1303
1574
  if (!itemView){
1304
- var err = new Error("An `itemView` must be specified");
1305
- err.name = "NoItemViewError";
1306
- throw err;
1575
+ throwError("An `itemView` must be specified", "NoItemViewError");
1307
1576
  }
1308
1577
 
1309
1578
  return itemView;
@@ -1312,12 +1581,10 @@ Marionette.CollectionView = Marionette.View.extend({
1312
1581
  // Render the child item's view and add it to the
1313
1582
  // HTML for the collection view.
1314
1583
  addItemView: function(item, ItemView, index){
1315
- var that = this;
1316
-
1317
1584
  // get the itemViewOptions if any were specified
1318
1585
  var itemViewOptions = Marionette.getOption(this, "itemViewOptions");
1319
1586
  if (_.isFunction(itemViewOptions)){
1320
- itemViewOptions = itemViewOptions.call(this, item);
1587
+ itemViewOptions = itemViewOptions.call(this, item, index);
1321
1588
  }
1322
1589
 
1323
1590
  // build the view
@@ -1354,7 +1621,7 @@ Marionette.CollectionView = Marionette.View.extend({
1354
1621
  // Forward all child item view events through the parent,
1355
1622
  // prepending "itemview:" to the event name
1356
1623
  this.listenTo(view, "all", function(){
1357
- var args = slice.call(arguments);
1624
+ var args = slice(arguments);
1358
1625
  args[0] = prefix + ":" + args[0];
1359
1626
  args.splice(1, 0, view);
1360
1627
 
@@ -1371,8 +1638,7 @@ Marionette.CollectionView = Marionette.View.extend({
1371
1638
  // Build an `itemView` for every model in the collection.
1372
1639
  buildItemView: function(item, ItemViewType, itemViewOptions){
1373
1640
  var options = _.extend({model: item}, itemViewOptions);
1374
- var view = new ItemViewType(options);
1375
- return view;
1641
+ return new ItemViewType(options);
1376
1642
  },
1377
1643
 
1378
1644
  // get the child view by item it holds, and remove it
@@ -1390,9 +1656,9 @@ Marionette.CollectionView = Marionette.View.extend({
1390
1656
  if (view){
1391
1657
  this.stopListening(view);
1392
1658
 
1393
- if (view.close){
1394
- view.close();
1395
- }
1659
+ // call 'close' or 'remove', depending on which is found
1660
+ if (view.close) { view.close(); }
1661
+ else if (view.remove) { view.remove(); }
1396
1662
 
1397
1663
  this.children.remove(view);
1398
1664
  }
@@ -1431,8 +1697,7 @@ Marionette.CollectionView = Marionette.View.extend({
1431
1697
  this.closeChildren();
1432
1698
  this.triggerMethod("collection:closed");
1433
1699
 
1434
- var args = Array.prototype.slice.apply(arguments);
1435
- Marionette.View.prototype.close.apply(this, args);
1700
+ Marionette.View.prototype.close.apply(this, slice(arguments));
1436
1701
  },
1437
1702
 
1438
1703
  // Close the child views that this collection view
@@ -1454,8 +1719,7 @@ Marionette.CollectionView = Marionette.View.extend({
1454
1719
  // an item view as `modelView`, for the top leaf
1455
1720
  Marionette.CompositeView = Marionette.CollectionView.extend({
1456
1721
  constructor: function(options){
1457
- var args = Array.prototype.slice.apply(arguments);
1458
- Marionette.CollectionView.apply(this, args);
1722
+ Marionette.CollectionView.apply(this, slice(arguments));
1459
1723
 
1460
1724
  this.itemView = this.getItemView();
1461
1725
  },
@@ -1479,9 +1743,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1479
1743
  var itemView = Marionette.getOption(this, "itemView") || this.constructor;
1480
1744
 
1481
1745
  if (!itemView){
1482
- var err = new Error("An `itemView` must be specified");
1483
- err.name = "NoItemViewError";
1484
- throw err;
1746
+ throwError("An `itemView` must be specified", "NoItemViewError");
1485
1747
  }
1486
1748
 
1487
1749
  return itemView;
@@ -1504,6 +1766,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1504
1766
  // this again will tell the model's view to re-render itself
1505
1767
  // but the collection will not re-render.
1506
1768
  render: function(){
1769
+ this.isRendered = true;
1507
1770
  this.isClosed = false;
1508
1771
  this.resetItemViewContainer();
1509
1772
 
@@ -1524,15 +1787,10 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1524
1787
  },
1525
1788
 
1526
1789
  _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
-
1790
+ if (this.isRendered){
1791
+ Marionette.CollectionView.prototype._renderChildren.call(this);
1792
+ this.triggerMethod("composite:collection:rendered");
1793
+ }
1536
1794
  },
1537
1795
 
1538
1796
  // Render an individual model, if we have one, as
@@ -1569,9 +1827,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1569
1827
  var selector = _.result(containerView, "itemViewContainer");
1570
1828
  container = containerView.$(selector);
1571
1829
  if (container.length <= 0) {
1572
- var err = new Error("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer);
1573
- err.name = "ItemViewContainerMissingError";
1574
- throw err;
1830
+ throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError");
1575
1831
  }
1576
1832
 
1577
1833
  } else {
@@ -1603,14 +1859,15 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1603
1859
  Marionette.Layout = Marionette.ItemView.extend({
1604
1860
  regionType: Marionette.Region,
1605
1861
 
1606
- // Ensure the regions are avialable when the `initialize` method
1862
+ // Ensure the regions are available when the `initialize` method
1607
1863
  // is called.
1608
- constructor: function () {
1609
- this._firstRender = true;
1610
- this.initializeRegions();
1864
+ constructor: function (options) {
1865
+ options = options || {};
1611
1866
 
1612
- var args = Array.prototype.slice.apply(arguments);
1613
- Marionette.ItemView.apply(this, args);
1867
+ this._firstRender = true;
1868
+ this._initializeRegions(options);
1869
+
1870
+ Marionette.ItemView.call(this, options);
1614
1871
  },
1615
1872
 
1616
1873
  // Layout's render will use the existing region objects the
@@ -1623,11 +1880,14 @@ Marionette.Layout = Marionette.ItemView.extend({
1623
1880
  // if this is the first render, don't do anything to
1624
1881
  // reset the regions
1625
1882
  this._firstRender = false;
1883
+ } else if (this.isClosed){
1884
+ // a previously closed layout means we need to
1885
+ // completely re-initialize the regions
1886
+ this._initializeRegions();
1626
1887
  } else {
1627
1888
  // If this is not the first render call, then we need to
1628
1889
  // re-initializing the `el` for each region
1629
- this.closeRegions();
1630
- this.reInitializeRegions();
1890
+ this._reInitializeRegions();
1631
1891
  }
1632
1892
 
1633
1893
  var args = Array.prototype.slice.apply(arguments);
@@ -1639,75 +1899,82 @@ Marionette.Layout = Marionette.ItemView.extend({
1639
1899
  // Handle closing regions, and then close the view itself.
1640
1900
  close: function () {
1641
1901
  if (this.isClosed){ return; }
1642
-
1643
- this.closeRegions();
1644
- this.destroyRegions();
1645
-
1902
+ this.regionManager.close();
1646
1903
  var args = Array.prototype.slice.apply(arguments);
1647
1904
  Marionette.ItemView.prototype.close.apply(this, args);
1648
1905
  },
1649
1906
 
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
- }
1907
+ // Add a single region, by name, to the layout
1908
+ addRegion: function(name, definition){
1909
+ var regions = {};
1910
+ regions[name] = definition;
1911
+ return this.addRegions(regions)[name];
1912
+ },
1660
1913
 
1661
- var that = this;
1662
- var regions = this.regions || {};
1663
- _.each(regions, function (region, name) {
1914
+ // Add multiple regions as a {name: definition, name2: def2} object literal
1915
+ addRegions: function(regions){
1916
+ this.regions = _.extend(this.regions || {}, regions);
1917
+ return this._buildRegions(regions);
1918
+ },
1664
1919
 
1665
- var regionManager = Marionette.Region.buildRegion(region, that.regionType);
1666
- regionManager.getEl = function(selector){
1667
- return that.$(selector);
1668
- };
1920
+ // Remove a single region from the Layout, by name
1921
+ removeRegion: function(name){
1922
+ return this.regionManager.removeRegion(name);
1923
+ },
1669
1924
 
1670
- that.regionManagers[name] = regionManager;
1671
- that[name] = regionManager;
1672
- });
1925
+ // internal method to build regions
1926
+ _buildRegions: function(regions){
1927
+ var that = this;
1673
1928
 
1929
+ var defaults = {
1930
+ parentEl: function(){ return that.$el; }
1931
+ };
1932
+
1933
+ return this.regionManager.addRegions(regions, defaults);
1674
1934
  },
1675
1935
 
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();
1936
+ // Internal method to initialize the regions that have been defined in a
1937
+ // `regions` attribute on this layout.
1938
+ _initializeRegions: function (options) {
1939
+ var regions;
1940
+ this._initRegionManager();
1941
+
1942
+ if (_.isFunction(this.regions)) {
1943
+ regions = this.regions(options);
1681
1944
  } else {
1682
- _.each(this.regionManagers, function(region){
1683
- region.reset();
1684
- });
1945
+ regions = this.regions || {};
1685
1946
  }
1947
+
1948
+ this.addRegions(regions);
1686
1949
  },
1687
1950
 
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();
1951
+ // Internal method to re-initialize all of the regions by updating the `el` that
1952
+ // they point to
1953
+ _reInitializeRegions: function(){
1954
+ this.regionManager.closeRegions();
1955
+ this.regionManager.each(function(region){
1956
+ region.reset();
1695
1957
  });
1696
1958
  },
1697
1959
 
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];
1960
+ // Internal method to initialize the region manager
1961
+ // and all regions in it
1962
+ _initRegionManager: function(){
1963
+ this.regionManager = new Marionette.RegionManager();
1964
+
1965
+ this.listenTo(this.regionManager, "region:add", function(name, region){
1966
+ this[name] = region;
1967
+ this.trigger("region:add", name, region);
1968
+ });
1969
+
1970
+ this.listenTo(this.regionManager, "region:remove", function(name, region){
1971
+ delete this[name];
1972
+ this.trigger("region:remove", name, region);
1704
1973
  });
1705
- this.regionManagers = {};
1706
1974
  }
1707
1975
  });
1708
1976
 
1709
1977
 
1710
-
1711
1978
  // AppRouter
1712
1979
  // ---------
1713
1980
 
@@ -1720,7 +1987,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1720
1987
  //
1721
1988
  // App routers can only take one `controller` object.
1722
1989
  // It is recommended that you divide your controller
1723
- // objects in to smaller peices of related functionality
1990
+ // objects in to smaller pieces of related functionality
1724
1991
  // and have multiple routers / controllers, instead of
1725
1992
  // just one giant router and controller.
1726
1993
  //
@@ -1729,8 +1996,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1729
1996
  Marionette.AppRouter = Backbone.Router.extend({
1730
1997
 
1731
1998
  constructor: function(options){
1732
- var args = Array.prototype.slice.apply(arguments);
1733
- Backbone.Router.prototype.constructor.apply(this, args);
1999
+ Backbone.Router.prototype.constructor.apply(this, slice(arguments));
1734
2000
 
1735
2001
  this.options = options;
1736
2002
 
@@ -1744,33 +2010,15 @@ Marionette.AppRouter = Backbone.Router.extend({
1744
2010
  // router, and turn them in to routes that trigger the
1745
2011
  // specified method on the specified `controller`.
1746
2012
  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
- }
2013
+ _.each(appRoutes, function(methodName, route) {
2014
+ var method = controller[methodName];
1757
2015
 
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;
2016
+ if (!method) {
2017
+ throw new Error("Method '" + methodName + "' was not found on the controller");
1769
2018
  }
1770
2019
 
1771
- method = _.bind(method, controller);
1772
- router.route(route, methodName, method);
1773
- }
2020
+ this.route(route, methodName, _.bind(method, controller));
2021
+ }, this);
1774
2022
  }
1775
2023
  });
1776
2024
 
@@ -1782,7 +2030,8 @@ Marionette.AppRouter = Backbone.Router.extend({
1782
2030
  // Stores and starts up `Region` objects, includes an
1783
2031
  // event aggregator as `app.vent`
1784
2032
  Marionette.Application = function(options){
1785
- this.initCallbacks = new Marionette.Callbacks();
2033
+ this._initRegionManager();
2034
+ this._initCallbacks = new Marionette.Callbacks();
1786
2035
  this.vent = new Backbone.Wreqr.EventAggregator();
1787
2036
  this.commands = new Backbone.Wreqr.Commands();
1788
2037
  this.reqres = new Backbone.Wreqr.RequestResponse();
@@ -1810,7 +2059,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1810
2059
  // method is called, or run immediately if added after `start`
1811
2060
  // has already been called.
1812
2061
  addInitializer: function(initializer){
1813
- this.initCallbacks.add(initializer);
2062
+ this._initCallbacks.add(initializer);
1814
2063
  },
1815
2064
 
1816
2065
  // kick off all of the application's processes.
@@ -1818,7 +2067,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1818
2067
  // to the app, and runs all of the initializer functions
1819
2068
  start: function(options){
1820
2069
  this.triggerMethod("initialize:before", options);
1821
- this.initCallbacks.run(options, this);
2070
+ this._initCallbacks.run(options, this);
1822
2071
  this.triggerMethod("initialize:after", options);
1823
2072
 
1824
2073
  this.triggerMethod("start", options);
@@ -1827,32 +2076,40 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
1827
2076
  // Add regions to your app.
1828
2077
  // Accepts a hash of named strings or Region objects
1829
2078
  // addRegions({something: "#someRegion"})
1830
- // addRegions{{something: Region.extend({el: "#someRegion"}) });
2079
+ // addRegions({something: Region.extend({el: "#someRegion"}) });
1831
2080
  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
- });
2081
+ return this._regionManager.addRegions(regions);
1837
2082
  },
1838
2083
 
1839
2084
  // Removes a region from your app.
1840
2085
  // Accepts the regions name
1841
2086
  // removeRegion('myRegion')
1842
2087
  removeRegion: function(region) {
1843
- this[region].close();
1844
- delete this[region];
2088
+ this._regionManager.removeRegion(region);
1845
2089
  },
1846
2090
 
1847
2091
  // Create a module, attached to the application
1848
2092
  module: function(moduleNames, moduleDefinition){
1849
2093
  // slice the args, and add this application object as the
1850
2094
  // first argument of the array
1851
- var args = slice.call(arguments);
2095
+ var args = slice(arguments);
1852
2096
  args.unshift(this);
1853
2097
 
1854
2098
  // see the Marionette.Module object for more information
1855
2099
  return Marionette.Module.create.apply(Marionette.Module, args);
2100
+ },
2101
+
2102
+ // Internal method to set up the region manager
2103
+ _initRegionManager: function(){
2104
+ this._regionManager = new Marionette.RegionManager();
2105
+
2106
+ this.listenTo(this._regionManager, "region:add", function(name, region){
2107
+ this[name] = region;
2108
+ });
2109
+
2110
+ this.listenTo(this._regionManager, "region:remove", function(name, region){
2111
+ delete this[name];
2112
+ });
1856
2113
  }
1857
2114
  });
1858
2115
 
@@ -1896,7 +2153,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1896
2153
  this._finalizerCallbacks.add(callback);
1897
2154
  },
1898
2155
 
1899
- // Start the module, and run all of it's initializers
2156
+ // Start the module, and run all of its initializers
1900
2157
  start: function(options){
1901
2158
  // Prevent re-starting a module that is already started
1902
2159
  if (this._isInitialized){ return; }
@@ -1904,11 +2161,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1904
2161
  // start the sub-modules (depth-first hierarchy)
1905
2162
  _.each(this.submodules, function(mod){
1906
2163
  // 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){
2164
+ if (mod.startWithParent){
1912
2165
  mod.start(options);
1913
2166
  }
1914
2167
  });
@@ -1962,7 +2215,7 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
1962
2215
  this.app,
1963
2216
  Backbone,
1964
2217
  Marionette,
1965
- $, _,
2218
+ Marionette.$, _,
1966
2219
  customArgs
1967
2220
  ]);
1968
2221
 
@@ -1983,12 +2236,11 @@ _.extend(Marionette.Module, {
1983
2236
 
1984
2237
  // Create a module, hanging off the app parameter as the parent object.
1985
2238
  create: function(app, moduleNames, moduleDefinition){
1986
- var that = this;
1987
2239
  var module = app;
1988
2240
 
1989
2241
  // get the custom args passed in after the module definition and
1990
2242
  // get rid of the module name and definition function
1991
- var customArgs = slice.apply(arguments);
2243
+ var customArgs = slice(arguments);
1992
2244
  customArgs.splice(0, 3);
1993
2245
 
1994
2246
  // split the module names and get the length
@@ -2002,9 +2254,9 @@ _.extend(Marionette.Module, {
2002
2254
  // Loop through all the parts of the module definition
2003
2255
  _.each(moduleNames, function(moduleName, i){
2004
2256
  var parentModule = module;
2005
- module = that._getModule(parentModule, moduleName, app);
2006
- that._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
2007
- });
2257
+ module = this._getModule(parentModule, moduleName, app);
2258
+ this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
2259
+ }, this);
2008
2260
 
2009
2261
  // Return the last module in the definition chain
2010
2262
  return module;
@@ -2051,7 +2303,6 @@ _.extend(Marionette.Module, {
2051
2303
 
2052
2304
  // `and` the two together, ensuring a single `false` will prevent it
2053
2305
  // from starting with the parent
2054
- var tmp = module.startWithParent;
2055
2306
  module.startWithParent = module.startWithParent && startWithParent;
2056
2307
 
2057
2308
  // setup auto-start if needed
@@ -2075,4 +2326,4 @@ _.extend(Marionette.Module, {
2075
2326
 
2076
2327
 
2077
2328
  return Marionette;
2078
- })(Backbone, _, $ || window.jQuery || window.Zepto || window.ender);
2329
+ })(this, Backbone, _);