yano-backbone-rails 2.2.1 → 2.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 676c52eac1bba3bc8489438d50db26145df21209
4
- data.tar.gz: 30af094566337d1d44165e42814826689c4aee95
3
+ metadata.gz: 21b6e33f2a3faa15fb31fd4854e4310eee9572ee
4
+ data.tar.gz: 78c2afe4e9a9ed45d268de71979b347239dcc44e
5
5
  SHA512:
6
- metadata.gz: aaeeef9c73aa07c6b5708528cf782344511de7869b891a62f4a8c478d75a33e9f23ee85b6877b2d2b3a59eb0a1d406b28ac553229ba316f62d73850c7533d628
7
- data.tar.gz: 41766aa65260e2636e29c0ac8cbf4ea4f48369540ff9b43984bdd907086e3ae90949179dc86a9af4c4227233ec40c701870b26b39dfa57bdade6178e573ecc5a
6
+ metadata.gz: a3884b74a2f08993340de28d252d7e7f1a1aa5ccdfeb0f7a3ef3c68910b8196df472befd6eaf166327877af07c09045b06c78efd6457292dd788e752492b0a80
7
+ data.tar.gz: 1a4e8868c4e53aebef3be87f5ed1fa85d99d2c113355c066dad52518b7015d2b83c9834e3a2ab21b6795568fbebf6ccca459d8a760e849bd8585cdd326140939
@@ -1,10 +1,10 @@
1
1
  module Yano
2
2
  module Backbone
3
3
  module Rails
4
- VERSION = "2.2.1"
4
+ VERSION = "2.2.2"
5
5
  UNDERSCORE_VERSION = "1.8.3"
6
6
  BACKBONE_VERSION = "1.3.3"
7
- MARIONETTE_VERSION = "3.0.0"
7
+ MARIONETTE_VERSION = "3.1.0"
8
8
  RADIO_VERSION = "2.0.0"
9
9
  end
10
10
  end
@@ -1,6 +1,6 @@
1
1
  // MarionetteJS (Backbone.Marionette)
2
2
  // ----------------------------------
3
- // v3.0.0
3
+ // v3.1.0
4
4
  //
5
5
  // Copyright (c)2016 Derick Bailey, Muted Solutions, LLC.
6
6
  // Distributed under MIT license
@@ -18,7 +18,7 @@
18
18
  _ = 'default' in _ ? _['default'] : _;
19
19
  Radio = 'default' in Radio ? Radio['default'] : Radio;
20
20
 
21
- var version = "3.0.0";
21
+ var version = "3.1.0";
22
22
 
23
23
  //Internal utility for creating context style global utils
24
24
  var proxy = function proxy(method) {
@@ -63,10 +63,18 @@
63
63
 
64
64
  // Merge `keys` from `options` onto `this`
65
65
  var mergeOptions = function mergeOptions(options, keys) {
66
+ var _this = this;
67
+
66
68
  if (!options) {
67
69
  return;
68
70
  }
69
- _.extend(this, _.pick(options, keys));
71
+
72
+ _.each(keys, function (key) {
73
+ var option = options[key];
74
+ if (option !== undefined) {
75
+ _this[key] = option;
76
+ }
77
+ });
70
78
  };
71
79
 
72
80
  // Marionette.getOption
@@ -113,6 +121,10 @@
113
121
  return eventName.toUpperCase();
114
122
  }
115
123
 
124
+ var getOnMethodName = _.memoize(function (event) {
125
+ return 'on' + event.replace(splitter, getEventName);
126
+ });
127
+
116
128
  // Trigger an event and/or a corresponding method name. Examples:
117
129
  //
118
130
  // `this.triggerMethod("foo")` will trigger the "foo" event and
@@ -121,24 +133,23 @@
121
133
  // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
122
134
  // call the "onFooBar" method.
123
135
  function triggerMethod(event) {
136
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
137
+ args[_key - 1] = arguments[_key];
138
+ }
139
+
124
140
  // get the method name from the event name
125
- var methodName = 'on' + event.replace(splitter, getEventName);
141
+ var methodName = getOnMethodName(event);
126
142
  var method = getOption.call(this, methodName);
127
143
  var result = void 0;
128
144
 
129
145
  // call the onMethodName if it exists
130
-
131
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
132
- args[_key - 1] = arguments[_key];
133
- }
134
-
135
146
  if (_.isFunction(method)) {
136
147
  // pass all args, except the event name
137
148
  result = method.apply(this, args);
138
149
  }
139
150
 
140
151
  // trigger the event
141
- this.trigger.apply(this, [event].concat(args));
152
+ this.trigger.apply(this, arguments);
142
153
 
143
154
  return result;
144
155
  }
@@ -148,13 +159,15 @@
148
159
  // e.g. `Marionette.triggerMethodOn(view, 'show')`
149
160
  // will trigger a "show" event or invoke onShow the view.
150
161
  function triggerMethodOn(context) {
151
- var fnc = _.isFunction(context.triggerMethod) ? context.triggerMethod : triggerMethod;
152
-
153
162
  for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
154
163
  args[_key2 - 1] = arguments[_key2];
155
164
  }
156
165
 
157
- return fnc.apply(context, args);
166
+ if (_.isFunction(context.triggerMethod)) {
167
+ return context.triggerMethod.apply(context, args);
168
+ }
169
+
170
+ return triggerMethod.apply(context, args);
158
171
  }
159
172
 
160
173
  // Trigger method on children unless a pure Backbone.View
@@ -194,41 +207,41 @@
194
207
  return true;
195
208
  }
196
209
 
