yano-backbone-rails 2.2.1 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
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;