backbone-marionette-rails 1.0.0.6 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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, _);