197
- // Monitor a view's state, propagating attach/detach events to children and firing dom:refresh
198
- // whenever a rendered view is attached or an attached view is rendered.
199
- function monitorViewEvents(view) {
200
- if (view._areViewEventsMonitored) {
201
- return;
210
+ function triggerDOMRefresh(view) {
211
+ if (view._isAttached && view._isRendered) {
212
+ triggerMethodOn(view, 'dom:refresh', view);
202
213
  }
214
+ }
203
215
 
204
- view._areViewEventsMonitored = true;
216
+ function handleBeforeAttach() {
217
+ triggerMethodChildren(this, 'before:attach', shouldTriggerAttach);
218
+ }
205
219
 
206
- function handleBeforeAttach() {
207
- triggerMethodChildren(view, 'before:attach', shouldTriggerAttach);
208
- }
220
+ function handleAttach() {
221
+ triggerMethodChildren(this, 'attach', shouldAttach);
222
+ triggerDOMRefresh(this);
223
+ }
209
224
 
210
- function handleAttach() {
211
- triggerMethodChildren(view, 'attach', shouldAttach);
212
- triggerDOMRefresh();
213
- }
225
+ function handleBeforeDetach() {
226
+ triggerMethodChildren(this, 'before:detach', shouldTriggerDetach);
227
+ }
214
228
 
215
- function handleBeforeDetach() {
216
- triggerMethodChildren(view, 'before:detach', shouldTriggerDetach);
217
- }
229
+ function handleDetach() {
230
+ triggerMethodChildren(this, 'detach', shouldDetach);
231
+ }
218
232
 
219
- function handleDetach() {
220
- triggerMethodChildren(view, 'detach', shouldDetach);
221
- }
233
+ function handleRender() {
234
+ triggerDOMRefresh(this);
235
+ }
222
236
 
223
- function handleRender() {
224
- triggerDOMRefresh();
237
+ // Monitor a view's state, propagating attach/detach events to children and firing dom:refresh
238
+ // whenever a rendered view is attached or an attached view is rendered.
239
+ function monitorViewEvents(view) {
240
+ if (view._areViewEventsMonitored) {
241
+ return;
225
242
  }
226
243
 
227
- function triggerDOMRefresh() {
228
- if (view._isAttached && view._isRendered) {
229
- triggerMethodOn(view, 'dom:refresh', view);
230
- }
231
- }
244
+ view._areViewEventsMonitored = true;
232
245
 
233
246
  view.on({
234
247
  'before:attach': handleBeforeAttach,
@@ -591,15 +604,7 @@
591
604
 
592
605
  var _invoke = _.invokeMap || _.invoke;
593
606
 
594
- var toConsumableArray = function (arr) {
595
- if (Array.isArray(arr)) {
596
- for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
597
-
598
- return arr2;
599
- } else {
600
- return Array.from(arr);
601
- }
602
- };
607
+ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
603
608
 
604
609
  // MixinOptions
605
610
  // - behaviors
@@ -651,11 +656,11 @@
651
656
  },
652
657
  _getBehaviorTriggers: function _getBehaviorTriggers() {
653
658
  var triggers = _invoke(this._behaviors, 'getTriggers');
654
- return _.extend.apply(_, [{}].concat(toConsumableArray(triggers)));
659
+ return _.extend.apply(_, [{}].concat(_toConsumableArray(triggers)));
655
660
  },
656
661
  _getBehaviorEvents: function _getBehaviorEvents() {
657
662
  var events = _invoke(this._behaviors, 'getEvents');
658
- return _.extend.apply(_, [{}].concat(toConsumableArray(events)));
663
+ return _.extend.apply(_, [{}].concat(_toConsumableArray(events)));
659
664
  },
660
665
 
661
666
 
@@ -680,7 +685,7 @@
680
685
  // destroying the view.
681
686
  // This unbinds event listeners
682
687
  // that behaviors have registered for.
683
- _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(toConsumableArray(args)));
688
+ _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(_toConsumableArray(args)));
684
689
  },
685
690
  _bindBehaviorUIElements: function _bindBehaviorUIElements() {
686
691
  _invoke(this._behaviors, 'bindUIElements');
@@ -691,13 +696,8 @@
691
696
  _triggerEventOnBehaviors: function _triggerEventOnBehaviors() {
692
697
  var behaviors = this._behaviors;
693
698
  // Use good ol' for as this is a very hot function
694
-
695
- for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
696
- args[_key] = arguments[_key];
697
- }
698
-
699
699
  for (var i = 0, length = behaviors && behaviors.length; i < length; i++) {
700
- triggerMethod.apply(behaviors[i], args);
700
+ triggerMethod.apply(behaviors[i], arguments);
701
701
  }
702
702
  }
703
703
  };
@@ -786,7 +786,7 @@
786
786
  // Returns a new, non-mutated, parsed events hash.
787
787
  var _normalizeUIKeys = function _normalizeUIKeys(hash, ui) {
788
788
  return _.reduce(hash, function (memo, val, key) {
789
- var normalizedKey = normalizeUIString(key, ui);
789
+ var normalizedKey = _normalizeUIString(key, ui);
790
790
  memo[normalizedKey] = val;
791
791
  return memo;
792
792
  }, {});
@@ -794,7 +794,7 @@
794
794
 
795
795
  // utility method for parsing @ui. syntax strings
796
796
  // into associated selector
797
- var normalizeUIString = function normalizeUIString(uiString, ui) {
797
+ var _normalizeUIString = function _normalizeUIString(uiString, ui) {
798
798
  return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function (r) {
799
799
  return ui[r.slice(4)];
800
800
  });
@@ -806,14 +806,14 @@
806
806
  var _normalizeUIValues = function _normalizeUIValues(hash, ui, properties) {
807
807
  _.each(hash, function (val, key) {
808
808
  if (_.isString(val)) {
809
- hash[key] = normalizeUIString(val, ui);
809
+ hash[key] = _normalizeUIString(val, ui);
810
810
  } else if (_.isObject(val) && _.isArray(properties)) {
811
811
  _.extend(val, _normalizeUIValues(_.pick(val, properties), ui));
812
812
  /* Value is an object, and we got an array of embedded property names to normalize. */
813
813
  _.each(properties, function (property) {
814
814
  var propertyVal = val[property];
815
815
  if (_.isString(propertyVal)) {
816
- val[property] = normalizeUIString(propertyVal, ui);
816
+ val[property] = _normalizeUIString(propertyVal, ui);
817
817
  }
818
818
  });
819
819
  }
@@ -831,6 +831,14 @@
831
831
  },
832
832
 
833
833
 
834
+ // normalize the passed string with the views `ui` selectors.
835
+ // `"@ui.bar"`
836
+ normalizeUIString: function normalizeUIString(uiString) {
837
+ var uiBindings = this._getUIBindings();
838
+ return _normalizeUIString(uiString, uiBindings);
839
+ },
840
+
841
+
834
842
  // normalize the values of passed hash with the views `ui` selectors.
835
843
  // `{foo: "@ui.bar"}`
836
844
  normalizeUIValues: function normalizeUIValues(hash, properties) {
@@ -930,23 +938,6 @@
930
938
  },
931
939
 
932
940
 
933
- // Overriding Backbone.View's `setElement` to handle
934
- // if an el was previously defined. If so, the view might be
935
- // rendered or attached on setElement.
936
- setElement: function setElement() {
937
- var hasEl = !!this.el;
938
-
939
- Backbone.View.prototype.setElement.apply(this, arguments);
940
-
941
- if (hasEl) {
942
- this._isRendered = !!this.$el.length;
943
- this._isAttached = isNodeAttached(this.el);
944
- }
945
-
946
- return this;
947
- },
948
-
949
-
950
941
  // Overriding Backbone.View's `delegateEvents` to handle
951
942
  // `events` and `triggers`
952
943
  delegateEvents: function delegateEvents(eventsArg) {
@@ -1095,14 +1086,10 @@
1095
1086
  // import the `triggerMethod` to trigger events with corresponding
1096
1087
  // methods if the method exists
1097
1088
  triggerMethod: function triggerMethod$$() {
1098
- for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
1099
- args[_key2] = arguments[_key2];
1100
- }
1089
+ var ret = triggerMethod.apply(this, arguments);
1101
1090
 
1102
- var ret = triggerMethod.apply(this, args);
1103
-
1104
- this._triggerEventOnBehaviors.apply(this, args);
1105
- this._triggerEventOnParentLayout.apply(this, args);
1091
+ this._triggerEventOnBehaviors.apply(this, arguments);
1092
+ this._triggerEventOnParentLayout.apply(this, arguments);
1106
1093
 
1107
1094
  return ret;
1108
1095
  },
@@ -1113,49 +1100,55 @@
1113
1100
  this._childViewEvents = _.result(this, 'childViewEvents');
1114
1101
  this._childViewTriggers = _.result(this, 'childViewTriggers');
1115
1102
  },
1116
- _triggerEventOnParentLayout: function _triggerEventOnParentLayout(eventName) {
1103
+ _triggerEventOnParentLayout: function _triggerEventOnParentLayout() {
1117
1104
  var layoutView = this._parentView();
1118
1105
  if (!layoutView) {
1119
1106
  return;
1120
1107
  }
1121
1108
 
1122
- // invoke triggerMethod on parent view
1123
- var eventPrefix = _.result(layoutView, 'childViewEventPrefix');
1124
- var prefixedEventName = eventPrefix + ':' + eventName;
1109
+ layoutView._childViewEventHandler.apply(layoutView, arguments);
1110
+ },
1111
+
1112
+
1113
+ // Walk the _parent tree until we find a view (if one exists).
1114
+ // Returns the parent view hierarchically closest to this view.
1115
+ _parentView: function _parentView() {
1116
+ var parent = this._parent;
1125
1117
 
1126
- for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
1127
- args[_key3 - 1] = arguments[_key3];
1118
+ while (parent) {
1119
+ if (parent instanceof View) {
1120
+ return parent;
1121
+ }
1122
+ parent = parent._parent;
1128
1123
  }
1124
+ },
1125
+ _childViewEventHandler: function _childViewEventHandler(eventName) {
1126
+ var childViewEvents = this.normalizeMethods(this._childViewEvents);
1129
1127
 
1130
- layoutView.triggerMethod.apply(layoutView, [prefixedEventName].concat(args));
1128
+ // call collectionView childViewEvent if defined
1131
1129
 
1132
- // use the parent view's childViewEvents handler
1133
- var childViewEvents = layoutView.normalizeMethods(layoutView._childViewEvents);
1130
+ for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
1131
+ args[_key2 - 1] = arguments[_key2];
1132
+ }
1134
1133
 
1135
- if (!!childViewEvents && _.isFunction(childViewEvents[eventName])) {
1136
- childViewEvents[eventName].apply(layoutView, args);
1134
+ if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) {
1135
+ childViewEvents[eventName].apply(this, args);
1137
1136
  }
1138
1137
 
1139
1138
  // use the parent view's proxyEvent handlers
1140
- var childViewTriggers = layoutView._childViewTriggers;
1139
+ var childViewTriggers = this._childViewTriggers;
1141
1140
 
1142
1141
  // Call the event with the proxy name on the parent layout
1143
1142
  if (childViewTriggers && _.isString(childViewTriggers[eventName])) {
1144
- layoutView.triggerMethod.apply(layoutView, [childViewTriggers[eventName]].concat(args));
1143
+ this.triggerMethod.apply(this, [childViewTriggers[eventName]].concat(args));
1145
1144
  }
1146
- },
1147
1145
 
1146
+ var prefix = _.result(this, 'childViewEventPrefix');
1148
1147
 
1149
- // Walk the _parent tree until we find a view (if one exists).
1150
- // Returns the parent view hierarchically closest to this view.
1151
- _parentView: function _parentView() {
1152
- var parent = this._parent;
1148
+ if (prefix !== false) {
1149
+ var childEventName = prefix + ':' + eventName;
1153
1150
 
1154
- while (parent) {
1155
- if (parent instanceof View) {
1156
- return parent;
1157
- }
1158
- parent = parent._parent;
1151
+ this.triggerMethod.apply(this, [childEventName].concat(args));
1159
1152
  }
1160
1153
  }
1161
1154
  };
@@ -1238,7 +1231,7 @@
1238
1231
  // We need to listen for if a view is destroyed in a way other than through the region.
1239
1232
  // If this happens we need to remove the reference to the currentView since once a view
1240
1233
  // has been destroyed we can not reuse it.
1241
- view.on('destroy', this.empty, this);
1234
+ view.on('destroy', this._empty, this);
1242
1235
 
1243
1236
  // Make this region the view's parent.
1244
1237
  // It's important that this parent binding happens before rendering so that any events
@@ -1269,7 +1262,7 @@
1269
1262
  }
1270
1263
  },
1271
1264
  _attachView: function _attachView(view) {
1272
- var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
1265
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1273
1266
 
1274
1267
  var shouldTriggerAttach = !view._isAttached && isNodeAttached(this.el);
1275
1268
  var shouldReplaceEl = typeof options.replaceElement === 'undefined' ? !!_.result(this, 'replaceElement') : !!options.replaceElement;
@@ -1278,7 +1271,11 @@
1278
1271
  triggerMethodOn(view, 'before:attach', view);
1279
1272
  }
1280
1273
 
1281
- this.attachHtml(view, shouldReplaceEl);
1274
+ if (shouldReplaceEl) {
1275
+ this._replaceEl(view);
1276
+ } else {
1277
+ this.attachHtml(view);
1278
+ }
1282
1279
 
1283
1280
  if (shouldTriggerAttach) {
1284
1281
  view._isAttached = true;
@@ -1288,7 +1285,7 @@
1288
1285
  this.currentView = view;
1289
1286
  },
1290
1287
  _ensureElement: function _ensureElement() {
1291
- var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
1288
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1292
1289
 
1293
1290
  if (!_.isObject(this.el)) {
1294
1291
  this.$el = this.getEl(this.el);
@@ -1371,20 +1368,15 @@
1371
1368
 
1372
1369
  // Override this method to change how the new view is appended to the `$el` that the
1373
1370
  // region is managing
1374
- attachHtml: function attachHtml(view, shouldReplace) {
1375
- if (shouldReplace) {
1376
- // replace the region's node with the view's node
1377
- this._replaceEl(view);
1378
- } else {
1379
- this.el.appendChild(view.el);
1380
- }
1371
+ attachHtml: function attachHtml(view) {
1372
+ this.el.appendChild(view.el);
1381
1373
  },
1382
1374
 
1383
1375
 
1384
1376
  // Destroy the current view, if there is one. If there is no current view, it does
1385
1377
  // nothing and returns immediately.
1386
1378
  empty: function empty() {
1387
- var options = arguments.length <= 0 || arguments[0] === undefined ? { allowMissingEl: true } : arguments[0];
1379
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { allowMissingEl: true };
1388
1380
 
1389
1381
  var view = this.currentView;
1390
1382
 
@@ -1396,7 +1388,17 @@
1396
1388
  return this;
1397
1389
  }
1398
1390
 
1399
- view.off('destroy', this.empty, this);
1391
+ var shouldDestroy = !options.preventDestroy;
1392
+
1393
+ if (!shouldDestroy) {
1394
+ deprecate('The preventDestroy option is deprecated. Use Region#detachView');
1395
+ }
1396
+
1397
+ this._empty(view, shouldDestroy);
1398
+ return this;
1399
+ },
1400
+ _empty: function _empty(view, shouldDestroy) {
1401
+ view.off('destroy', this._empty, this);
1400
1402
  this.triggerMethod('before:empty', this, view);
1401
1403
 
1402
1404
  this._restoreEl();
@@ -1404,21 +1406,14 @@
1404
1406
  delete this.currentView;
1405
1407
 
1406
1408
  if (!view._isDestroyed) {
1407
- this._removeView(view, options);
1409
+ this._removeView(view, shouldDestroy);
1408
1410
  delete view._parent;
1409
1411
  }
1410
1412
 
1411
1413
  this.triggerMethod('empty', this, view);
1412
- return this;
1413
1414
  },
1414
- _removeView: function _removeView(view) {
1415
- var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
1416
-
1417
- var preventDestroy = _ref.preventDestroy;
1418
-
1419
- var shouldPreventDestroy = !!preventDestroy;
1420
-
1421
- if (shouldPreventDestroy) {
1415
+ _removeView: function _removeView(view, shouldDestroy) {
1416
+ if (!shouldDestroy) {
1422
1417
  this._detachView(view);
1423
1418
  return;
1424
1419
  }
@@ -1429,6 +1424,17 @@
1429
1424
  destroyBackboneView(view);
1430
1425
  }
1431
1426
  },
1427
+ detachView: function detachView() {
1428
+ var view = this.currentView;
1429
+
1430
+ if (!view) {
1431
+ return;
1432
+ }
1433
+
1434
+ this._empty(view);
1435
+
1436
+ return view;
1437
+ },
1432
1438
  _detachView: function _detachView(view) {
1433
1439
  var shouldTriggerDetach = !!view._isAttached;
1434
1440
  if (shouldTriggerDetach) {
@@ -1476,6 +1482,54 @@
1476
1482
  }
1477
1483
  });
1478
1484
 
1485
+ // return the region instance from the definition
1486
+ function buildRegion (definition, defaults) {
1487
+ if (definition instanceof Region) {
1488
+ return definition;
1489
+ }
1490
+
1491
+ return buildRegionFromDefinition(definition, defaults);
1492
+ }
1493
+
1494
+ function buildRegionFromDefinition(definition, defaults) {
1495
+ var opts = _.extend({}, defaults);
1496
+
1497
+ if (_.isString(definition)) {
1498
+ _.extend(opts, { el: definition });
1499
+
1500
+ return buildRegionFromObject(opts);
1501
+ }
1502
+
1503
+ if (_.isFunction(definition)) {
1504
+ _.extend(opts, { regionClass: definition });
1505
+
1506
+ return buildRegionFromObject(opts);
1507
+ }
1508
+
1509
+ if (_.isObject(definition)) {
1510
+ if (definition.selector) {
1511
+ deprecate('The selector option on a Region definition object is deprecated. Use el to pass a selector string');
1512
+ }
1513
+
1514
+ _.extend(opts, { el: definition.selector }, definition);
1515
+
1516
+ return buildRegionFromObject(opts);
1517
+ }
1518
+
1519
+ throw new MarionetteError({
1520
+ message: 'Improper region configuration type.',
1521
+ url: 'marionette.region.html#region-configuration-types'
1522
+ });
1523
+ }
1524
+
1525
+ function buildRegionFromObject(definition) {
1526
+ var RegionClass = definition.regionClass;
1527
+
1528
+ var options = _.omit(definition, 'regionClass');
1529
+
1530
+ return new RegionClass(options);
1531
+ }
1532
+
1479
1533
  // MixinOptions
1480
1534
  // - regions
1481
1535
  // - regionClass
@@ -1532,60 +1586,17 @@
1532
1586
  _addRegions: function _addRegions(regionDefinitions) {
1533
1587
  var _this = this;
1534
1588
 
1589
+ var defaults = {
1590
+ regionClass: this.regionClass,
1591
+ parentEl: _.partial(_.result, this, 'el')
1592
+ };
1593
+
1535
1594
  return _.reduce(regionDefinitions, function (regions, definition, name) {
1536
- regions[name] = _this._buildRegion(definition);
1595
+ regions[name] = buildRegion(definition, defaults);
1537
1596
  _this._addRegion(regions[name], name);
1538
1597
  return regions;
1539
1598
  }, {});
1540
1599
  },
1541
-
1542
-
1543
- // return the region instance from the definition
1544
- _buildRegion: function _buildRegion(definition) {
1545
- if (definition instanceof Region) {
1546
- return definition;
1547
- }
1548
-
1549
- return this._buildRegionFromDefinition(definition);
1550
- },
1551
- _buildRegionFromDefinition: function _buildRegionFromDefinition(definition) {
1552
- if (_.isString(definition)) {
1553
- return this._buildRegionFromObject({ el: definition });
1554
- }
1555
-
1556
- if (_.isFunction(definition)) {
1557
- return this._buildRegionFromRegionClass(definition);
1558
- }
1559
-
1560
- if (_.isObject(definition)) {
1561
- return this._buildRegionFromObject(definition);
1562
- }
1563
-
1564
- throw new MarionetteError({
1565
- message: 'Improper region configuration type.',
1566
- url: 'marionette.region.html#region-configuration-types'
1567
- });
1568
- },
1569
- _buildRegionFromObject: function _buildRegionFromObject(definition) {
1570
- var RegionClass = definition.regionClass || this.regionClass;
1571
-
1572
- var options = _.omit(definition, 'regionClass');
1573
-
1574
- _.defaults(options, {
1575
- el: definition.selector,
1576
- parentEl: _.partial(_.result, this, 'el')
1577
- });
1578
-
1579
- return new RegionClass(options);
1580
- },
1581
-
1582
-
1583
- // Build the region directly from a given `RegionClass`
1584
- _buildRegionFromRegionClass: function _buildRegionFromRegionClass(RegionClass) {
1585
- return new RegionClass({
1586
- parentEl: _.partial(_.result, this, 'el')
1587
- });
1588
- },
1589
1600
  _addRegion: function _addRegion(region, name) {
1590
1601
  this.triggerMethod('before:add:region', this, name, region);
1591
1602
 
@@ -1618,8 +1629,7 @@
1618
1629
  _removeRegion: function _removeRegion(region, name) {
1619
1630
  this.triggerMethod('before:remove:region', this, name, region);
1620
1631
 
1621
- region.empty();
1622
- region.stopListening();
1632
+ region.destroy();
1623
1633
 
1624
1634
  delete this.regions[name];
1625
1635
  delete this._regions[name];
@@ -1666,6 +1676,9 @@
1666
1676
 
1667
1677
  return region.show.apply(region, [view].concat(args));
1668
1678
  },
1679
+ detachChildView: function detachChildView(name) {
1680
+ return this.getRegion(name).detachView();
1681
+ },
1669
1682
  getChildView: function getChildView(name) {
1670
1683
  return this.getRegion(name).currentView;
1671
1684
  }
@@ -1762,6 +1775,27 @@
1762
1775
  },
1763
1776
 
1764
1777
 
1778
+ // Overriding Backbone.View's `setElement` to handle
1779
+ // if an el was previously defined. If so, the view might be
1780
+ // rendered or attached on setElement.
1781
+ setElement: function setElement() {
1782
+ var hasEl = !!this.el;
1783
+
1784
+ Backbone.View.prototype.setElement.apply(this, arguments);
1785
+
1786
+ if (hasEl) {
1787
+ this._isRendered = !!this.$el.length;
1788
+ this._isAttached = isNodeAttached(this.el);
1789
+ }
1790
+
1791
+ if (this._isRendered) {
1792
+ this.bindUIElements();
1793
+ }
1794
+
1795
+ return this;
1796
+ },
1797
+
1798
+
1765
1799
  // Render the view, defaulting to underscore.js templates.
1766
1800
  // You can override this in your view definition to provide
1767
1801
  // a very specific rendering for your view. In general, though,
@@ -1824,7 +1858,7 @@
1824
1858
  // literal. All methods and attributes from this object
1825
1859
  // are copies to the object passed in.
1826
1860
  mixinTemplateContext: function mixinTemplateContext() {
1827
- var target = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
1861
+ var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1828
1862
 
1829
1863
  var templateContext = _.result(this, 'templateContext');
1830
1864
  return _.extend(target, templateContext);
@@ -2070,7 +2104,7 @@
2070
2104
  _initialEvents: function _initialEvents() {
2071
2105
  if (this.collection) {
2072
2106
  this.listenTo(this.collection, 'add', this._onCollectionAdd);
2073
- this.listenTo(this.collection, 'remove', this._onCollectionRemove);
2107
+ this.listenTo(this.collection, 'update', this._onCollectionUpdate);
2074
2108
  this.listenTo(this.collection, 'reset', this.render);
2075
2109
 
2076
2110
  if (this.sort) {
@@ -2092,17 +2126,109 @@
2092
2126
 
2093
2127
  if (this._shouldAddChild(child, index)) {
2094
2128
  this._destroyEmptyView();
2095
- var ChildView = this._getChildView(child);
2096
- this._addChild(child, ChildView, index);
2129
+ this._addChild(child, index);
2130
+ }
2131
+ },
2132
+
2133
+
2134
+ // Handle collection update model removals
2135
+ _onCollectionUpdate: function _onCollectionUpdate(collection, options) {
2136
+ var changes = options.changes;
2137
+ this._removeChildModels(changes.removed);
2138
+ },
2139
+
2140
+
2141
+ // Remove the child views and destroy them.
2142
+ // This function also updates the indices of later views
2143
+ // in the collection in order to keep the children in sync with the collection.
2144
+ // "models" is an array of models and the corresponding views
2145
+ // will be removed and destroyed from the CollectionView
2146
+ _removeChildModels: function _removeChildModels(models) {
2147
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2148
+
2149
+ var checkEmpty = _ref.checkEmpty;
2150
+
2151
+ var shouldCheckEmpty = checkEmpty !== false;
2152
+
2153
+ // Used to determine where to update the remaining
2154
+ // sibling view indices after these views are removed.
2155
+ var removedViews = this._getRemovedViews(models);
2156
+
2157
+ if (!removedViews.length) {
2158
+ return;
2159
+ }
2160
+
2161
+ this.children._updateLength();
2162
+
2163
+ // decrement the index of views after this one
2164
+ this._updateIndices(removedViews, false);
2165
+
2166
+ if (shouldCheckEmpty) {
2167
+ this._checkEmpty();
2168
+ }
2169
+ },
2170
+
2171
+
2172
+ // Returns the views that will be used for re-indexing
2173
+ // through CollectionView#_updateIndices.
2174
+ _getRemovedViews: function _getRemovedViews(models) {
2175
+ var _this = this;
2176
+
2177
+ // Returning a view means something was removed.
2178
+ return _.reduce(models, function (removingViews, model) {
2179
+ var view = _this.children.findByModel(model);
2180
+
2181
+ if (!view || view._isDestroyed) {
2182
+ return removingViews;
2183
+ }
2184
+
2185
+ _this._removeChildView(view);
2186
+
2187
+ removingViews.push(view);
2188
+
2189
+ return removingViews;
2190
+ }, []);
2191
+ },
2192
+ _findGreatestIndexedView: function _findGreatestIndexedView(views) {
2193
+
2194
+ return _.reduce(views, function (greatestIndexedView, view) {
2195
+ // Even if the index is `undefined`, a view will get returned.
2196
+ if (!greatestIndexedView || greatestIndexedView._index < view._index) {
2197
+ return view;
2198
+ }
2199
+
2200
+ return greatestIndexedView;
2201
+ }, undefined);
2202
+ },
2203
+ _removeChildView: function _removeChildView(view) {
2204
+ this.triggerMethod('before:remove:child', this, view);
2205
+
2206
+ this.children._remove(view);
2207
+ if (view.destroy) {
2208
+ view.destroy();
2209
+ } else {
2210
+ destroyBackboneView(view);
2097
2211
  }
2212
+
2213
+ delete view._parent;
2214
+ this.stopListening(view);
2215
+ this.triggerMethod('remove:child', this, view);
2098
2216
  },
2099
2217
 
2100
2218
 
2101
- // get the child view by model it holds, and remove it
2102
- _onCollectionRemove: function _onCollectionRemove(model) {
2103
- var view = this.children.findByModel(model);
2104
- this.removeChildView(view);
2105
- this._checkEmpty();
2219
+ // Overriding Backbone.View's `setElement` to handle
2220
+ // if an el was previously defined. If so, the view might be
2221
+ // attached on setElement.
2222
+ setElement: function setElement() {
2223
+ var hasEl = !!this.el;
2224
+
2225
+ Backbone.View.prototype.setElement.apply(this, arguments);
2226
+
2227
+ if (hasEl) {
2228
+ this._isAttached = isNodeAttached(this.el);
2229
+ }
2230
+
2231
+ return this;
2106
2232
  },
2107
2233
 
2108
2234
 
@@ -2121,9 +2247,9 @@
2121
2247
  // An efficient rendering used for filtering. Instead of modifying the whole DOM for the
2122
2248
  // collection view, we are only adding or removing the related childrenViews.
2123
2249
  setFilter: function setFilter(filter) {
2124
- var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
2250
+ var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2125
2251
 
2126
- var preventRender = _ref.preventRender;
2252
+ var preventRender = _ref2.preventRender;
2127
2253
 
2128
2254
  var canBeRendered = this._isRendered && !this._isDestroyed;
2129
2255
  var filterChanged = this.filter !== filter;
@@ -2150,22 +2276,22 @@
2150
2276
 
2151
2277
  // Calculate and apply difference by cid between `models` and `previousModels`.
2152
2278
  _applyModelDeltas: function _applyModelDeltas(models, previousModels) {
2153
- var _this = this;
2279
+ var _this2 = this;
2154
2280
 
2155
2281
  var currentIds = {};
2156
2282
  _.each(models, function (model, index) {
2157
- var addedChildNotExists = !_this.children.findByModel(model);
2283
+ var addedChildNotExists = !_this2.children.findByModel(model);
2158
2284
  if (addedChildNotExists) {
2159
- _this._onCollectionAdd(model, _this.collection, { at: index });
2285
+ _this2._onCollectionAdd(model, _this2.collection, { at: index });
2160
2286
  }
2161
2287
  currentIds[model.cid] = true;
2162
2288
  });
2163
- _.each(previousModels, function (prevModel) {
2164
- var removedChildExists = !currentIds[prevModel.cid] && _this.children.findByModel(prevModel);
2165
- if (removedChildExists) {
2166
- _this._onCollectionRemove(prevModel);
2167
- }
2289
+
2290
+ var removeModels = _.filter(previousModels, function (prevModel) {
2291
+ return !currentIds[prevModel.cid] && _this2.children.findByModel(prevModel);
2168
2292
  });
2293
+
2294
+ this._removeChildModels(removeModels);
2169
2295
  },
2170
2296
 
2171
2297
 
@@ -2173,7 +2299,7 @@
2173
2299
  // you can pass reorderOnSort: true to only reorder the DOM after a sort instead of
2174
2300
  // rendering all the collectionView.
2175
2301
  reorder: function reorder() {
2176
- var _this2 = this;
2302
+ var _this3 = this;
2177
2303
 
2178
2304
  var children = this.children;
2179
2305
  var models = this._filteredSortedModels();
@@ -2192,29 +2318,36 @@
2192
2318
  this.render();
2193
2319
  } else {
2194
2320
  (function () {
2195
- // Get the DOM nodes in the same order as the models.
2196
- var elsToReorder = _.map(models, function (model, index) {
2197
- var view = children.findByModel(model);
2321
+
2322
+ var filteredOutModels = [];
2323
+
2324
+ // Get the DOM nodes in the same order as the models and
2325
+ // find the model that were children before but aren't in this new order.
2326
+ var elsToReorder = children.reduce(function (viewEls, view) {
2327
+ var index = _.indexOf(models, view.model);
2328
+
2329
+ if (index === -1) {
2330
+ filteredOutModels.push(view.model);
2331
+ return viewEls;
2332
+ }
2333
+
2198
2334
  view._index = index;
2199
- return view.el;
2200
- });
2201
2335
 
2202
- // Find the views that were children before but aren't in this new ordering.
2203
- var filteredOutViews = children.filter(function (view) {
2204
- return !_.contains(elsToReorder, view.el);
2205
- });
2336
+ viewEls[index] = view.el;
2206
2337
 
2207
- _this2.triggerMethod('before:reorder', _this2);
2338
+ return viewEls;
2339
+ }, new Array(models.length));
2340
+
2341
+ _this3.triggerMethod('before:reorder', _this3);
2208
2342
 
2209
2343
  // Since append moves elements that are already in the DOM, appending the elements
2210
2344
  // will effectively reorder them.
2211
- _this2._appendReorderedChildren(elsToReorder);
2345
+ _this3._appendReorderedChildren(elsToReorder);
2212
2346
 
2213
2347
  // remove any views that have been filtered out
2214
- _.each(filteredOutViews, _.bind(_this2.removeChildView, _this2));
2215
- _this2._checkEmpty();
2348
+ _this3._removeChildModels(filteredOutModels);
2216
2349
 
2217
- _this2.triggerMethod('reorder', _this2);
2350
+ _this3.triggerMethod('reorder', _this3);
2218
2351
  })();
2219
2352
  }
2220
2353
  return this;
@@ -2236,13 +2369,13 @@
2236
2369
  // Internal method. This checks for any changes in the order of the collection.
2237
2370
  // If the index of any view doesn't match, it will render.
2238
2371
  _sortViews: function _sortViews() {
2239
- var _this3 = this;
2372
+ var _this4 = this;
2240
2373
 
2241
2374
  var models = this._filteredSortedModels();
2242
2375
 
2243
2376
  // check for any changes in sort order of views
2244
2377
  var orderChanged = _.find(models, function (item, index) {
2245
- var view = _this3.children.findByModel(item);
2378
+ var view = _this4.children.findByModel(item);
2246
2379
  return !view || view._index !== index;
2247
2380
  });
2248
2381
 
@@ -2281,16 +2414,30 @@
2281
2414
  this.triggerMethod('render:children', this);
2282
2415
  }
2283
2416
  },
2417
+ _createView: function _createView(model, index) {
2418
+ var ChildView = this._getChildView(model);
2419
+ var childViewOptions = this._getChildViewOptions(model, index);
2420
+ var view = this.buildChildView(model, ChildView, childViewOptions);
2421
+ return view;
2422
+ },
2423
+ _setupChildView: function _setupChildView(view, index) {
2424
+ view._parent = this;
2425
+
2426
+ monitorViewEvents(view);
2427
+
2428
+ // set up the child view event forwarding
2429
+ this._proxyChildEvents(view);
2430
+
2431
+ if (this.sort) {
2432
+ view._index = index;
2433
+ }
2434
+ },
2284
2435
 
2285
2436
 
2286
2437
  // Internal method to loop through collection and show each child view.
2287
2438
  _showCollection: function _showCollection(models) {
2288
- var _this4 = this;
2289
-
2290
- _.each(models, function (child, index) {
2291
- var ChildView = _this4._getChildView(child);
2292
- _this4._addChild(child, ChildView, index);
2293
- });
2439
+ _.each(models, _.bind(this._addChild, this));
2440
+ this.children._updateLength();
2294
2441
  },
2295
2442
 
2296
2443
 
@@ -2368,10 +2515,8 @@
2368
2515
  var view = this.buildChildView(model, EmptyView, emptyViewOptions);
2369
2516
 
2370
2517
  this.triggerMethod('before:render:empty', this, view);
2371
- this._addChildView(view, 0);
2518
+ this.addChildView(view, 0);
2372
2519
  this.triggerMethod('render:empty', this, view);
2373
-
2374
- view._parent = this;
2375
2520
  }
2376
2521
  },
2377
2522
 
@@ -2441,11 +2586,8 @@
2441
2586
 
2442
2587
 
2443
2588
  // Internal method for building and adding a child view
2444
- _addChild: function _addChild(child, ChildView, index) {
2445
- var childViewOptions = this._getChildViewOptions(child, index);
2446
-
2447
- var view = this.buildChildView(child, ChildView, childViewOptions);
2448
-
2589
+ _addChild: function _addChild(child, index) {
2590
+ var view = this._createView(child, index);
2449
2591
  this.addChildView(view, index);
2450
2592
 
2451
2593
  return view;
@@ -2464,13 +2606,21 @@
2464
2606
  // children in sync with the collection.
2465
2607
  addChildView: function addChildView(view, index) {
2466
2608
  this.triggerMethod('before:add:child', this, view);
2609
+ this._setupChildView(view, index);
2467
2610
 
2468
- // increment indices of views after this one
2469
- this._updateIndices(view, true, index);
2611
+ // Store the child view itself so we can properly remove and/or destroy it later
2612
+ if (this._isBuffering) {
2613
+ // Add to children, but don't update children's length.
2614
+ this.children._add(view);
2615
+ } else {
2616
+ // increment indices of views after this one
2617
+ this._updateIndices(view, true);
2618
+ this.children.add(view);
2619
+ }
2470
2620
 
2471
- view._parent = this;
2621
+ this._renderView(view);
2472
2622
 
2473
- this._addChildView(view, index);
2623
+ this._attachView(view, index);
2474
2624
 
2475
2625
  this.triggerMethod('add:child', this, view);
2476
2626
 
@@ -2480,15 +2630,12 @@
2480
2630
 
2481
2631
  // Internal method. This decrements or increments the indices of views after the added/removed
2482
2632
  // view to keep in sync with the collection.
2483
- _updateIndices: function _updateIndices(view, increment, index) {
2633
+ _updateIndices: function _updateIndices(views, increment) {
2484
2634
  if (!this.sort) {
2485
2635
  return;
2486
2636
  }
2487
2637
 
2488
- if (increment) {
2489
- // assign the index to the view
2490
- view._index = index;
2491
- }
2638
+ var view = _.isArray(views) ? this._findGreatestIndexedView(views) : views;
2492
2639
 
2493
2640
  // update the indexes of views after this one
2494
2641
  this.children.each(function (laterView) {
@@ -2497,39 +2644,31 @@
2497
2644
  }
2498
2645
  });
2499
2646
  },
2500
-
2501
-
2502
- // Internal Method. Add the view to children and render it at the given index.
2503
- _addChildView: function _addChildView(view, index) {
2504
- // Only trigger attach if already attached and not buffering,
2505
- // otherwise _endBuffering() or Region#show() handles this.
2506
- var shouldTriggerAttach = !this._isBuffering && this._isAttached;
2507
-
2508
- monitorViewEvents(view);
2509
-
2510
- // set up the child view event forwarding
2511
- this._proxyChildEvents(view);
2512
-
2513
- // Store the child view itself so we can properly remove and/or destroy it later
2514
- this.children.add(view);
2647
+ _renderView: function _renderView(view) {
2648
+ if (view._isRendered) {
2649
+ return;
2650
+ }
2515
2651
 
2516
2652
  if (!view.supportsRenderLifecycle) {
2517
2653
  triggerMethodOn(view, 'before:render', view);
2518
2654
  }
2519
2655
 
2520
- // Render view
2521
2656
  view.render();
2522
2657
 
2523
2658
  if (!view.supportsRenderLifecycle) {
2524
2659
  view._isRendered = true;
2525
2660
  triggerMethodOn(view, 'render', view);
2526
2661
  }
2662
+ },
2663
+ _attachView: function _attachView(view, index) {
2664
+ // Only trigger attach if already attached and not buffering,
2665
+ // otherwise _endBuffering() or Region#show() handles this.
2666
+ var shouldTriggerAttach = !view._isAttached && !this._isBuffering && this._isAttached;
2527
2667
 
2528
2668
  if (shouldTriggerAttach) {
2529
2669
  triggerMethodOn(view, 'before:attach', view);
2530
2670
  }
2531
2671
 
2532
- // Attach view
2533
2672
  this.attachHtml(this, view, index);
2534
2673
 
2535
2674
  if (shouldTriggerAttach) {
@@ -2553,22 +2692,10 @@
2553
2692
  return view;
2554
2693
  }
2555
2694
 
2556
- this.triggerMethod('before:remove:child', this, view);
2557
-
2558
- if (view.destroy) {
2559
- view.destroy();
2560
- } else {
2561
- destroyBackboneView(view);
2562
- }
2563
-
2564
- delete view._parent;
2565
- this.stopListening(view);
2566
- this.children.remove(view);
2567
- this.triggerMethod('remove:child', this, view);
2568
-
2695
+ this._removeChildView(view);
2696
+ this.children._updateLength();
2569
2697
  // decrement the index of views after this one
2570
2698
  this._updateIndices(view, false);
2571
-
2572
2699
  return view;
2573
2700
  },
2574
2701
 
@@ -2668,23 +2795,15 @@
2668
2795
 
2669
2796
 
2670
2797
  // Destroy the child views that this collection view is holding on to, if any
2671
- _destroyChildren: function _destroyChildren() {
2672
- var _ref2 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
2673
-
2674
- var checkEmpty = _ref2.checkEmpty;
2675
-
2676
- this.triggerMethod('before:destroy:children', this);
2677
- var shouldCheckEmpty = checkEmpty !== false;
2678
- var childViews = this.children.map(_.identity);
2679
-
2680
- this.children.each(_.bind(this.removeChildView, this));
2681
-
2682
- if (shouldCheckEmpty) {
2683
- this._checkEmpty();
2798
+ _destroyChildren: function _destroyChildren(options) {
2799
+ if (!this.children.length) {
2800
+ return;
2684
2801
  }
2685
2802
 
2803
+ this.triggerMethod('before:destroy:children', this);
2804
+ var childModels = this.children.map('model');
2805
+ this._removeChildModels(childModels, options);
2686
2806
  this.triggerMethod('destroy:children', this);
2687
- return childViews;
2688
2807
  },
2689
2808
 
2690
2809
 
@@ -2701,36 +2820,7 @@
2701
2820
 
2702
2821
  // Set up the child view event forwarding. Uses a "childview:" prefix in front of all forwarded events.
2703
2822
  _proxyChildEvents: function _proxyChildEvents(view) {
2704
- var _this6 = this;
2705
-
2706
- var prefix = _.result(this, 'childViewEventPrefix');
2707
-
2708
- // Forward all child view events through the parent,
2709
- // prepending "childview:" to the event name
2710
- this.listenTo(view, 'all', function (eventName) {
2711
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2712
- args[_key - 1] = arguments[_key];
2713
- }
2714
-
2715
- var childEventName = prefix + ':' + eventName;
2716
-
2717
- var childViewEvents = _this6.normalizeMethods(_this6._childViewEvents);
2718
-
2719
- // call collectionView childViewEvent if defined
2720
- if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) {
2721
- childViewEvents[eventName].apply(_this6, args);
2722
- }
2723
-
2724
- // use the parent view's proxyEvent handlers
2725
- var childViewTriggers = _this6._childViewTriggers;
2726
-
2727
- // Call the event with the proxy name on the parent layout
2728
- if (childViewTriggers && _.isString(childViewTriggers[eventName])) {
2729
- _this6.triggerMethod.apply(_this6, [childViewTriggers[eventName]].concat(args));
2730
- }
2731
-
2732
- _this6.triggerMethod.apply(_this6, [childEventName].concat(args));
2733
- });
2823
+ this.listenTo(view, 'all', this._childViewEventHandler);
2734
2824
  }
2735
2825
  });
2736
2826
 
@@ -2767,7 +2857,7 @@
2767
2857
 
2768
2858
  if (this.collection) {
2769
2859
  this.listenTo(this.collection, 'add', this._onCollectionAdd);
2770
- this.listenTo(this.collection, 'remove', this._onCollectionRemove);
2860
+ this.listenTo(this.collection, 'update', this._onCollectionUpdate);
2771
2861
  this.listenTo(this.collection, 'reset', this.renderChildren);
2772
2862
 
2773
2863
  if (this.sort) {
@@ -2986,6 +3076,8 @@
2986
3076
  return this;
2987
3077
  },
2988
3078
  getEvents: function getEvents() {
3079
+ var _this = this;
3080
+
2989
3081
  // Normalize behavior events hash to allow
2990
3082
  // a user to use the @ui. syntax.
2991
3083
  var behaviorEvents = this.normalizeUIKeys(_.result(this, 'events'));
@@ -2993,15 +3085,15 @@
2993
3085
  // binds the handler to the behavior and builds a unique eventName
2994
3086
  return _.reduce(behaviorEvents, function (events, behaviorHandler, key) {
2995
3087
  if (!_.isFunction(behaviorHandler)) {
2996
- behaviorHandler = this[behaviorHandler];
3088
+ behaviorHandler = _this[behaviorHandler];
2997
3089
  }
2998
3090
  if (!behaviorHandler) {
2999
3091
  return;
3000
3092
  }
3001
3093
  key = getUniqueEventName(key);
3002
- events[key] = _.bind(behaviorHandler, this);
3094
+ events[key] = _.bind(behaviorHandler, _this);
3003
3095
  return events;
3004
- }, {}, this);
3096
+ }, {});
3005
3097
  },
3006
3098
 
3007
3099
 
@@ -3040,20 +3132,18 @@
3040
3132
 
3041
3133
  regionClass: Region,
3042
3134
 
3043
- _initRegion: function _initRegion(options) {
3135
+ _initRegion: function _initRegion() {
3044
3136
  var region = this.region;
3045
- var RegionClass = this.regionClass;
3046
3137
 
3047
- // if the region is a string expect an el or selector
3048
- // and instantiate a region
3049
- if (_.isString(region)) {
3050
- this._region = new RegionClass({
3051
- el: region
3052
- });
3138
+ if (!region) {
3053
3139
  return;
3054
3140
  }
3055
3141
 
3056
- this._region = region;
3142
+ var defaults = {
3143
+ regionClass: this.regionClass
3144
+ };
3145
+
3146
+ this._region = buildRegion(region, defaults);
3057
3147
  },
3058
3148
  getRegion: function getRegion() {
3059
3149
  return this._region;